Implement Redis 7.0.0 [P]EXPIRE[AT] options

See #2068
This commit is contained in:
michael-grunder
2022-10-13 10:51:28 -07:00
committed by Michael Grunder
parent d05d301b5a
commit 872ae1079f
12 changed files with 167 additions and 38 deletions
+18 -16
View File
@@ -1098,17 +1098,18 @@ $redis->get('x'); // → `FALSE`
-----
_**Description**_: Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx.
### expire, setTimeout, pexpire
### expire, pexpire
-----
_**Description**_: Sets an expiration date (a timeout) on an item. pexpire requires a TTL in milliseconds.
_**Description**_: Sets an expiration on a key in either seconds or milliseconds.
##### *Parameters*
*Key*: key. The key that will disappear.
*Integer*: ttl. The key's remaining Time To Live, in seconds.
##### *Prototype*
~~~php
public function expire(string $key, int $seconds, ?string $mode = NULL): Redis|bool;
public function pexpire(string $key, int $milliseconds, ?string $mode = NULL): Redis|bool;
~~~
##### *Return value*
*BOOL*: `TRUE` in case of success, `FALSE` in case of failure.
*BOOL*: `TRUE` if an expiration was set, and `FALSE` on failure or if one was not set. You can distinguish between an error and an expiration not being set by checking `getLastError()`.
##### *Example*
~~~php
$redis->set('x', '42');
@@ -1121,22 +1122,23 @@ $redis->get('x'); // will return `FALSE`, as 'x' has expired.
### expireAt, pexpireAt
-----
_**Description**_: Sets an expiration date (a timestamp) on an item. pexpireAt requires a timestamp in milliseconds.
_**Description**_: Seta specific timestamp for a key to expire in seconds or milliseconds.
##### *Parameters*
*Key*: key. The key that will disappear.
*Integer*: Unix timestamp. The key's date of death, in seconds from Epoch time.
##### *Prototype*
~~~php
public function expireat(string $key, int $unix_timestamp, ?string $mode = NULL): Redis|bool;
public function pexpireat(string $key, int $unix_timestamp_millis, ?string $mode = NULL): Redis|bool;
~~~
##### *Return value*
*BOOL*: `TRUE` in case of success, `FALSE` in case of failure.
*BOOL*: `TRUE` if an expiration was set and `FALSE` if one was not set or in the event on an error. You can detect an actual error by checking `getLastError()`.
##### *Example*
~~~php
$redis->set('x', '42');
$now = time(NULL); // current timestamp
$redis->expireAt('x', $now + 3); // x will disappear in 3 seconds.
$redis->expireAt('x', time(NULL) + 3); // x will disappear in 3 seconds.
sleep(5); // wait 5 seconds
$redis->get('x'); // will return `FALSE`, as 'x' has expired.
$redis->get('x'); // will return `FALSE`, as 'x' has expired.
~~~
### keys, getKeys
+4 -4
View File
@@ -1635,25 +1635,25 @@ PHP_METHOD(Redis, sortDescAlpha)
/* {{{ proto array Redis::expire(string key, int timeout) */
PHP_METHOD(Redis, expire) {
REDIS_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, redis_1_response);
REDIS_PROCESS_KW_CMD("EXPIRE", redis_expire_cmd, redis_1_response);
}
/* }}} */
/* {{{ proto bool Redis::pexpire(string key, long ms) */
PHP_METHOD(Redis, pexpire) {
REDIS_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, redis_1_response);
REDIS_PROCESS_KW_CMD("PEXPIRE", redis_expire_cmd, redis_1_response);
}
/* }}} */
/* {{{ proto array Redis::expireAt(string key, int timestamp) */
PHP_METHOD(Redis, expireAt) {
REDIS_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, redis_1_response);
REDIS_PROCESS_KW_CMD("EXPIREAT", redis_expire_cmd, redis_1_response);
}
/* }}} */
/* {{{ proto array Redis::pexpireAt(string key, int timestamp) */
PHP_METHOD(Redis, pexpireAt) {
REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, redis_1_response);
REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_expire_cmd, redis_1_response);
}
/* }}} */
+49 -5
View File
@@ -105,9 +105,32 @@ class Redis {
public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool;
public function expire(string $key, int $timeout): Redis|bool;
/**
Sets an expiration in seconds on the key in question. If connected to
redis-server >= 7.0.0 you may send an additional "mode" argument which
modifies how the command will execute.
public function expireAt(string $key, int $timestamp): Redis|bool;
@param string $key The key to set an expiration on.
@param ?string $mode A two character modifier that changes how the
command works.
NX - Set expiry only if key has no expiry
XX - Set expiry only if key has an expiry
LT - Set expiry only when new expiry is < current expiry
GT - Set expiry only when new expiry is > current expiry
*/
public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool;
/**
Set a key's expiration to a specific unix timestamp in seconds. If
connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
@see Redis::expire() For a description of the mode argument.
@param string $key The key to set an expiration on.
@param ?string $mode A two character modifier that changes how the
command works.
*/
public function expireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool;
public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool;
@@ -313,11 +336,32 @@ class Redis {
public function pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool;
public function persist(string $key): bool;
public function persist(string $key): bool;
public function pexpire(string $key, int $timeout): bool;
/**
Sets an expiration in milliseconds on a given key. If connected to
Redis >= 7.0.0 you can pass an optional mode argument that modifies
how the command will execute.
public function pexpireAt(string $key, int $timestamp): bool;
@see Redis::expire() for a description of the mode argument.
@param string $key The key to set an expiration on.
@param ?string $mode A two character modifier that changes how the
command works.
*/
public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool;
/**
Set a key's expiration to a specific unix timestamp in milliseconds. If
connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.
@see Redis::expire() For a description of the mode argument.
@param string $key The key to set an expiration on.
@param ?string $mode A two character modifier that changes how the
command works.
*/
public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): bool;
public function pfadd(string $key, array $elements): int;
+6 -2
View File
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 1810caef11b38440e073059e2d9c65f92fa8a9a5 */
* Stub hash: b53146f6b329f404b4bfa9e5df9dde9c36b50440 */
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")
@@ -201,11 +201,13 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expire, 0, 2, Redis, MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2, Redis, MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_failover, 0, 0, Redis, MAY_BE_BOOL)
@@ -287,7 +289,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_geosearchstore,
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_get, 0, 1, Redis, MAY_BE_ANY|MAY_BE_FALSE)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_get, 0, 1, IS_MIXED, 0)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_END_ARG_INFO()
@@ -576,11 +578,13 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpireAt, 0, 2, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pfadd, 0, 2, IS_LONG, 0)
+4 -4
View File
@@ -1252,24 +1252,24 @@ PHP_METHOD(RedisCluster, decrbyfloat) {
/* {{{ proto bool RedisCluster::expire(string key, long sec) */
PHP_METHOD(RedisCluster, expire) {
CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, cluster_1_resp, 0);
CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_expire_cmd, cluster_1_resp, 0);
}
/* }}} */
/* {{{ proto bool RedisCluster::expireat(string key, long ts) */
PHP_METHOD(RedisCluster, expireat) {
CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, cluster_1_resp, 0);
CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_expire_cmd, cluster_1_resp, 0);
}
/* {{{ proto bool RedisCluster::pexpire(string key, long ms) */
PHP_METHOD(RedisCluster, pexpire) {
CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, cluster_1_resp, 0);
CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_expire_cmd, cluster_1_resp, 0);
}
/* }}} */
/* {{{ proto bool RedisCluster::pexpireat(string key, long ts) */
PHP_METHOD(RedisCluster, pexpireat) {
CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, cluster_1_resp, 0);
CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_expire_cmd, cluster_1_resp, 0);
}
/* }}} */
+4 -4
View File
@@ -95,9 +95,9 @@ class RedisCluster {
public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool;
public function expire(string $key, int $timeout): RedisCluster|bool;
public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool;
public function expireat(string $key, int $timestamp): RedisCluster|bool;
public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
public function expiretime(string $key): RedisCluster|int|false;
@@ -218,9 +218,9 @@ class RedisCluster {
public function persist(string $key): RedisCluster|bool;
public function pexpire(string $key, int $timeout): RedisCluster|bool;
public function pexpire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool;
public function pexpireat(string $key, int $timestamp): RedisCluster|bool;
public function pexpireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool;
public function pfadd(string $key, array $elements): RedisCluster|bool;
+3 -1
View File
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: c6326ac0f4a1dc7b6fe920a7358010f1a570832a */
* Stub hash: 6b41f3c801e587509bc5c7cab302dc6c1e0f7d56 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -203,11 +203,13 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expire, 0, 2, RedisCluster, MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expireat, 0, 2, RedisCluster, MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
+3 -1
View File
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: c6326ac0f4a1dc7b6fe920a7358010f1a570832a */
* Stub hash: 6b41f3c801e587509bc5c7cab302dc6c1e0f7d56 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_INFO(0, name)
@@ -177,11 +177,13 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expire, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expireat, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, timestamp)
ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()
#define arginfo_class_RedisCluster_expiretime arginfo_class_RedisCluster__prefix
+36
View File
@@ -5724,6 +5724,42 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
return SUCCESS;
}
// [P]EXPIRE[AT] [NX | XX | GT | LT]
int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot,
void **ctx)
{
zend_string *key = NULL, *mode = NULL;
smart_string cmdstr = {0};
zend_long timeout = 0;
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(key)
Z_PARAM_LONG(timeout)
Z_PARAM_OPTIONAL
Z_PARAM_STR(mode)
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
if (mode != NULL && !(zend_string_equals_literal_ci(mode, "NX") ||
zend_string_equals_literal_ci(mode, "XX") ||
zend_string_equals_literal_ci(mode, "LT") ||
zend_string_equals_literal_ci(mode, "GT")))
{
php_error_docref(NULL, E_WARNING, "Unknown expiration modifier '%s'", ZSTR_VAL(mode));
return FAILURE;
}
redis_cmd_init_sstr(&cmdstr, 2 + (mode != NULL), kw, strlen(kw));
redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
redis_cmd_append_sstr_long(&cmdstr, timeout);
if (mode != NULL) redis_cmd_append_sstr_zstr(&cmdstr, mode);
*cmd = cmdstr.c;
*cmd_len = cmdstr.len;
return SUCCESS;
}
int
redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx)
+4
View File
@@ -354,6 +354,10 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx);
int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot,
void **ctx);
int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
+3 -1
View File
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 1810caef11b38440e073059e2d9c65f92fa8a9a5 */
* Stub hash: b53146f6b329f404b4bfa9e5df9dde9c36b50440 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
ZEND_ARG_INFO(0, options)
@@ -181,11 +181,13 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expire, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expireAt, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, timestamp)
ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_failover, 0, 0, 0)
+33
View File
@@ -703,6 +703,39 @@ class Redis_Test extends TestSuite
$this->assertTrue($success);
}
function testExpireOptions() {
if (!$this->minVersionCheck('7.0.0'))
return;
$this->redis->set('eopts', 'value');
/* NX -- Only if expiry isn't set so success, then failure */
$this->assertTrue($this->redis->expire('eopts', 1000, 'NX'));
$this->assertFalse($this->redis->expire('eopts', 1000, 'NX'));
/* XX -- Only set if the key has an existing expiry */
$this->assertTrue($this->redis->expire('eopts', 1000, 'XX'));
$this->assertTrue($this->redis->persist('eopts'));
$this->assertFalse($this->redis->expire('eopts', 1000, 'XX'));
/* GT -- Only set when new expiry > current expiry */
$this->assertTrue($this->redis->expire('eopts', 200));
$this->assertTrue($this->redis->expire('eopts', 300, 'GT'));
$this->assertFalse($this->redis->expire('eopts', 100, 'GT'));
/* LT -- Only set when expiry < current expiry */
$this->assertTrue($this->redis->expire('eopts', 200));
$this->assertTrue($this->redis->expire('eopts', 100, 'LT'));
$this->assertFalse($this->redis->expire('eopts', 300, 'LT'));
/* Sending a nonsensical mode fails without sending a command */
$this->redis->clearLastError();
$this->assertFalse(@$this->redis->expire('eopts', 999, 'nonsense'));
$this->assertEquals(NULL, $this->redis->getLastError());
$this->redis->del('eopts');
}
public function testExpiretime() {
if(version_compare($this->version, "7.0.0") < 0) {
$this->markTestSkipped();