diff --git a/library.c b/library.c index ce3e267..c73858e 100644 --- a/library.c +++ b/library.c @@ -2027,7 +2027,7 @@ redis_hello_response(INTERNAL_FUNCTION_PARAMETERS, array_init(&z_ret); if (redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret) != SUCCESS || - array_zip_values_recursive(&z_ret) != SUCCESS) + array_zip_values_recursive(&z_ret) != SUCCESS) { zval_dtor(&z_ret); goto fail; diff --git a/redis.c b/redis.c index 629dd5c..2ef2fc8 100644 --- a/redis.c +++ b/redis.c @@ -1878,6 +1878,51 @@ PHP_METHOD(Redis, hMset) } /* }}} */ +PHP_METHOD(Redis, hexpire) { + REDIS_PROCESS_KW_CMD("HEXPIRE", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpexpire) { + REDIS_PROCESS_KW_CMD("HPEXPIRE", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hexpireat) { + REDIS_PROCESS_KW_CMD("HEXPIREAT", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpexpireat) { + REDIS_PROCESS_KW_CMD("HPEXPIREAT", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, httl) { + REDIS_PROCESS_KW_CMD("HTTL", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpttl) { + REDIS_PROCESS_KW_CMD("HPTTL", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hexpiretime) { + REDIS_PROCESS_KW_CMD("HEXPIRETIME", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpexpiretime) { + REDIS_PROCESS_KW_CMD("HPEXPIRETIME", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpersist) { + REDIS_PROCESS_KW_CMD("HPERSIST", redis_httl_cmd, + redis_read_variant_reply); +} + /* {{{ proto bool Redis::hRandField(string key, [array $options]) */ PHP_METHOD(Redis, hRandField) { diff --git a/redis.stub.php b/redis.stub.php index a2cca87..3f95468 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -1913,6 +1913,121 @@ class Redis { */ public function hVals(string $key): Redis|array|false; + /** + * Set the expiration on one or more fields in a hash. + * + * @param string $key The hash to update. + * @param int $ttl The time to live in seconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $option An optional mode (NX, XX, ETC) + * @return Redis|array|false + * + * @see https://redis.io/commands/hexpire + */ + public function hexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Set the expiration on one or more fields in a hash in milliseconds. + * + * @param string $key The hash to update. + * @param int $ttl The time to live in milliseconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $option An optional mode (NX, XX, ETC) + * @return Redis|array|false + * + * @see https://redis.io/commands/hexpire + */ + public function hpexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Set the expiration time on one or more fields of a hash. + * + * @param string $key The hash to update. + * @param int $time The time to live in seconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $option An optional mode (NX, XX, ETC) + * @return Redis|array|false + * + * @see https://redis.io/commands/hexpire + */ + public function hexpireat(string $key, int $time, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Set the expiration time on one or more fields of a hash in milliseconds. + * + * @param string $key The hash to update. + * @param int $mstime The time to live in milliseconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $option An optional mode (NX, XX, ETC) + * @return Redis|array|false + * + * @see https://redis.io/commands/hexpire + */ + public function hpexpireat(string $key, int $mstime, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Get the TTL of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/commands/httl + */ + public function httl(string $key, array $fields): Redis|array|false; + + /** + * Get the millisecond TTL of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/commands/hpttl + */ + public function hpttl(string $key, array $fields): Redis|array|false; + + /** + * Get the expiration time of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/commands/hexpiretime + */ + public function hexpiretime(string $key, array $fields): Redis|array|false; + + /** + * Get the expiration time in milliseconds of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/commands/hpexpiretime + */ + public function hpexpiretime(string $key, array $fields): Redis|array|false; + + /** + * Persist one or more hash fields + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/commands/hpersist + */ + public function hpersist(string $key, array $fields): Redis|array|false; /** * Iterate over the fields and values of a hash in an incremental fashion. diff --git a/redis_arginfo.h b/redis_arginfo.h index 7f31a5e..32c2754 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: 3a08bc16dd5a73e721e0df8f7843acdbbb585df5 */ + * Stub hash: c6205649cd23ff2b9fcc63a034b601ee566ef236 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") @@ -464,6 +464,39 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_hVals arginfo_class_Redis_getWithMeta +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hexpire, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_hpexpire arginfo_class_Redis_hexpire + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hexpireat, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, time, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 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_hpexpireat, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, mstime, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_httl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpttl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpersist arginfo_class_Redis_hMget + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) @@ -1292,6 +1325,15 @@ ZEND_METHOD(Redis, hSet); ZEND_METHOD(Redis, hSetNx); ZEND_METHOD(Redis, hStrLen); ZEND_METHOD(Redis, hVals); +ZEND_METHOD(Redis, hexpire); +ZEND_METHOD(Redis, hpexpire); +ZEND_METHOD(Redis, hexpireat); +ZEND_METHOD(Redis, hpexpireat); +ZEND_METHOD(Redis, httl); +ZEND_METHOD(Redis, hpttl); +ZEND_METHOD(Redis, hexpiretime); +ZEND_METHOD(Redis, hpexpiretime); +ZEND_METHOD(Redis, hpersist); ZEND_METHOD(Redis, hscan); ZEND_METHOD(Redis, expiremember); ZEND_METHOD(Redis, expirememberat); @@ -1553,6 +1595,15 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpire, arginfo_class_Redis_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpire, arginfo_class_Redis_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpireat, arginfo_class_Redis_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpireat, arginfo_class_Redis_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, httl, arginfo_class_Redis_httl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpttl, arginfo_class_Redis_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpiretime, arginfo_class_Redis_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpiretime, arginfo_class_Redis_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpersist, arginfo_class_Redis_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 1cbd825..7b63696 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1203,6 +1203,49 @@ PHP_METHOD(RedisCluster, hmset) { } /* }}} */ +PHP_METHOD(RedisCluster, hexpire) { + CLUSTER_PROCESS_KW_CMD("HEXPIRE", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, hpexpire) { + CLUSTER_PROCESS_KW_CMD("HPEXPIRE", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, hexpireat) { + CLUSTER_PROCESS_KW_CMD("HEXPIREAT", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, hpexpireat) { + CLUSTER_PROCESS_KW_CMD("HPEXPIREAT", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, httl) { + CLUSTER_PROCESS_KW_CMD("HTTL", redis_httl_cmd, cluster_variant_resp, 1); +} + +PHP_METHOD(RedisCluster, hpttl) { + CLUSTER_PROCESS_KW_CMD("HPTTL", redis_httl_cmd, cluster_variant_resp, 1); +} + + +PHP_METHOD(RedisCluster, hexpiretime) { + CLUSTER_PROCESS_KW_CMD("HEXPIRETIME", redis_httl_cmd, + cluster_variant_resp, 1); +} + +PHP_METHOD(RedisCluster, hpexpiretime) { + CLUSTER_PROCESS_KW_CMD("HPEXPIRETIME", redis_httl_cmd, + cluster_variant_resp, 1); +} + +PHP_METHOD(RedisCluster, hpersist) { + CLUSTER_PROCESS_KW_CMD("HPERSIST", redis_httl_cmd, cluster_variant_resp, 0); +} + /* {{{ proto bool RedisCluster::hrandfield(string key, [array $options]) */ PHP_METHOD(RedisCluster, hrandfield) { CLUSTER_PROCESS_CMD(hrandfield, cluster_hrandfield_resp, 1); diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 58cced5..05a6df7 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -535,6 +535,55 @@ class RedisCluster { */ public function hstrlen(string $key, string $field): RedisCluster|int|false; + /** + * @see Redis::hexpire + */ + public function hexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see Redis::hpexpire + */ + public function hpexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see Redis::hexpireat + */ + public function hexpireat(string $key, int $time, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see Redis::hpexpireat + */ + public function hpexpireat(string $key, int $mstime, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see Redis::httl + */ + public function httl(string $key, array $fields): RedisCluster|array|false; + + /** + * @see Redis::hpttl + */ + public function hpttl(string $key, array $fields): RedisCluster|array|false; + + /** + * @see Redis::hexpiretime + */ + public function hexpiretime(string $key, array $fields): RedisCluster|array|false; + + /** + * @see Redis::hpexpiretime + */ + public function hpexpiretime(string $key, array $fields): RedisCluster|array|false; + + /** + * @see Redis::hpexpiretime + */ + public function hpersist(string $key, array $fields): RedisCluster|array|false; + /** * @see Redis::hvals */ diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index b3fb584..4fea76b 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 43a43fa735ced4b48a361078ac8a10fb62cb1244 */ + * Stub hash: 5788cd1d12611ef1ff5747efe07b99f66f07fa05 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -455,6 +455,42 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hstrlen, ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexpire, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hpexpire arginfo_class_RedisCluster_hexpire + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexpireat, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, time, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 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_hpexpireat, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, mstime, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 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_httl, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hpttl arginfo_class_RedisCluster_httl + +#define arginfo_class_RedisCluster_hexpiretime arginfo_class_RedisCluster_httl + +#define arginfo_class_RedisCluster_hpexpiretime arginfo_class_RedisCluster_httl + +#define arginfo_class_RedisCluster_hpersist arginfo_class_RedisCluster_httl + #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_getWithMeta #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr @@ -1160,6 +1196,15 @@ ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); +ZEND_METHOD(RedisCluster, hexpire); +ZEND_METHOD(RedisCluster, hpexpire); +ZEND_METHOD(RedisCluster, hexpireat); +ZEND_METHOD(RedisCluster, hpexpireat); +ZEND_METHOD(RedisCluster, httl); +ZEND_METHOD(RedisCluster, hpttl); +ZEND_METHOD(RedisCluster, hexpiretime); +ZEND_METHOD(RedisCluster, hpexpiretime); +ZEND_METHOD(RedisCluster, hpersist); ZEND_METHOD(RedisCluster, hvals); ZEND_METHOD(RedisCluster, incr); ZEND_METHOD(RedisCluster, incrby); @@ -1391,6 +1436,15 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpire, arginfo_class_RedisCluster_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpire, arginfo_class_RedisCluster_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpireat, arginfo_class_RedisCluster_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpireat, arginfo_class_RedisCluster_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, httl, arginfo_class_RedisCluster_httl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpttl, arginfo_class_RedisCluster_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpiretime, arginfo_class_RedisCluster_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpiretime, arginfo_class_RedisCluster_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpersist, arginfo_class_RedisCluster_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index d117db5..e1a18b1 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 43a43fa735ced4b48a361078ac8a10fb62cb1244 */ + * Stub hash: 5788cd1d12611ef1ff5747efe07b99f66f07fa05 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -396,6 +396,42 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hstrlen, 0, 0, 2) ZEND_ARG_INFO(0, field) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexpire, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hpexpire arginfo_class_RedisCluster_hexpire + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, time) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hpexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, mstime) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_httl, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, fields) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hpttl arginfo_class_RedisCluster_httl + +#define arginfo_class_RedisCluster_hexpiretime arginfo_class_RedisCluster_httl + +#define arginfo_class_RedisCluster_hpexpiretime arginfo_class_RedisCluster_httl + +#define arginfo_class_RedisCluster_hpersist arginfo_class_RedisCluster_httl + #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr @@ -1002,6 +1038,15 @@ ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); +ZEND_METHOD(RedisCluster, hexpire); +ZEND_METHOD(RedisCluster, hpexpire); +ZEND_METHOD(RedisCluster, hexpireat); +ZEND_METHOD(RedisCluster, hpexpireat); +ZEND_METHOD(RedisCluster, httl); +ZEND_METHOD(RedisCluster, hpttl); +ZEND_METHOD(RedisCluster, hexpiretime); +ZEND_METHOD(RedisCluster, hpexpiretime); +ZEND_METHOD(RedisCluster, hpersist); ZEND_METHOD(RedisCluster, hvals); ZEND_METHOD(RedisCluster, incr); ZEND_METHOD(RedisCluster, incrby); @@ -1233,6 +1278,15 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpire, arginfo_class_RedisCluster_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpire, arginfo_class_RedisCluster_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpireat, arginfo_class_RedisCluster_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpireat, arginfo_class_RedisCluster_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, httl, arginfo_class_RedisCluster_httl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpttl, arginfo_class_RedisCluster_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpiretime, arginfo_class_RedisCluster_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpiretime, arginfo_class_RedisCluster_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpersist, arginfo_class_RedisCluster_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index d57d4c9..f473da4 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -2375,7 +2375,7 @@ int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Calculate argc based on options set */ - int argc = 2 + (ifeq ? 2 : 0) + (exp_type ? 2 : 0) + (set_type != NULL) + + int argc = 2 + (ifeq ? 2 : 0) + (exp_type ? 2 : 0) + (set_type != NULL) + (keep_ttl != 0) + get; /* Initial SET */ @@ -4634,6 +4634,92 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Starting with Redis version 6.0.0: Added the AUTH2 option. */ +int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_string *key, *field, *tmp; + HashTable *fields; + int argc; + zval *zv; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(fields) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(fields) < 1) { + php_error_docref(NULL, E_WARNING, "Must pass at least one field"); + return FAILURE; + } + + // 3 because FIELDS + argc = 3 + zend_hash_num_elements(fields); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + + ZEND_HASH_FOREACH_VAL(fields, zv) + field = zval_get_tmp_string(zv, &tmp); + redis_cmd_append_sstr_zstr(&cmdstr, field); + zend_tmp_string_release(tmp); + ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zend_string *key, *option = NULL, *tmp, *field; + smart_string cmdstr = {0}; + HashTable *fields; + zend_long ttl; + zval *zv; + int argc; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STR(key) + Z_PARAM_LONG(ttl) + Z_PARAM_ARRAY_HT(fields) + Z_PARAM_OPTIONAL + Z_PARAM_STR(option) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(fields) < 1) { + php_error_docref(NULL, E_WARNING, "Must pass at least one field"); + return FAILURE; + } + + // 4 because FIELDS + argc = 4 + zend_hash_num_elements(fields) + (option ? 1 : 0); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_long(&cmdstr, ttl); + if (option) redis_cmd_append_sstr_zstr(&cmdstr, option); + + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + + ZEND_HASH_FOREACH_VAL(fields, zv) + field = zval_get_tmp_string(zv, &tmp); + redis_cmd_append_sstr_zstr(&cmdstr, field); + zend_tmp_string_release(tmp); + ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* MIGRATE */ int redis_migrate_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 b0c5895..6b52fee 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -356,6 +356,12 @@ int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index a6aae1c..27acccc 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: 3a08bc16dd5a73e721e0df8f7843acdbbb585df5 */ + * Stub hash: c6205649cd23ff2b9fcc63a034b601ee566ef236 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -412,6 +412,39 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_hVals arginfo_class_Redis__prefix +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hexpire, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_hpexpire arginfo_class_Redis_hexpire + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, time) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hpexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, mstime) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_httl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpttl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpersist arginfo_class_Redis_hMget + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(1, iterator) @@ -1134,6 +1167,15 @@ ZEND_METHOD(Redis, hSet); ZEND_METHOD(Redis, hSetNx); ZEND_METHOD(Redis, hStrLen); ZEND_METHOD(Redis, hVals); +ZEND_METHOD(Redis, hexpire); +ZEND_METHOD(Redis, hpexpire); +ZEND_METHOD(Redis, hexpireat); +ZEND_METHOD(Redis, hpexpireat); +ZEND_METHOD(Redis, httl); +ZEND_METHOD(Redis, hpttl); +ZEND_METHOD(Redis, hexpiretime); +ZEND_METHOD(Redis, hpexpiretime); +ZEND_METHOD(Redis, hpersist); ZEND_METHOD(Redis, hscan); ZEND_METHOD(Redis, expiremember); ZEND_METHOD(Redis, expirememberat); @@ -1395,6 +1437,15 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpire, arginfo_class_Redis_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpire, arginfo_class_Redis_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpireat, arginfo_class_Redis_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpireat, arginfo_class_Redis_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, httl, arginfo_class_Redis_httl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpttl, arginfo_class_Redis_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpiretime, arginfo_class_Redis_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpiretime, arginfo_class_Redis_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpersist, arginfo_class_Redis_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 1ebcc61..7ca9e68 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -6288,6 +6288,68 @@ class Redis_Test extends TestSuite { } } + public function testHashExpiration() { + if ( ! $this->minVersionCheck('7.4.0')) + $this->markTestSkipped(); + + $hexpire_cmds = [ + 'hexpire' => 10, + 'hpexpire' => 10000, + 'hexpireat' => time() + 10, + 'hpexpireat' => time() * 1000 + 10000, + ]; + + $httl_cmds = ['httl', 'hpttl', 'hexpiretime', 'hpexpiretime']; + + $hash = ['Picard' => 'Enterprise', 'Sisko' => 'Defiant']; + $keys = array_keys($hash); + + foreach ($hexpire_cmds as $exp_cmd => $ttl) { + $this->redis->del('hash'); + $this->redis->hmset('hash', $hash); + + /* Set a TTL on one existing and one non-existing field */ + $res = $this->redis->{$exp_cmd}('hash', $ttl, ['Picard', 'nofield']); + + $this->assertEquals($res, [1, -2]); + + foreach ($httl_cmds as $ttl_cmd) { + $res = $this->redis->{$ttl_cmd}('hash', $keys); + $this->assertIsArray($res); + $this->assertEquals(count($keys), count($res)); + + /* Picard: has an expiry (>0), Siskto does not (<0) */ + $this->assertTrue($res[0] > 0); + $this->assertTrue($res[1] < 0); + } + + $this->redis->del('m'); + $this->redis->hmset('m', ['F' => 'V']); + + // NX - Only set expiry if it doesn't have one + foreach ([[1], [0]] as $expected) { + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'NX'); + $this->assertEquals($expected, $res); + } + + // XX - Set if it has one + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'XX'); + $this->assertEquals([1], $res); + $this->redis->hpersist('m', ['F']); + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'XX'); + $this->assertEquals([0], $res); + + // GT - should set if the new expiration is larger + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F']); + $res = $this->redis->{$exp_cmd}('m', $ttl + 100, ['F'], 'GT'); + $this->assertEquals([1], $res); + + // LT - should not set if the new expiration is smaller + $res = $this->redis->{$exp_cmd}('m', $ttl / 2, ['F'], 'LT'); + $this->assertTrue(is_array($res) && $res[0] > 0); + } + } + public function testHScan() { if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped();