Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -559,17 +559,19 @@ _**Description**_: Get or Set the Redis server configuration parameters.

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

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

##### *Examples*
~~~php
$redis->config("GET", "*max-*-entries*");
$redis->config("SET", ['timeout', 'loglevel']);
$redis->config("SET", "dir", "/var/run/redis/dumps/");
$redis->config("SET", ['timeout' => 128, 'loglevel' => 'warning']);
$redis->config('RESETSTAT');
~~~

Expand Down
9 changes: 9 additions & 0 deletions library.c
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,15 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r
return SUCCESS;
}

PHP_REDIS_API int
redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
FailableResultCallback cb = ctx;

ZEND_ASSERT(cb == redis_boolean_response || cb == redis_mbulk_reply_zipped_raw);
Comment thread
yatsukhnenko marked this conversation as resolved.

return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx);
}

PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) {
char *response;
int response_len;
Expand Down
1 change: 1 addition & 0 deletions library.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, Redis
PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret);
PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret);
PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
Expand Down
43 changes: 4 additions & 39 deletions redis.c
Original file line number Diff line number Diff line change
Expand Up @@ -2707,45 +2707,10 @@ PHP_METHOD(Redis, setOption)
/* }}} */

/* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */
PHP_METHOD(Redis, config)
{
zend_string *op, *key = NULL, *val = NULL;
FailableResultCallback cb;
RedisSock *redis_sock;
zval *object;
int cmd_len;
char *cmd;

if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|SS", &object,
redis_ce, &op, &key, &val) == FAILURE)
{
RETURN_FALSE;
}

if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
RETURN_FALSE;
}

if (zend_string_equals_literal_ci(op, "GET") && key != NULL) {
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SS", op, key);
cb = redis_mbulk_reply_zipped_raw;
} else if (zend_string_equals_literal_ci(op, "RESETSTAT") ||
zend_string_equals_literal_ci(op, "REWRITE"))
{
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "s", ZSTR_VAL(op), ZSTR_LEN(op));
cb = redis_boolean_response;
} else if (zend_string_equals_literal_ci(op, "SET") && key != NULL && val != NULL) {
cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "SSS", op, key, val);
cb = redis_boolean_response;
} else {
RETURN_FALSE;
}

REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
if (IS_ATOMIC(redis_sock)) {
cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_boolean_response);
/* {{{ proto public function config(string $op, string ...$args) }}} */
// CONFIG SET/GET
PHP_METHOD(Redis, config) {
REDIS_PROCESS_CMD(config, redis_config_response);
}
/* }}} */

Expand Down
24 changes: 23 additions & 1 deletion redis.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,29 @@ public function close(): bool;

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

public function config(string $operation, ?string $key = NULL, mixed $value = null): mixed;
/**
Execute the Redis CONFIG command in a variety of ways. What the command does in particular depends
on the `$operation` qualifier.

Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET.

@param string $operation The CONFIG subcommand to execute
@param array|string|null $key_or_setting Can either be a setting string for the GET/SET operation or
an array of settings or settings and values.
Note: Redis 7.0.0 is required to send an array of settings.
@param ?string $value The setting value when the operation is SET.

<code>
<?php
$redis->config('GET', 'timeout');
$redis->config('GET', ['timeout', 'databases']);

$redis->config('SET', 'timeout', 30);
$redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);
?>
</code>
*/
public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed;

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;

Expand Down
6 changes: 3 additions & 3 deletions redis_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: bd81918bd558ec53399e7f64647c39f288f3413e */
* Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */

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")
Expand Down Expand Up @@ -127,8 +127,8 @@ ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0)
ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "NULL")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, "null")
ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "NULL")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "NULL")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0)
Expand Down
121 changes: 121 additions & 0 deletions redis_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,127 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
return SUCCESS;
}

static int redis_build_config_get_cmd(smart_string *dst, zval *val) {
zend_string *zstr;
int ncfg;
zval *zv;

if (val == NULL || (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY)) {
php_error_docref(NULL, E_WARNING, "Must pass a string or array of values to CONFIG GET");
return FAILURE;
} else if (Z_TYPE_P(val) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(val)) == 0) {
php_error_docref(NULL, E_WARNING, "Cannot pass an empty array to CONFIG GET");
return FAILURE;
}

ncfg = Z_TYPE_P(val) == IS_STRING ? 1 : zend_hash_num_elements(Z_ARRVAL_P(val));

REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + ncfg, "CONFIG");
REDIS_CMD_APPEND_SSTR_STATIC(dst, "GET");

if (Z_TYPE_P(val) == IS_STRING) {
redis_cmd_append_sstr_zstr(dst, Z_STR_P(val));
} else {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), zv) {
ZVAL_DEREF(zv);

zstr = zval_get_string(zv);
redis_cmd_append_sstr_zstr(dst, zstr);
zend_string_release(zstr);
} ZEND_HASH_FOREACH_END();
}

return SUCCESS;
}

