diff options
-rw-r--r-- | library.c | 69 | ||||
-rw-r--r-- | library.h | 1 | ||||
-rw-r--r-- | php_redis.h | 2 | ||||
-rw-r--r-- | redis.c | 8 | ||||
-rw-r--r-- | redis.stub.php | 4 | ||||
-rw-r--r-- | redis_arginfo.h | 23 | ||||
-rw-r--r-- | redis_commands.c | 242 | ||||
-rw-r--r-- | redis_commands.h | 6 | ||||
-rw-r--r-- | redis_legacy_arginfo.h | 23 | ||||
-rw-r--r-- | tests/RedisClusterTest.php | 2 | ||||
-rw-r--r-- | tests/RedisTest.php | 28 |
11 files changed, 405 insertions, 3 deletions
@@ -1543,6 +1543,75 @@ redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, size_t buflen, return type == TYPE_LINE ? 0 : -1; } +static int +geosearch_cast(zval *zv) +{ + if (Z_TYPE_P(zv) == IS_ARRAY) { + zend_hash_apply(Z_ARRVAL_P(zv), geosearch_cast); + } else if (Z_TYPE_P(zv) == IS_STRING) { + convert_to_double(zv); + } + return SUCCESS; +} + +PHP_REDIS_API int +redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + int numElems; + zval z_ret, z_multi_result, z_sub, z_tmp, *z_ele, *zv; + zend_string *zkey; + + if (read_mbulk_header(redis_sock, &numElems) < 0) { + return FAILURE; + } + + if (numElems < 0 && redis_sock->null_mbulk_as_null) { + ZVAL_NULL(&z_ret); + } else { + array_init(&z_ret); + if (ctx == NULL) { + redis_mbulk_reply_loop(redis_sock, &z_ret, numElems, UNSERIALIZE_NONE); + } else { + array_init(&z_multi_result); + redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_multi_result); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_multi_result), z_ele) { + // The first item in the sub-array is always the name of the returned item + zv = zend_hash_index_find(Z_ARRVAL_P(z_ele), 0); + zkey = zval_get_string(zv); + + zend_hash_index_del(Z_ARRVAL_P(z_ele), 0); + + // The other information is returned in the following order as successive + // elements of the sub-array: distance, geohash, coordinates + zend_hash_apply(Z_ARRVAL_P(z_ele), geosearch_cast); + + // Copy values to re-order from zero + array_init(&z_sub); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_ele), zv) { + ZVAL_ZVAL(&z_tmp, zv, 1, 0); + add_next_index_zval(&z_sub, &z_tmp); + } ZEND_HASH_FOREACH_END(); + + add_assoc_zval_ex(&z_ret, ZSTR_VAL(zkey), ZSTR_LEN(zkey), &z_sub); + zend_string_release(zkey); + } ZEND_HASH_FOREACH_END(); + + // Cleanup + zval_dtor(&z_multi_result); + } + } + + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(&z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, &z_ret); + } + + return SUCCESS; +} + /* Helper function to consume Redis stream message data. This is useful for * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */ PHP_REDIS_API int @@ -165,6 +165,7 @@ PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSoc PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); /* Helper methods to get configuration values from a HashTable. */ diff --git a/php_redis.h b/php_redis.h index 2c9ec50f..97ef3b55 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "5.3.2" +#define PHP_REDIS_VERSION "6.0.0-dev" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ @@ -3598,6 +3598,14 @@ PHP_METHOD(Redis, georadiusbymember_ro) { REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, redis_read_variant_reply); } +PHP_METHOD(Redis, geosearch) { + REDIS_PROCESS_CMD(geosearch, redis_geosearch_response); +} + +PHP_METHOD(Redis, geosearchstore) { + REDIS_PROCESS_CMD(geosearchstore, redis_long_response); +} + /* * Streams */ diff --git a/redis.stub.php b/redis.stub.php index 1cef27c7..1ab3b318 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -137,6 +137,10 @@ class Redis { public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): array; + public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array; + + public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): array; + /** @return string|Redis */ public function get(string $key); diff --git a/redis_arginfo.h b/redis_arginfo.h index 2efa4481..09b2fda4 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: 1c4a88252c8b66263f1b1d72af974ba5ce992d40 */ + * Stub hash: c2cbe49e22cba6f23e98c1676b7769c55a6fd043 */ 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") @@ -229,6 +229,23 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearch, 0, 4, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) + ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) + ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 5, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) + ZEND_ARG_TYPE_MASK(0, position, MAY_BE_ARRAY|MAY_BE_STRING, NULL) + ZEND_ARG_TYPE_MASK(0, shape, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_DOUBLE, NULL) + ZEND_ARG_TYPE_INFO(0, unit, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_get arginfo_class_Redis_decr ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0) @@ -974,6 +991,8 @@ ZEND_METHOD(Redis, georadius); ZEND_METHOD(Redis, georadius_ro); ZEND_METHOD(Redis, georadiusbymember); ZEND_METHOD(Redis, georadiusbymember_ro); +ZEND_METHOD(Redis, geosearch); +ZEND_METHOD(Redis, geosearchstore); ZEND_METHOD(Redis, get); ZEND_METHOD(Redis, getAuth); ZEND_METHOD(Redis, getBit); @@ -1197,6 +1216,8 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index 3a274ebe..895078da 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -3316,6 +3316,248 @@ int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s return SUCCESS; } +int +redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + char *key, *unit; + int argc = 2; + short store_slot = 0; + size_t keylen, unitlen; + geoOptions gopts = {0}; + smart_string cmdstr = {0}; + zval *position, *shape, *opts = NULL, *z_ele; + zend_string *zkey; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a", + &key, &keylen, &position, &shape, + &unit, &unitlen, &opts) == FAILURE) + { + return FAILURE; + } + + if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { + argc += 2; + } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) { + argc += 3; + } else { + php_error_docref(NULL, E_WARNING, "Invalid position"); + return FAILURE; + } + + if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { + argc += 2; + } else if (Z_TYPE_P(shape) == IS_ARRAY) { + argc += 3; + } else { + php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); + return FAILURE; + } + + /* Attempt to parse our options array */ + if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { + ZVAL_DEREF(z_ele); + if (zkey != NULL) { + if (zend_string_equals_literal_ci(zkey, "COUNT")) { + if (Z_TYPE_P(z_ele) != IS_LONG || Z_LVAL_P(z_ele) <= 0) { + php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!"); + return FAILURE; + } + gopts.count = Z_LVAL_P(z_ele); + } + } else if (Z_TYPE_P(z_ele) == IS_STRING) { + if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHCOORD")) { + gopts.withcoord = 1; + } else if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHDIST")) { + gopts.withdist = 1; + } else if (!strcasecmp(Z_STRVAL_P(z_ele), "WITHHASH")) { + gopts.withhash = 1; + } else if (!strcasecmp(Z_STRVAL_P(z_ele), "ASC")) { + gopts.sort = SORT_ASC; + } else if (!strcasecmp(Z_STRVAL_P(z_ele), "DESC")) { + gopts.sort = SORT_DESC; + } + } + } ZEND_HASH_FOREACH_END(); + } + + /* Increment argc based on options */ + argc += gopts.withcoord + gopts.withdist + gopts.withhash + + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0); + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCH"); + redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + + if (Z_TYPE_P(position) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); + } + + if (Z_TYPE_P(shape) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); + } + redis_cmd_append_sstr(&cmdstr, unit, unitlen); + + /* Append optional arguments */ + if (gopts.withcoord) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHCOORD"); + if (gopts.withdist) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHDIST"); + if (gopts.withhash) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHHASH"); + + /* Append sort if it's not GEO_NONE */ + if (gopts.sort == SORT_ASC) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC"); + } else if (gopts.sort == SORT_DESC) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC"); + } + + /* Append our count if we've got one */ + if (gopts.count) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); + redis_cmd_append_sstr_long(&cmdstr, gopts.count); + } + + if ((argc = gopts.withcoord + gopts.withdist + gopts.withhash) > 0) { + *ctx = PHPREDIS_CTX_PTR; + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int +redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + int argc = 3; + char *dest, *src, *unit; + short store_slot = 0; + size_t destlen, srclen, unitlen; + geoOptions gopts = {0}; + smart_string cmdstr = {0}; + zval *position, *shape, *opts = NULL, *z_ele; + zend_string *zkey; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszzs|a", + &dest, &destlen, &src, &srclen, &position, &shape, + &unit, &unitlen, &opts) == FAILURE) + { + return FAILURE; + } + + if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { + argc += 2; + } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) { + argc += 3; + } else { + php_error_docref(NULL, E_WARNING, "Invalid position"); + return FAILURE; + } + + if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { + argc += 2; + } else if (Z_TYPE_P(shape) == IS_ARRAY) { + argc += 3; + } else { + php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); + return FAILURE; + } + + /* Attempt to parse our options array */ + if (opts != NULL && Z_TYPE_P(opts) == IS_ARRAY) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { + ZVAL_DEREF(z_ele); + if (zkey != NULL) { + if (zend_string_equals_literal_ci(zkey, "COUNT")) { + if (Z_TYPE_P(z_ele) != IS_LONG || Z_LVAL_P(z_ele) <= 0) { + php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!"); + return FAILURE; + } + gopts.count = Z_LVAL_P(z_ele); + } + } else if (Z_TYPE_P(z_ele) == IS_STRING) { + if (!strcasecmp(Z_STRVAL_P(z_ele), "ASC")) { + gopts.sort = SORT_ASC; + } else if (!strcasecmp(Z_STRVAL_P(z_ele), "DESC")) { + gopts.sort = SORT_DESC; + } else if (!strcasecmp(Z_STRVAL_P(z_ele), "STOREDIST")) { + gopts.store = STORE_DIST; + } + } + } ZEND_HASH_FOREACH_END(); + + } + + /* Increment argc based on options */ + argc += gopts.withcoord + gopts.withdist + gopts.withhash + + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0) + + (gopts.store != STORE_NONE); + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCHSTORE"); + redis_cmd_append_sstr_key(&cmdstr, dest, destlen, redis_sock, slot); + redis_cmd_append_sstr_key(&cmdstr, src, srclen, redis_sock, slot); + + if (Z_TYPE_P(position) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); + } + + if (Z_TYPE_P(shape) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); + } + redis_cmd_append_sstr(&cmdstr, unit, unitlen); + + /* Append sort if it's not GEO_NONE */ + if (gopts.sort == SORT_ASC) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC"); + } else if (gopts.sort == SORT_DESC) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC"); + } + + /* Append our count if we've got one */ + if (gopts.count) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); + redis_cmd_append_sstr_long(&cmdstr, gopts.count); + } + + if (gopts.store == STORE_DIST) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "STOREDIST"); + } + + *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 88c66bcf..851e28b2 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -145,6 +145,12 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + /* Commands which need a unique construction mechanism. This is either because * they don't share a signature with any other command, or because there is * specific processing we do (e.g. verifying subarguments) that make them diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index f1022664..9021f63c 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: 1c4a88252c8b66263f1b1d72af974ba5ce992d40 */ + * Stub hash: c2cbe49e22cba6f23e98c1676b7769c55a6fd043 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -217,6 +217,23 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_georadiusbymember_ro arginfo_class_Redis_georadiusbymember +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geosearch, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, position) + ZEND_ARG_INFO(0, shape) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_geosearchstore, 0, 0, 5) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, src) + ZEND_ARG_INFO(0, position) + ZEND_ARG_INFO(0, shape) + ZEND_ARG_INFO(0, unit) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_get arginfo_class_Redis__prefix #define arginfo_class_Redis_getAuth arginfo_class_Redis___destruct @@ -876,6 +893,8 @@ ZEND_METHOD(Redis, georadius); ZEND_METHOD(Redis, georadius_ro); ZEND_METHOD(Redis, georadiusbymember); ZEND_METHOD(Redis, georadiusbymember_ro); +ZEND_METHOD(Redis, geosearch); +ZEND_METHOD(Redis, geosearchstore); ZEND_METHOD(Redis, get); ZEND_METHOD(Redis, getAuth); ZEND_METHOD(Redis, getBit); @@ -1099,6 +1118,8 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, georadius_ro, arginfo_class_Redis_georadius_ro, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember, arginfo_class_Redis_georadiusbymember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, georadiusbymember_ro, arginfo_class_Redis_georadiusbymember_ro, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC) diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 394d13f2..f491e92a 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -58,6 +58,8 @@ class Redis_Cluster_Test extends Redis_Test { public function testzMscore() { return $this->marktestSkipped(); } public function testZRandMember() { return $this->marktestSkipped(); } public function testCopy() { return $this->marktestSkipped(); } + public function testGeoSearch() { return $this->marktestSkipped(); } + public function testGeoSearchStore() { return $this->marktestSkipped(); } /* Session locking feature is currently not supported in in context of Redis Cluster. The biggest issue for this is the distribution nature of Redis cluster */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index fa26d18e..66b51f7f 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -5875,6 +5875,34 @@ class Redis_Test extends TestSuite $this->assertEquals(round($r1, 8), round($r2, 8)); } + public function testGeoSearch() { + if (!$this->minVersionCheck("6.2.0")) { + return $this->markTestSkipped(); + } + + $this->addCities('gk'); + + $this->assertEquals($this->redis->geosearch('gk', 'Chico', 1, 'm'), ['Chico']); + $this->assertValidate($this->redis->geosearch('gk', 'Chico', 1, 'm', ['withcoord', 'withdist', 'withhash']), function ($v) { + $this->assertArrayKey($v, 'Chico', 'is_array'); + $this->assertEquals(count($v['Chico']), 3); + $this->assertArrayKey($v['Chico'], 0, 'is_float'); + $this->assertArrayKey($v['Chico'], 1, 'is_int'); + $this->assertArrayKey($v['Chico'], 2, 'is_array'); + return true; + }); + } + + public function testGeoSearchStore() { + if (!$this->minVersionCheck("6.2.0")) { + return $this->markTestSkipped(); + } + + $this->addCities('gk'); + $this->assertEquals($this->redis->geosearchstore('{gk}', 'gk', 'Chico', 100, 'km'), 3); + $this->assertEquals($this->redis->geosearch('{gk}', 'Chico', 1, 'm'), ['Chico']); + } + /* Test a 'raw' command */ public function testRawCommand() { $this->redis->set('mykey','some-value'); |