Skip to content

Commit 7c46ad2

Browse files
committed
Issue #2068
Add FCALL/FCALL_RO commands
1 parent 350aff4 commit 7c46ad2

File tree

7 files changed

+114
-4
lines changed

7 files changed

+114
-4
lines changed

redis.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,6 +2360,16 @@ PHP_METHOD(Redis, evalsha_ro) {
23602360
REDIS_PROCESS_KW_CMD("EVALSHA_RO", redis_eval_cmd, redis_read_raw_variant_reply);
23612361
}
23622362

2363+
/* {{{ proto variant Redis::fcall(string fn [, array keys [, array args]]) */
2364+
PHP_METHOD(Redis, fcall) {
2365+
REDIS_PROCESS_KW_CMD("FCALL", redis_fcall_cmd, redis_read_raw_variant_reply);
2366+
}
2367+
2368+
/* {{{ proto variant Redis::fcall_ro(string fn [, array keys [, array args]]) */
2369+
PHP_METHOD(Redis, fcall_ro) {
2370+
REDIS_PROCESS_KW_CMD("FCALL_RO", redis_fcall_cmd, redis_read_raw_variant_reply);
2371+
}
2372+
23632373
/* {{{ public function script($args...): mixed }}} */
23642374
PHP_METHOD(Redis, script) {
23652375
REDIS_PROCESS_CMD(script, redis_read_variant_reply);

redis.stub.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,34 @@ public function expiretime(string $key): Redis|int|false;
11641164
*/
11651165
public function pexpiretime(string $key): Redis|int|false;
11661166

1167+
/**
1168+
* Invoke a function.
1169+
*
1170+
* @param string $fn The name of the function
1171+
* @param array $keys Optional list of keys
1172+
* @param array $args Optional list of args
1173+
*
1174+
* @return mixed Function may return arbitrary data so this method can return
1175+
* strings, arrays, nested arrays, etc.
1176+
*
1177+
* @see https://redis.io/commands/fcall
1178+
*/
1179+
public function fcall(string $fn, array $keys = [], array $args = []): mixed;
1180+
1181+
/**
1182+
* This is a read-only variant of the FCALL command that cannot execute commands that modify data.
1183+
*
1184+
* @param string $fn The name of the function
1185+
* @param array $keys Optional list of keys
1186+
* @param array $args Optional list of args
1187+
*
1188+
* @return mixed Function may return arbitrary data so this method can return
1189+
* strings, arrays, nested arrays, etc.
1190+
*
1191+
* @see https://redis.io/commands/fcall_ro
1192+
*/
1193+
public function fcall_ro(string $fn, array $keys = [], array $args = []): mixed;
1194+
11671195
/**
11681196
* Deletes every key in all Redis databases
11691197
*

redis_arginfo.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 3d369227b8f6d01fffa0ffda01f379f0138fa226 */
2+
* Stub hash: 600a10da9438d33050be825dff3683399737cc5e */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
55
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
@@ -231,6 +231,14 @@ ZEND_END_ARG_INFO()
231231

232232
#define arginfo_class_Redis_pexpiretime arginfo_class_Redis_expiretime
233233

234+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_fcall, 0, 1, IS_MIXED, 0)
235+
ZEND_ARG_TYPE_INFO(0, fn, IS_STRING, 0)
236+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, keys, IS_ARRAY, 0, "[]")
237+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_ARRAY, 0, "[]")
238+
ZEND_END_ARG_INFO()
239+
240+
#define arginfo_class_Redis_fcall_ro arginfo_class_Redis_fcall
241+
234242
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_flushAll, 0, 0, Redis, MAY_BE_BOOL)
235243
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sync, _IS_BOOL, 1, "null")
236244
ZEND_END_ARG_INFO()
@@ -1207,6 +1215,8 @@ ZEND_METHOD(Redis, expireAt);
12071215
ZEND_METHOD(Redis, failover);
12081216
ZEND_METHOD(Redis, expiretime);
12091217
ZEND_METHOD(Redis, pexpiretime);
1218+
ZEND_METHOD(Redis, fcall);
1219+
ZEND_METHOD(Redis, fcall_ro);
12101220
ZEND_METHOD(Redis, flushAll);
12111221
ZEND_METHOD(Redis, flushDB);
12121222
ZEND_METHOD(Redis, function);
@@ -1460,6 +1470,8 @@ static const zend_function_entry class_Redis_methods[] = {
14601470
ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC)
14611471
ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC)
14621472
ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC)
1473+
ZEND_ME(Redis, fcall, arginfo_class_Redis_fcall, ZEND_ACC_PUBLIC)
1474+
ZEND_ME(Redis, fcall_ro, arginfo_class_Redis_fcall_ro, ZEND_ACC_PUBLIC)
14631475
ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
14641476
ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
14651477
ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC)

redis_commands.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,45 @@ redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
10291029
return SUCCESS;
10301030
}
10311031

