Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/phpredis/phpredis.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavlo Yatsukhnenko <yatsukhnenko@gmail.com>2022-09-04 21:31:20 +0300
committerPavlo Yatsukhnenko <yatsukhnenko@gmail.com>2022-09-07 22:47:02 +0300
commita8d10291a28497e5288b6b7ef9e443ac8290707a (patch)
tree6c2334679b3e54ce5d5e3925e1ffa86d9d6ef7ad
parent8d99181e81c43e7c18c8275e48fc9aef85f5bea5 (diff)
Redis::client commandissue-1894-client
-rw-r--r--library.c151
-rw-r--r--library.h1
-rw-r--r--redis.c57
-rw-r--r--redis.stub.php2
-rw-r--r--redis_arginfo.h4
-rw-r--r--redis_commands.c415
-rw-r--r--redis_commands.h3
-rw-r--r--redis_legacy_arginfo.h4
-rw-r--r--tests/RedisTest.php22
9 files changed, 565 insertions, 94 deletions
diff --git a/library.c b/library.c
index 782f81bf..2fbc4ae8 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 48164f09..a632a84e 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 30e8f401..8499d33e 100644
--- a/redis.c
+++ b/redis.c
@@ -3286,62 +3286,11 @@ PHP_METHOD(Redis, getAuth) {
}
}
-/*
- * $redis->client('list');
- * $redis->client('info');
- * $redis->client('kill', <ip:port>);
- * $redis->client('setname', <name>);
- * $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 16b6164c..4a2751b3 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -64,7 +64,7 @@ class Redis {
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 b5f67ad0..35ea8d58 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 ea1eedc7..a208119a 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 e412d828..662d0500 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 5cced37a..11803eb4 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 de76e9b4..c2e413ae 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -1991,8 +1991,26 @@ class Redis_Test extends TestSuite
/* 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 */