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