1032+
int
1033+
redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
1034+
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
1035+
{
1036+
HashTable *keys = NULL, *args = NULL;
1037+
smart_string cmdstr = {0};
1038+
zend_string *fn = NULL;
1039+
zval *zv;
1040+
1041+
ZEND_PARSE_PARAMETERS_START(1, 3)
1042+
Z_PARAM_STR(fn)
1043+
Z_PARAM_OPTIONAL
1044+
Z_PARAM_ARRAY_HT(keys)
1045+
Z_PARAM_ARRAY_HT(args)
1046+
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
1047+
1048+
redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) +
1049+
(args ? zend_hash_num_elements(args) : 0), kw, strlen(kw));
1050+
redis_cmd_append_sstr_zstr(&cmdstr, fn);
1051+
redis_cmd_append_sstr_long(&cmdstr, keys ? zend_hash_num_elements(keys) : 0);
1052+
1053+
if (keys != NULL) {
1054+
ZEND_HASH_FOREACH_VAL(keys, zv) {
1055+
redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot);
1056+
} ZEND_HASH_FOREACH_END();
1057+
}
1058+
1059+
if (args != NULL) {
1060+
ZEND_HASH_FOREACH_VAL(args, zv) {
1061+
redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock);
1062+
} ZEND_HASH_FOREACH_END();
1063+
}
1064+
1065+
*cmd = cmdstr.c;
1066+
*cmd_len = cmdstr.len;
1067+
1068+
return SUCCESS;
1069+
}
1070+
10321071
int
10331072
redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
10341073
char **cmd, int *cmd_len, short *slot, void **ctx)

redis_commands.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
162162
int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
163163
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
164164

165+
int redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
166+
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
167+
165168
int redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
166169
char **cmd, int *cmd_len, short *slot, void **ctx);
167170

redis_legacy_arginfo.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 3d369227b8f6d01fffa0ffda01f379f0138fa226 */
2+
* Stub hash: 600a10da9438d33050be825dff3683399737cc5e */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
55
ZEND_ARG_INFO(0, options)
@@ -213,6 +213,14 @@ ZEND_END_ARG_INFO()
213213

214214
#define arginfo_class_Redis_pexpiretime arginfo_class_Redis__prefix
215215

216+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_fcall, 0, 0, 1)
217+
ZEND_ARG_INFO(0, fn)
218+
ZEND_ARG_INFO(0, keys)
219+
ZEND_ARG_INFO(0, args)
220+
ZEND_END_ARG_INFO()
221+
222+
#define arginfo_class_Redis_fcall_ro arginfo_class_Redis_fcall
223+
216224
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_flushAll, 0, 0, 0)
217225
ZEND_ARG_INFO(0, sync)
218226
ZEND_END_ARG_INFO()
@@ -1049,6 +1057,8 @@ ZEND_METHOD(Redis, expireAt);
10491057
ZEND_METHOD(Redis, failover);
10501058
ZEND_METHOD(Redis, expiretime);
10511059
ZEND_METHOD(Redis, pexpiretime);
1060+
ZEND_METHOD(Redis, fcall);
1061+
ZEND_METHOD(Redis, fcall_ro);
10521062
ZEND_METHOD(Redis, flushAll);
10531063
ZEND_METHOD(Redis, flushDB);
10541064
ZEND_METHOD(Redis, function);
@@ -1302,6 +1312,8 @@ static const zend_function_entry class_Redis_methods[] = {
13021312
ZEND_ME(Redis, failover, arginfo_class_Redis_failover, ZEND_ACC_PUBLIC)
13031313
ZEND_ME(Redis, expiretime, arginfo_class_Redis_expiretime, ZEND_ACC_PUBLIC)
13041314
ZEND_ME(Redis, pexpiretime, arginfo_class_Redis_pexpiretime, ZEND_ACC_PUBLIC)
1315+
ZEND_ME(Redis, fcall, arginfo_class_Redis_fcall, ZEND_ACC_PUBLIC)
1316+
ZEND_ME(Redis, fcall_ro, arginfo_class_Redis_fcall_ro, ZEND_ACC_PUBLIC)
13051317
ZEND_ME(Redis, flushAll, arginfo_class_Redis_flushAll, ZEND_ACC_PUBLIC)
13061318
ZEND_ME(Redis, flushDB, arginfo_class_Redis_flushDB, ZEND_ACC_PUBLIC)
13071319
ZEND_ME(Redis, function, arginfo_class_Redis_function, ZEND_ACC_PUBLIC)

tests/RedisTest.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7565,11 +7565,17 @@ public function testCommand()
75657565
}
75667566

75677567
public function testFunction() {
7568+
if (version_compare($this->version, '7.0') < 0) {
7569+
$this->markTestSkipped();
7570+
return;
7571+
}
75687572
$this->assertTrue($this->redis->function('flush', 'sync'));
75697573
$this->assertEquals('mylib', $this->redis->function('load', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)"));
7570-
$this->assertEquals('mylib', $this->redis->function('load', 'replace', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)"));
7571-
$this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]);
7574+
$this->assertEquals('foo', $this->redis->fcall('myfunc', [], ['foo']));
75727575
$payload = $this->redis->function('dump');
7576+
$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'}}"));
7577+
$this->assertEquals('foo', $this->redis->fcall_ro('myfunc', [], ['foo']));
7578+
$this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]);
75737579
$this->assertTrue($this->redis->function('delete', 'mylib'));
75747580
$this->assertTrue($this->redis->function('restore', $payload));
75757581
$this->assertEquals($this->redis->function('list'), [['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]]);

0 commit comments

Comments
 (0)