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:
authormichael-grunder <michael.grunder@gmail.com>2022-10-01 20:26:11 +0300
committerMichael Grunder <michael.grunder@gmail.com>2022-10-01 20:42:23 +0300
commitc0e839f6ac45d51adbb109b246c9b3565c3b61e1 (patch)
tree16c20e37e1c39971a5c788e8e2e508c00aa23d71
parent643005080839b35c40d78af40eda3e2743ad4d6f (diff)
LCS command
Implement the Redis 7.0.0 LCS command with tests.
-rw-r--r--common.h7
-rw-r--r--redis.c6
-rw-r--r--redis.stub.php2
-rw-r--r--redis_arginfo.h10
-rw-r--r--redis_cluster.c9
-rw-r--r--redis_cluster.stub.php2
-rw-r--r--redis_cluster_arginfo.h10
-rw-r--r--redis_cluster_legacy_arginfo.h10
-rw-r--r--redis_commands.c103
-rw-r--r--redis_commands.h3
-rw-r--r--redis_legacy_arginfo.h10
-rw-r--r--tests/RedisTest.php21
12 files changed, 187 insertions, 6 deletions
diff --git a/common.h b/common.h
index 8af8b541..c8d6c9b1 100644
--- a/common.h
+++ b/common.h
@@ -146,6 +146,13 @@ typedef enum {
#define PHPREDIS_DEBUG_LOGGING 0
+#if PHP_VERSION_ID < 80000
+#define Z_PARAM_ARRAY_HT_OR_NULL(dest) \
+ Z_PARAM_ARRAY_HT_EX(dest, 1, 0)
+#define Z_PARAM_STR_OR_NULL(dest) \
+ Z_PARAM_STR_EX(dest, 1, 0)
+#endif
+
#if PHPREDIS_DEBUG_LOGGING == 1
#define redisDbgFmt(fmt, ...) \
php_printf("%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__)
diff --git a/redis.c b/redis.c
index ca8f96c5..1a9af65b 100644
--- a/redis.c
+++ b/redis.c
@@ -1182,6 +1182,12 @@ PHP_METHOD(Redis, getRange)
}
/* }}} */
+/* {{{ proto mixed Redis::lcs(string $key1, string $key2, ?array $options = NULL); */
+PHP_METHOD(Redis, lcs) {
+ REDIS_PROCESS_CMD(lcs, redis_read_variant_reply);
+}
+/* }}} */
+
/* {{{ proto string Redis::setRange(string key, long start, string value) */
PHP_METHOD(Redis, setRange)
{
diff --git a/redis.stub.php b/redis.stub.php
index 7f125ddf..c3740b8d 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -173,6 +173,8 @@ class Redis {
/** @return string|Redis */
public function getRange(string $key, int $start, int $end);
+ public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
+
public function getReadTimeout(): int;
/** @return string|Redis */
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 33526de0..7c2cdd43 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: f43a528bc5874c419161b5eb874537fbe9c00e87 */
+ * Stub hash: 1c32099810101448fc32ce331e2b494dabc22cc4 */
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")
@@ -300,6 +300,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE)
+ ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
+ZEND_END_ARG_INFO()
+
#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_dbSize
#define arginfo_class_Redis_getset arginfo_class_Redis_append
@@ -1061,6 +1067,7 @@ ZEND_METHOD(Redis, getOption);
ZEND_METHOD(Redis, getPersistentID);
ZEND_METHOD(Redis, getPort);
ZEND_METHOD(Redis, getRange);
+ZEND_METHOD(Redis, lcs);
ZEND_METHOD(Redis, getReadTimeout);
ZEND_METHOD(Redis, getset);
ZEND_METHOD(Redis, getTimeout);
@@ -1295,6 +1302,7 @@ static const zend_function_entry class_Redis_methods[] = {
ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
+ ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster.c b/redis_cluster.c
index 4be54bf8..cee00ae2 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -1315,13 +1315,18 @@ PHP_METHOD(RedisCluster, lget) {
}
/* }}} */
-/* {{{ proto string RedisCluster::getrange(string key, long start, long end) */
-PHP_METHOD(RedisCluster, getrange) {
+/* {{{ proto string RedisCluster::getrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, getrange) {
CLUSTER_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd,
cluster_bulk_resp, 1);
}
/* }}} */
+/* {{{ prot RedisCluster::lcs(string $key1, string $key2, ?array $options = NULL): mixed; */
+PHP_METHOD(RedisCluster, lcs) {
+ CLUSTER_PROCESS_CMD(lcs, cluster_variant_resp, 1);
+}
+
+/* }}} */
/* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */
PHP_METHOD(RedisCluster, ltrim) {
CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp, 0);
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index 924b9c04..31ba4ef6 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -124,6 +124,8 @@ class RedisCluster {
public function getrange(string $key, int $start, int $end): string;
+ public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
+
public function getset(string $key, mixed $value): string;
public function hdel(string $key, string $member, string ...$other_members): int;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 35f2ab11..bbf10c92 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: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
+ * Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -253,6 +253,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getrange, 0,
ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE)
+ ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getset, 0, 2, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
@@ -904,6 +910,7 @@ ZEND_METHOD(RedisCluster, getlasterror);
ZEND_METHOD(RedisCluster, getmode);
ZEND_METHOD(RedisCluster, getoption);
ZEND_METHOD(RedisCluster, getrange);
+ZEND_METHOD(RedisCluster, lcs);
ZEND_METHOD(RedisCluster, getset);
ZEND_METHOD(RedisCluster, hdel);
ZEND_METHOD(RedisCluster, hexists);
@@ -1100,6 +1107,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
+ ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index bff23cd6..5669c8be 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: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
+ * Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_INFO(0, name)
@@ -232,6 +232,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getrange, 0, 0, 3)
ZEND_ARG_INFO(0, end)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lcs, 0, 0, 2)
+ ZEND_ARG_INFO(0, key1)
+ ZEND_ARG_INFO(0, key2)
+ ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
#define arginfo_class_RedisCluster_getset arginfo_class_RedisCluster_append
#define arginfo_class_RedisCluster_hdel arginfo_class_RedisCluster_geohash
@@ -792,6 +798,7 @@ ZEND_METHOD(RedisCluster, getlasterror);
ZEND_METHOD(RedisCluster, getmode);
ZEND_METHOD(RedisCluster, getoption);
ZEND_METHOD(RedisCluster, getrange);
+ZEND_METHOD(RedisCluster, lcs);
ZEND_METHOD(RedisCluster, getset);
ZEND_METHOD(RedisCluster, hdel);
ZEND_METHOD(RedisCluster, hexists);
@@ -988,6 +995,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
+ ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index 1db729a6..22c19a8d 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -58,6 +58,13 @@ typedef struct geoOptions {
zend_string *key;
} geoOptions;
+typedef struct redisLcsOptions {
+ zend_bool len;
+ zend_bool idx;
+ zend_long minmatchlen;
+ zend_bool withmatchlen;
+} redisLcsOptions;
+
/* Local passthrough macro for command construction. Given that these methods
* are generic (so they work whether the caller is Redis or RedisCluster) we
* will always have redis_sock, slot*, and */
@@ -2222,6 +2229,102 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
return SUCCESS;
}
+void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) {
+ zend_string *key;
+ zval *zv;
+
+ ZEND_ASSERT(dst != NULL);
+
+ memset(dst, 0, sizeof(*dst));
+
+ if (ht == NULL)
+ return;
+
+ ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) {
+ if (key) {
+ if (zend_string_equals_literal_ci(key, "LEN")) {
+ dst->idx = 0;
+ dst->len = zval_is_true(zv);
+ } else if (zend_string_equals_literal_ci(key, "IDX")) {
+ dst->len = 0;
+ dst->idx = zval_is_true(zv);
+ } else if (zend_string_equals_literal_ci(key, "MINMATCHLEN")) {
+ dst->minmatchlen = zval_get_long(zv);
+ } else if (zend_string_equals_literal_ci(key, "WITHMATCHLEN")) {
+ dst->withmatchlen = zval_is_true(zv);
+ } else {
+ php_error_docref(NULL, E_WARNING, "Unknown LCS option '%s'", ZSTR_VAL(key));
+ }
+ } else if (Z_TYPE_P(zv) == IS_STRING) {
+ if (zend_string_equals_literal_ci(Z_STR_P(zv), "LEN")) {
+ dst->idx = 0;
+ dst->len = 1;
+ } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "IDX")) {
+ dst->idx = 1;
+ dst->len = 0;
+ } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "WITHMATCHLEN")) {
+ dst->withmatchlen = 1;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+}
+
+/* LCS */
+int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+ char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+ zend_string *key1 = NULL, *key2 = NULL;
+ smart_string cmdstr = {0};
+ HashTable *ht = NULL;
+ redisLcsOptions opt;
+ int argc;
+
+ ZEND_PARSE_PARAMETERS_START(2, 3)
+ Z_PARAM_STR(key1)
+ Z_PARAM_STR(key2)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_ARRAY_HT_OR_NULL(ht)
+ ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+ key1 = redis_key_prefix_zstr(redis_sock, key1);
+ key2 = redis_key_prefix_zstr(redis_sock, key2);
+
+ if (slot) {
+ *slot = cluster_hash_key_zstr(key1);
+ if (*slot != cluster_hash_key_zstr(key2)) {
+ php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!");
+ zend_string_release(key1);
+ zend_string_release(key2);
+ return FAILURE;
+ }
+ }
+
+ redis_get_lcs_options(&opt, ht);
+
+ argc = 2 + !!opt.idx + !!opt.len + !!opt.withmatchlen + (opt.minmatchlen ? 2 : 0);
+ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LCS");
+
+ redis_cmd_append_sstr_zstr(&cmdstr, key1);
+ redis_cmd_append_sstr_zstr(&cmdstr, key2);
+
+ REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.idx, "IDX");
+ REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.len, "LEN");
+ REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withmatchlen, "WITHMATCHLEN");
+
+ if (opt.minmatchlen) {
+ REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINMATCHLEN");
+ redis_cmd_append_sstr_long(&cmdstr, opt.minmatchlen);
+ }
+
+ zend_string_release(key1);
+ zend_string_release(key2);
+
+ *cmd = cmdstr.c;
+ *cmd_len = cmdstr.len;
+ return SUCCESS;
+}
+
+
/* BITPOS */
int redis_bitpos_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 24fbe92c..c51896ad 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -129,6 +129,9 @@ int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char *kw, char **cmd, int *cmd_len, short *slot,
void **ctx);
+int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+ char **cmd, int *cmd_len, short *slot, void **ctx);
+
int redis_subscribe_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 aaf02d19..7954c6f2 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: f43a528bc5874c419161b5eb874537fbe9c00e87 */
+ * Stub hash: 1c32099810101448fc32ce331e2b494dabc22cc4 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
ZEND_ARG_INFO(0, options)
@@ -279,6 +279,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
ZEND_ARG_INFO(0, end)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_lcs, 0, 0, 2)
+ ZEND_ARG_INFO(0, key1)
+ ZEND_ARG_INFO(0, key2)
+ ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO()
+
#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis___destruct
#define arginfo_class_Redis_getset arginfo_class_Redis_append
@@ -953,6 +959,7 @@ ZEND_METHOD(Redis, getOption);
ZEND_METHOD(Redis, getPersistentID);
ZEND_METHOD(Redis, getPort);
ZEND_METHOD(Redis, getRange);
+ZEND_METHOD(Redis, lcs);
ZEND_METHOD(Redis, getReadTimeout);
ZEND_METHOD(Redis, getset);
ZEND_METHOD(Redis, getTimeout);
@@ -1187,6 +1194,7 @@ static const zend_function_entry class_Redis_methods[] = {
ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
+ ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 551c6346..2e4402f3 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -272,6 +272,27 @@ class Redis_Test extends TestSuite
$this->assertEquals(1, $this->redis->getBit('key', 0x7fffffff));
}
+ public function testLcs() {
+ $key1 = '{lcs}1'; $key2 = '{lcs}2';
+ $this->assertTrue($this->redis->set($key1, '12244447777777'));
+ $this->assertTrue($this->redis->set($key2, '6666662244441'));
+
+ $this->assertEquals('224444', $this->redis->lcs($key1, $key2));
+
+ $this->assertEquals(
+ ['matches', [[[1, 6], [6, 11]]], 'len', 6],
+ $this->redis->lcs($key1, $key2, ['idx'])
+ );
+ $this->assertEquals(
+ ['matches', [[[1, 6], [6, 11], 6]], 'len', 6],
+ $this->redis->lcs($key1, $key2, ['idx', 'withmatchlen'])
+ );
+
+ $this->assertEquals(6, $this->redis->lcs($key1, $key2, ['len']));
+
+ $this->redis->del([$key1, $key2]);
+ }
+
public function testBitPos() {
if (version_compare($this->version, "2.8.7") < 0) {
$this->MarkTestSkipped();