Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 36ef4bd

Browse files
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
1 parent b5ea5fd commit 36ef4bd

10 files changed

Lines changed: 234 additions & 50 deletions

README.markdown

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,17 +559,19 @@ _**Description**_: Get or Set the Redis server configuration parameters.
559559

560560
##### *Prototype*
561561
~~~php
562-
$redis->config($operation, ?string $key = NULL, ?string $value = NULL): mixed;
562+
$redis->config(string $operation, string|array|null $key = NULL, ?string $value = NULL): mixed;
563563
~~~
564564

565565
##### *Return value*
566-
*Associative array* for `GET`, key -> value
567-
*bool* for `SET`, and `RESETSTAT`
566+
*Associative array* for `GET`, key(s) -> value(s)
567+
*bool* for `SET`, `RESETSTAT`, and `REWRITE`
568568

569569
##### *Examples*
570570
~~~php
571571
$redis->config("GET", "*max-*-entries*");
572+
$redis->config("SET", ['timeout', 'loglevel']);
572573
$redis->config("SET", "dir", "/var/run/redis/dumps/");
574+
$redis->config("SET", ['timeout' => 128, 'loglevel' => 'warning']);
573575
$redis->config('RESETSTAT');
574576
~~~
575577

library.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,15 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
11511151
return SUCCESS;
11521152
}
11531153

1154+
PHP_REDIS_API int
1155+
redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
1156+
FailableResultCallback cb = ctx;
1157+
1158+
ZEND_ASSERT(cb == redis_boolean_response || cb == redis_mbulk_reply_zipped_raw);
1159+
1160+
return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
1161+
}
1162+
11541163
PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
11551164
char *response;
11561165
int response_len;

library.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redis
6767
PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
6868
PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
6969
PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
70+
PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
7071
PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret);
7172
PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret);
7273
PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);

redis.c

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,45 +2707,10 @@ PHP_METHOD(Redis, setOption)
27072707
/* }}} */
27082708

27092709
/* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */
2710-
PHP_METHOD(Redis, config)
2711-
{
2712-
zend_string *op, *key = NULL, *val = NULL;
2713-
FailableResultCallback cb;
2714-
RedisSock *redis_sock;
2715-
zval *object;
2716-
int cmd_len;
2717-
char *cmd;
2718-
2719-
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|SS", &object,
2720-
redis_ce, &op, &key, &val) == FAILURE)
2721-
{
2722-
RETURN_FALSE;
2723-
}
2724-
2725-
if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
2726-
RETURN_FALSE;
2727-
}
2728-
2729-
if (zend_string_equals_literal_ci(op, "GET") && key != NULL) {
2730-
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SS", op, key);
2731-
cb = redis_mbulk_reply_zipped_raw;
2732-
} else if (zend_string_equals_literal_ci(op, "RESETSTAT") ||
2733-
zend_string_equals_literal_ci(op, "REWRITE"))
2734-
{
2735-
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "s", ZSTR_VAL(op), ZSTR_LEN(op));
2736-
cb = redis_boolean_response;
2737-
} else if (zend_string_equals_literal_ci(op, "SET") && key != NULL && val != NULL) {
2738-
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SSS", op, key, val);
2739-
cb = redis_boolean_response;
2740-
} else {
2741-
RETURN_FALSE;
2742-
}
2743-
2744-
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
2745-
if (IS_ATOMIC(redis_sock)) {
2746-
cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
2747-
}
2748-
REDIS_PROCESS_RESPONSE(redis_boolean_response);
2710+
/* {{{ proto public function config(string $op, string ...$args) }}} */
2711+
// CONFIG SET/GET
2712+
PHP_METHOD(Redis, config) {
2713+
REDIS_PROCESS_CMD(config, redis_config_response);
27492714
}
27502715
/* }}} */
27512716

redis.stub.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,29 @@ public function close(): bool;
8181

8282
public function command(string $opt = null, string|array $arg): mixed;
8383

84-
public function config(string $operation, ?string $key = NULL, mixed $value = null): mixed;
84+
/**
85+
Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends
86+
on the `$operation` qualifier.
87+
88+
Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.
89+
90+
@param string $operation The CONFIG subcommand to execute
91+
@param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or
92+
an array of settings or settings and values.
93+
Note: Redis 7.0.0 is required to send an array of settings.
94+
@param ?string $value The setting value when the operation is SET.
95+
96+
<code>
97+
<?php
98+
$redis->config('GET', 'timeout');
99+
$redis->config('GET', ['timeout', 'databases']);
100+
101+
$redis->config('SET', 'timeout', 30);
102+
$redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);
103+
?>
104+
</code>
105+
*/
106+
public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed;
85107

86108
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;
87109

redis_arginfo.h

Lines changed: 3 additions & 3 deletions
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: bd81918bd558ec53399e7f64647c39f288f3413e */
2+
* Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */
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")
@@ -127,8 +127,8 @@ ZEND_END_ARG_INFO()
127127