static int redis_build_config_set_cmd(smart_string *dst, zval *key, zend_string *val) {
zend_string *zkey, *zstr;
zval *zv;

/* Legacy case: CONFIG SET <string> <string> */
if (key != NULL && val != NULL) {
REDIS_CMD_INIT_SSTR_STATIC(dst, 3, "CONFIG");
REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET");

zstr = zval_get_string(key);
redis_cmd_append_sstr_zstr(dst, zstr);
zend_string_release(zstr);

redis_cmd_append_sstr_zstr(dst, val);

return SUCCESS;
}

/* Now we must have an array with at least one element */
if (key == NULL || Z_TYPE_P(key) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(key)) == 0) {
php_error_docref(NULL, E_WARNING, "Must either pass two strings to CONFIG SET or a non-empty array of values");
return FAILURE;
}

REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + (2 * zend_hash_num_elements(Z_ARRVAL_P(key))), "CONFIG");
REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET");

ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(key), zkey, zv) {
if (zkey == NULL)
goto fail;

ZVAL_DEREF(zv);

redis_cmd_append_sstr_zstr(dst, zkey);

zstr = zval_get_string(zv);
redis_cmd_append_sstr_zstr(dst, zstr);
zend_string_release(zstr);
} ZEND_HASH_FOREACH_END();

return SUCCESS;

fail:
php_error_docref(NULL, E_WARNING, "Must pass an associate array of config keys and values");
efree(dst->c);
memset(dst, 0, sizeof(*dst));
return FAILURE;
}

int
redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx)
{
zend_string *op = NULL, *arg = NULL;
smart_string cmdstr = {0};
int res = FAILURE;
zval *key = NULL;

ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_STR(op)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL_OR_NULL(key)
Z_PARAM_STR_OR_NULL(arg)
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);

if (zend_string_equals_literal_ci(op, "RESETSTAT") ||
zend_string_equals_literal_ci(op, "REWRITE"))
{
REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CONFIG");
redis_cmd_append_sstr_zstr(&cmdstr, op);
*ctx = redis_boolean_response;
res = SUCCESS;
} else if (zend_string_equals_literal_ci(op, "GET")) {
res = redis_build_config_get_cmd(&cmdstr, key);
*ctx = redis_mbulk_reply_zipped_raw;
} else if (zend_string_equals_literal_ci(op, "SET")) {
res = redis_build_config_set_cmd(&cmdstr, key, arg);
*ctx = redis_boolean_response;
} else {
php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op));
return FAILURE;
}

*cmd = cmdstr.c;
*cmd_len = cmdstr.len;
return res;
Comment thread Fixed
}

int
redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx)
Expand Down
3 changes: 3 additions & 0 deletions redis_commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, int *withscores, short *slot,
void **ctx);

int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx);

int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx);

Expand Down
4 changes: 2 additions & 2 deletions redis_legacy_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: bd81918bd558ec53399e7f64647c39f288f3413e */
* Stub hash: a024c59eff58030ac224fc22cc4040b6e926a643 */

ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
ZEND_ARG_INFO(0, options)
Expand Down Expand Up @@ -117,7 +117,7 @@ ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_config, 0, 0, 1)
ZEND_ARG_INFO(0, operation)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, key_or_settings)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

Expand Down
36 changes: 34 additions & 2 deletions tests/RedisTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5612,9 +5612,9 @@ public function testConfig() {

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

/* REWRITE. We don't care if it actually works, just that the
command be attempted */
Expand All @@ -5624,6 +5624,38 @@ public function testConfig() {
$this->assertPatternMatch($this->redis->getLastError(), '/.*config.*/');
$this->redis->clearLastError();
}

if (!$this->minVersionCheck("7.0.0"))
return;

/* Test getting multiple values */
$settings = $this->redis->config('get', ['timeout', 'databases', 'set-max-intset-entries']);
$this->assertTrue(is_array($settings) && isset($settings['timeout']) &&
isset($settings['databases']) && isset($settings['set-max-intset-entries']));

/* Short circuit if the above assertion would have failed */
if ( ! is_array($settings) || ! isset($settings['timeout']) || ! isset($settings['set-max-intset-entries']))
return;

list($timeout, $max_intset) = [$settings['timeout'], $settings['set-max-intset-entries']];

$updates = [
['timeout' => (string)($timeout + 30), 'set-max-intset-entries' => (string)($max_intset + 128)],
['timeout' => (string)($timeout), 'set-max-intset-entries' => (string)$max_intset],
];

foreach ($updates as $update) {
$this->assertTrue($this->redis->config('set', $update));
$vals = $this->redis->config('get', array_keys($update));
ksort($vals);
ksort($update);
$this->assertEquals($vals, $update);
}

/* Make sure PhpRedis catches malformed multiple get/set calls */
$this->assertFalse(@$this->redis->config('get', []));
$this->assertFalse(@$this->redis->config('set', []));
$this->assertFalse(@$this->redis->config('set', [0, 1, 2]));
}

public function testReconnectSelect() {
Expand Down