Skip to content

Commit c0e839f

Browse files
LCS command
Implement the Redis 7.0.0 LCS command with tests.
1 parent 6430050 commit c0e839f

12 files changed

Lines changed: 187 additions & 6 deletions

common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,13 @@ typedef enum {
146146

147147
#define PHPREDIS_DEBUG_LOGGING 0
148148

149+
#if PHP_VERSION_ID < 80000
150+
#define Z_PARAM_ARRAY_HT_OR_NULL(dest) \
151+
Z_PARAM_ARRAY_HT_EX(dest, 1, 0)
152+
#define Z_PARAM_STR_OR_NULL(dest) \
153+
Z_PARAM_STR_EX(dest, 1, 0)
154+
#endif
155+
149156
#if PHPREDIS_DEBUG_LOGGING == 1
150157
#define redisDbgFmt(fmt, ...) \
151158
php_printf("%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__)

redis.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,12 @@ PHP_METHOD(Redis, getRange)
11821182
}
11831183
/* }}} */
11841184

1185+
/* {{{ proto mixed Redis::lcs(string $key1, string $key2, ?array $options = NULL); */
1186+
PHP_METHOD(Redis, lcs) {
1187+
REDIS_PROCESS_CMD(lcs, redis_read_variant_reply);
1188+
}
1189+
/* }}} */
1190+
11851191
/* {{{ proto string Redis::setRange(string key, long start, string value) */
11861192
PHP_METHOD(Redis, setRange)
11871193
{

redis.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ public function getPort(): int;
173173
/** @return string|Redis */
174174
public function getRange(string $key, int $start, int $end);
175175

176+
public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
177+
176178
public function getReadTimeout(): int;
177179

178180
/** @return string|Redis */

redis_arginfo.h

Lines changed: 9 additions & 1 deletion
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: f43a528bc5874c419161b5eb874537fbe9c00e87 */
2+
* Stub hash: 1c32099810101448fc32ce331e2b494dabc22cc4 */
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")
@@ -300,6 +300,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3)
300300
ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
301301
ZEND_END_ARG_INFO()
302302

303+
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)
304+
ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0)
305+
ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0)
306+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
307+
ZEND_END_ARG_INFO()
308+
303309
#define arginfo_class_Redis_getReadTimeout arginfo_class_Redis_dbSize
304310

305311
#define arginfo_class_Redis_getset arginfo_class_Redis_append
@@ -1061,6 +1067,7 @@ ZEND_METHOD(Redis, getOption);
10611067
ZEND_METHOD(Redis, getPersistentID);
10621068
ZEND_METHOD(Redis, getPort);
10631069
ZEND_METHOD(Redis, getRange);
1070+
ZEND_METHOD(Redis, lcs);
10641071
ZEND_METHOD(Redis, getReadTimeout);
10651072
ZEND_METHOD(Redis, getset);
10661073
ZEND_METHOD(Redis, getTimeout);
@@ -1295,6 +1302,7 @@ static const zend_function_entry class_Redis_methods[] = {
12951302
ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC)
12961303
ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC)
12971304
ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC)
1305+
ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC)
12981306
ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC)
12991307
ZEND_ME(Redis, getset, arginfo_class_Redis_getset, ZEND_ACC_PUBLIC)
13001308
ZEND_ME(Redis, getTimeout, arginfo_class_Redis_getTimeout, ZEND_ACC_PUBLIC)

redis_cluster.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,13 +1315,18 @@ PHP_METHOD(RedisCluster, lget) {
13151315
}
13161316
/* }}} */
13171317

1318-
/* {{{ proto string RedisCluster::getrange(string key, long start, long end) */
1319-
PHP_METHOD(RedisCluster, getrange) {
1318+
/* {{{ proto string RedisCluster::getrange(string key, long start, long end) */ PHP_METHOD(RedisCluster, getrange) {
13201319
CLUSTER_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd,
13211320
cluster_bulk_resp, 1);
13221321
}
13231322
/* }}} */
13241323

1324+
/* {{{ prot RedisCluster::lcs(string $key1, string $key2, ?array $options = NULL): mixed; */
1325+
PHP_METHOD(RedisCluster, lcs) {
1326+
CLUSTER_PROCESS_CMD(lcs, cluster_variant_resp, 1);
1327+
}
1328+
1329+
/* }}} */
13251330
/* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */
13261331
PHP_METHOD(RedisCluster, ltrim) {
13271332
CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp, 0);