128128
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0)
129129
ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
130-
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "NULL")
131-
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "null")
130+
ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "NULL")
131+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "NULL")
132132
ZEND_END_ARG_INFO()
133133

134134
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0)

redis_commands.c

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232

3333
#include <zend_exceptions.h>
3434

35+
/* Config operations */
36+
typedef enum redisConfigOp {
37+
REDIS_CFG_RESETSTAT,
38+
REDIS_CFG_REWRITE,
39+
REDIS_CFG_GET,
40+
REDIS_CFG_SET,
41+
} redisConfigOp;
42+
3543
/* Georadius sort type */
3644
typedef enum geoSortType {
3745
SORT_NONE,
@@ -728,6 +736,148 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
728736
return SUCCESS;
729737
}
730738

739+
static int redis_get_config_op(enum redisConfigOp *dst, zend_string *op) {
740+
if (zend_string_equals_literal_ci(op, "RESETSTAT"))
741+
*dst = REDIS_CFG_RESETSTAT;
742+
else if (zend_string_equals_literal_ci(op, "REWRITE"))
743+
*dst = REDIS_CFG_REWRITE;
744+
else if (zend_string_equals_literal_ci(op, "GET"))
745+
*dst = REDIS_CFG_GET;
746+
else if (zend_string_equals_literal_ci(op, "SET"))
747+
*dst = REDIS_CFG_SET;
748+
else
749+
return FAILURE;
750+
751+
return SUCCESS;
752+
}
753+
754+
static int redis_build_config_get_cmd(smart_string *dst, zval *val) {
755+
zend_string *zstr;
756+
int ncfg;
757+
zval *zv;
758+
759+
if (val == NULL || (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY)) {
760+
php_error_docref(NULL, E_WARNING, "Must pass a string or array of values to CONFIG GET");
761+
return FAILURE;
762+
} else if (Z_TYPE_P(val) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(val)) == 0) {
763+
php_error_docref(NULL, E_WARNING, "Cannot pass an empty array to CONFIG GET");
764+
return FAILURE;
765+
}
766+
767+
ncfg = Z_TYPE_P(val) == IS_STRING ? 1 : zend_hash_num_elements(Z_ARRVAL_P(val));
768+
769+
REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + ncfg, "CONFIG");
770+
REDIS_CMD_APPEND_SSTR_STATIC(dst, "GET");
771+
772+
if (Z_TYPE_P(val) == IS_STRING) {
773+
redis_cmd_append_sstr_zstr(dst, Z_STR_P(val));
774+
} else {
775+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), zv) {
776+
ZVAL_DEREF(zv);
777+
778+
zstr = zval_get_string(zv);
779+
redis_cmd_append_sstr_zstr(dst, zstr);
780+
zend_string_release(zstr);
781+
} ZEND_HASH_FOREACH_END();
782+
}
783+
784+
return SUCCESS;
785+
}
786+
787+
static int redis_build_config_set_cmd(smart_string *dst, zval *key, zend_string *val) {
788+
zend_string *zkey, *zstr;
789+
zval *zv;
790+
791+
/* Legacy case: CONFIG SET <string> <string> */
792+
if (key != NULL && val != NULL) {
793+
REDIS_CMD_INIT_SSTR_STATIC(dst, 3, "CONFIG");
794+
REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET");
795+
796+
zstr = zval_get_string(key);
797+
redis_cmd_append_sstr_zstr(dst, zstr);
798+
zend_string_release(zstr);
799+
800+
redis_cmd_append_sstr_zstr(dst, val);
801+
802+
return SUCCESS;
803+
}
804+
805+
/* Now we must have an array with at least one element */
806+
if (key == NULL || Z_TYPE_P(key) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(key)) == 0) {
807+
php_error_docref(NULL, E_WARNING, "Must either pass two strings to CONFIG SET or a non-empty array of values");
808+
return FAILURE;
809+
}
810+
811+
REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + (2 * zend_hash_num_elements(Z_ARRVAL_P(key))), "CONFIG");
812+
REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET");
813+
814+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(key), zkey, zv) {
815+
if (zkey == NULL)
816+
goto fail;
817+
818+
ZVAL_DEREF(zv);
819+
820+
redis_cmd_append_sstr_zstr(dst, zkey);
821+
822+
zstr = zval_get_string(zv);
823+
redis_cmd_append_sstr_zstr(dst, zstr);
824+
zend_string_release(zstr);
825+
} ZEND_HASH_FOREACH_END();
826+
827+
return SUCCESS;
828+
829+
fail:
830+
php_error_docref(NULL, E_WARNING, "Must pass an associate array of config keys and values");
831+
efree(dst->c);
832+
memset(dst, 0, sizeof(*dst));
833+
return FAILURE;
834+
}
835+
836+
int
837+
redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
838+
char **cmd, int *cmd_len, short *slot, void **ctx)
839+
{
840+
zend_string *op = NULL, *arg = NULL;
841+
smart_string cmdstr = {0};
842+
enum redisConfigOp cfg_op;
843+
int res = FAILURE;
844+
zval *key = NULL;
845+
846+
ZEND_PARSE_PARAMETERS_START(1, 3)
847+
Z_PARAM_STR(op)
848+
Z_PARAM_OPTIONAL
849+
Z_PARAM_ZVAL_OR_NULL(key)
850+
Z_PARAM_STR_OR_NULL(arg)
851+
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
852+
853+
if (redis_get_config_op(&cfg_op, op) != SUCCESS) {
854+
php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op));
855+
return FAILURE;
856+
}
857+
858+
switch (cfg_op) {
859+
case REDIS_CFG_RESETSTAT: /* fallthrough */
860+
case REDIS_CFG_REWRITE:
861+
REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CONFIG");
862+
redis_cmd_append_sstr_zstr(&cmdstr, op);
863+
*ctx = redis_boolean_response;
864+
res = SUCCESS;
865+
break;
866+
case REDIS_CFG_GET:
867+
res = redis_build_config_get_cmd(&cmdstr, key);
868+
*ctx = redis_mbulk_reply_zipped_raw;
869+
break;
870+
case REDIS_CFG_SET:
871+
res = redis_build_config_set_cmd(&cmdstr, key, arg);
872+
*ctx = redis_boolean_response;
873+
break;
874+
}
875+
876+
*cmd = cmdstr.c;
877+
*cmd_len = cmdstr.len;
878+
return res;
879+
}
880+
731881
int
732882
redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
733883
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
@@ -110,6 +110,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
110110
char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
111111
void **ctx);
112112

