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
This commit is contained in:
michael-grunder
2019-05-12 18:49:33 -07:00
committed by Michael Grunder
parent be3089c6cb
commit 5cb30fb2a6
10 changed files with 75 additions and 8 deletions

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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;
/* }}} */

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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

View File

@@ -609,6 +609,26 @@ class Redis_Cluster_Test extends Redis_Test {
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');

View File

@@ -4825,6 +4825,26 @@ class Redis_Test extends TestSuite
}
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!';