redis_cluster.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ public function getoption(int $option): mixed;
124124

125125
public function getrange(string $key, int $start, int $end): string;
126126

127+
public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false;
128+
127129
public function getset(string $key, mixed $value): string;
128130

129131
public function hdel(string $key, string $member, string ...$other_members): int;

redis_cluster_arginfo.h

Lines changed: 9 additions & 1 deletion
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: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
2+
* Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
55
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,
253253
ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0)
254254
ZEND_END_ARG_INFO()
255255

256+
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)
257+
ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0)
258+
ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0)
259+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL")
260+
ZEND_END_ARG_INFO()
261+
256262
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_getset, 0, 2, IS_STRING, 0)
257263
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
258264
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
@@ -904,6 +910,7 @@ ZEND_METHOD(RedisCluster, getlasterror);
904910
ZEND_METHOD(RedisCluster, getmode);
905911
ZEND_METHOD(RedisCluster, getoption);
906912
ZEND_METHOD(RedisCluster, getrange);
913+
ZEND_METHOD(RedisCluster, lcs);
907914
ZEND_METHOD(RedisCluster, getset);
908915
ZEND_METHOD(RedisCluster, hdel);
909916
ZEND_METHOD(RedisCluster, hexists);
@@ -1100,6 +1107,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
11001107
ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
11011108
ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
11021109
ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
1110+
ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
11031111
ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
11041112
ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
11051113
ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)

redis_cluster_legacy_arginfo.h

Lines changed: 9 additions & 1 deletion
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: c9bfd7de5c930c9d7190139e207eb6025e33bdb2 */
2+
* Stub hash: 45d120a35a1c256964c45c71ab91c025ea00e862 */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
55
ZEND_ARG_INFO(0, name)
@@ -232,6 +232,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_getrange, 0, 0, 3)
232232
ZEND_ARG_INFO(0, end)
233233
ZEND_END_ARG_INFO()
234234

235+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_lcs, 0, 0, 2)
236+
ZEND_ARG_INFO(0, key1)
237+
ZEND_ARG_INFO(0, key2)
238+
ZEND_ARG_INFO(0, options)
239+
ZEND_END_ARG_INFO()
240+
235241
#define arginfo_class_RedisCluster_getset arginfo_class_RedisCluster_append
236242

237243
#define arginfo_class_RedisCluster_hdel arginfo_class_RedisCluster_geohash
@@ -792,6 +798,7 @@ ZEND_METHOD(RedisCluster, getlasterror);
792798
ZEND_METHOD(RedisCluster, getmode);
793799
ZEND_METHOD(RedisCluster, getoption);
794800
ZEND_METHOD(RedisCluster, getrange);
801+
ZEND_METHOD(RedisCluster, lcs);
795802
ZEND_METHOD(RedisCluster, getset);
796803
ZEND_METHOD(RedisCluster, hdel);
797804
ZEND_METHOD(RedisCluster, hexists);
@@ -988,6 +995,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
988995
ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC)
989996
ZEND_ME(RedisCluster, getoption, arginfo_class_RedisCluster_getoption, ZEND_ACC_PUBLIC)
990997
ZEND_ME(RedisCluster, getrange, arginfo_class_RedisCluster_getrange, ZEND_ACC_PUBLIC)
998+
ZEND_ME(RedisCluster, lcs, arginfo_class_RedisCluster_lcs, ZEND_ACC_PUBLIC)
991999
ZEND_ME(RedisCluster, getset, arginfo_class_RedisCluster_getset, ZEND_ACC_PUBLIC)
9921000
ZEND_ME(RedisCluster, hdel, arginfo_class_RedisCluster_hdel, ZEND_ACC_PUBLIC)
9931001
ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC)

redis_commands.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ typedef struct geoOptions {
5858
zend_string *key;
5959
} geoOptions;
6060

61+
typedef struct redisLcsOptions {
62+
zend_bool len;
63+
zend_bool idx;
64+
zend_long minmatchlen;
65+
zend_bool withmatchlen;
66+
} redisLcsOptions;
67+
6168
/* Local passthrough macro for command construction. Given that these methods
6269
* are generic (so they work whether the caller is Redis or RedisCluster) we
6370
* will always have redis_sock, slot*, and */
@@ -2222,6 +2229,102 @@ redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
22222229
return SUCCESS;
22232230
}
22242231