113+
int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
114+
char **cmd, int *cmd_len, short *slot, void **ctx);
115+
113116
int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
114117
char **cmd, int *cmd_len, short *slot, void **ctx);
115118

redis_legacy_arginfo.h

Lines changed: 2 additions & 2 deletions
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: bd81918bd558ec53399e7f64647c39f288f3413e */
2+
* Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
55
ZEND_ARG_INFO(0, options)
@@ -117,7 +117,7 @@ ZEND_END_ARG_INFO()
117117

118118
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 1)
119119
ZEND_ARG_INFO(0, operation)
120-
ZEND_ARG_INFO(0, key)
120+
ZEND_ARG_INFO(0, key_or_settings)
121121
ZEND_ARG_INFO(0, value)
122122
ZEND_END_ARG_INFO()
123123

tests/RedisTest.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5612,9 +5612,9 @@ public function testConfig() {
56125612

56135613
/* Ensure invalid calls are handled by PhpRedis */
56145614
foreach (['notacommand', 'get', 'set'] as $cmd) {
5615-
$this->assertFalse($this->redis->config($cmd));
5615+
$this->assertFalse(@$this->redis->config($cmd));
56165616
}
5617-
$this->assertFalse($this->redis->config('set', 'foo'));
5617+
$this->assertFalse(@$this->redis->config('set', 'foo'));
56185618

56195619
/* REWRITE. We don't care if it actually works, just that the
56205620
command be attempted */
@@ -5624,6 +5624,38 @@ public function testConfig() {
56245624
$this->assertPatternMatch($this->redis->getLastError(), '/.*config.*/');
56255625
$this->redis->clearLastError();
56265626
}
5627+
5628+
if (!$this->minVersionCheck("7.0.0"))
5629+
return;
5630+
5631+
/* Test getting multiple values */
5632+
$settings = $this->redis->config('get', ['timeout', 'databases', 'set-max-intset-entries']);
5633+
$this->assertTrue(is_array($settings) && isset($settings['timeout']) &&
5634+
isset($settings['databases']) && isset($settings['set-max-intset-entries']));
5635+
5636+
/* Short circuit if the above assertion would have failed */
5637+
if ( ! is_array($settings) || ! isset($settings['timeout']) || ! isset($settings['set-max-intset-entries']))
5638+
return;
5639+
5640+
list($timeout, $max_intset) = [$settings['timeout'], $settings['set-max-intset-entries']];
5641+
5642+
$updates = [
5643+
['timeout' => (string)($timeout + 30), 'set-max-intset-entries' => (string)($max_intset + 128)],
5644+
['timeout' => (string)($timeout), 'set-max-intset-entries' => (string)$max_intset],
5645+
];
5646+
5647+
foreach ($updates as $update) {
5648+
$this->assertTrue($this->redis->config('set', $update));
5649+
$vals = $this->redis->config('get', array_keys($update));
5650+
ksort($vals);
5651+
ksort($update);
5652+
$this->assertEquals($vals, $update);
5653+
}
5654+
5655+
/* Make sure PhpRedis catches malformed multiple get/set calls */
5656+
$this->assertFalse(@$this->redis->config('get', []));
5657+
$this->assertFalse(@$this->redis->config('set', []));
5658+
$this->assertFalse(@$this->redis->config('set', [0, 1, 2]));
56275659
}
56285660

56295661
public function testReconnectSelect() {

0 commit comments

Comments
 (0)