2232+
void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) {
2233+
zend_string *key;
2234+
zval *zv;
2235+
2236+
ZEND_ASSERT(dst != NULL);
2237+
2238+
memset(dst, 0, sizeof(*dst));
2239+
2240+
if (ht == NULL)
2241+
return;
2242+
2243+
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) {
2244+
if (key) {
2245+
if (zend_string_equals_literal_ci(key, "LEN")) {
2246+
dst->idx = 0;
2247+
dst->len = zval_is_true(zv);
2248+
} else if (zend_string_equals_literal_ci(key, "IDX")) {
2249+
dst->len = 0;
2250+
dst->idx = zval_is_true(zv);
2251+
} else if (zend_string_equals_literal_ci(key, "MINMATCHLEN")) {
2252+
dst->minmatchlen = zval_get_long(zv);
2253+
} else if (zend_string_equals_literal_ci(key, "WITHMATCHLEN")) {
2254+
dst->withmatchlen = zval_is_true(zv);
2255+
} else {
2256+
php_error_docref(NULL, E_WARNING, "Unknown LCS option '%s'", ZSTR_VAL(key));
2257+
}
2258+
} else if (Z_TYPE_P(zv) == IS_STRING) {
2259+
if (zend_string_equals_literal_ci(Z_STR_P(zv), "LEN")) {
2260+
dst->idx = 0;
2261+
dst->len = 1;
2262+
} else if (zend_string_equals_literal_ci(Z_STR_P(zv), "IDX")) {
2263+
dst->idx = 1;
2264+
dst->len = 0;
2265+
} else if (zend_string_equals_literal_ci(Z_STR_P(zv), "WITHMATCHLEN")) {
2266+
dst->withmatchlen = 1;
2267+
}
2268+
}
2269+
} ZEND_HASH_FOREACH_END();
2270+
}
2271+
2272+
/* LCS */
2273+
int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
2274+
char **cmd, int *cmd_len, short *slot, void **ctx)
2275+
{
2276+
zend_string *key1 = NULL, *key2 = NULL;
2277+
smart_string cmdstr = {0};
2278+
HashTable *ht = NULL;
2279+
redisLcsOptions opt;
2280+
int argc;
2281+
2282+
ZEND_PARSE_PARAMETERS_START(2, 3)
2283+
Z_PARAM_STR(key1)
2284+
Z_PARAM_STR(key2)
2285+
Z_PARAM_OPTIONAL
2286+
Z_PARAM_ARRAY_HT_OR_NULL(ht)
2287+
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
2288+
2289+
key1 = redis_key_prefix_zstr(redis_sock, key1);
2290+
key2 = redis_key_prefix_zstr(redis_sock, key2);
2291+
2292+
if (slot) {
2293+
*slot = cluster_hash_key_zstr(key1);
2294+
if (*slot != cluster_hash_key_zstr(key2)) {
2295+
php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!");
2296+
zend_string_release(key1);
2297+
zend_string_release(key2);
2298+
return FAILURE;
2299+
}
2300+
}
2301+
2302+
redis_get_lcs_options(&opt, ht);
2303+
2304+
argc = 2 + !!opt.idx + !!opt.len + !!opt.withmatchlen + (opt.minmatchlen ? 2 : 0);
2305+
REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LCS");
2306+
2307+
redis_cmd_append_sstr_zstr(&cmdstr, key1);
2308+
redis_cmd_append_sstr_zstr(&cmdstr, key2);
2309+
2310+
REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.idx, "IDX");
2311+
REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.len, "LEN");
2312+
REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withmatchlen, "WITHMATCHLEN");
2313+
2314+
if (opt.minmatchlen) {
2315+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINMATCHLEN");
2316+
redis_cmd_append_sstr_long(&cmdstr, opt.minmatchlen);
2317+
}
2318+
2319+
zend_string_release(key1);
2320+
zend_string_release(key2);
2321+
2322+
*cmd = cmdstr.c;
2323+
*cmd_len = cmdstr.len;
2324+
return SUCCESS;
2325+
}
2326+
2327+
22252328
/* BITPOS */
22262329
int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
22272330
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
@@ -129,6 +129,9 @@ int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
129129
char *kw, char **cmd, int *cmd_len, short *slot,
130130
void **ctx);
131131

132+
int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
133+
char **cmd, int *cmd_len, short *slot, void **ctx);
134+
132135
int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
133136
char *kw, char **cmd, int *cmd_len, short *slot, void **ctx);
134137

0 commit comments

Comments
 (0)