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-11-06 05:59:30 +0300
committermichael-grunder <michael.grunder@gmail.com>2022-11-13 07:47:10 +0300
commitbc2f42bdd5237205b5af1b8a4ef59040084615d2 (patch)
treea9b0b6361e5d983f29ce08e9f0aa729d2b573c6d
parentcf63e96ec5f6c9363bc5c6955d29c726fc7ec6fe (diff)
Add more docblocks and fix XAUTOCLAIM response handler.more-docblocks
- Finish adding docblocks with examples for all of the stream commands. - Fix XAUTOCLAIM response handler (the reply has a slightly different structure to XCLAIM.
-rw-r--r--cluster_library.c4
-rw-r--r--library.c87
-rw-r--r--library.h6
-rw-r--r--redis.stub.php304
-rw-r--r--redis_arginfo.h6
-rw-r--r--redis_cluster.c4
-rw-r--r--redis_cluster.stub.php5
-rw-r--r--redis_cluster_arginfo.h21
-rw-r--r--redis_cluster_legacy_arginfo.h21
-rw-r--r--redis_commands.c41
-rw-r--r--redis_legacy_arginfo.h2
-rw-r--r--tests/RedisTest.php31
12 files changed, 475 insertions, 57 deletions
diff --git a/cluster_library.c b/cluster_library.c
index 945c7bc1..0d04593e 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -2333,7 +2333,9 @@ cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
array_init(&z_msg);
- if (redis_read_xclaim_response(c->cmd_sock, c->reply_len, &z_msg) < 0) {
+ ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR);
+
+ if (redis_read_xclaim_reply(c->cmd_sock, c->reply_len, ctx == PHPREDIS_CTX_PTR, &z_msg) < 0) {
zval_dtor(&z_msg);
CLUSTER_RETURN_FALSE(c);
}
diff --git a/library.c b/library.c
index 1560e6b0..5130249a 100644
--- a/library.c
+++ b/library.c
@@ -1999,11 +1999,9 @@ failure:
return -1;
}
-/* This helper function does that actual XCLAIM response handling, which can be used by both
- * Redis and RedisCluster. Note that XCLAIM is somewhat unique in that its reply type depends
- * on whether or not it was called with the JUSTID option */
-PHP_REDIS_API int
-redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) {
+/* A helper method to read X[AUTO]CLAIM messages into an array. */
+static int
+redis_read_xclaim_ids(RedisSock *redis_sock, int count, zval *rv) {
zval z_msg;
REDIS_REPLY_TYPE type;
char *id = NULL;
@@ -2011,6 +2009,8 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) {
long li;
for (i = 0; i < count; i++) {
+ id = NULL;
+
/* Consume inner reply type */
if (redis_read_reply_type(redis_sock, &type, &li) < 0 ||
(type != TYPE_BULK && type != TYPE_MULTIBULK) ||
@@ -2043,29 +2043,88 @@ redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv) {
return 0;
}
+/* Read an X[AUTO]CLAIM reply having already consumed the reply-type byte. */
+PHP_REDIS_API int
+redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv) {
+ REDIS_REPLY_TYPE type;
+ zval z_msgs = {0};
+ char *id = NULL;
+ long id_len = 0;
+ int messages;
+
+ ZEND_ASSERT(!is_xautoclaim || count == 3);
+
+ ZVAL_UNDEF(rv);
+
+ /* If this is XAUTOCLAIM consume the BULK ID and then the actual number of IDs.
+ * Otherwise, our 'count' argument is the number of IDs. */
+ if (is_xautoclaim) {
+ if (redis_read_reply_type(redis_sock, &type, &id_len) < 0 || type != TYPE_BULK)
+ goto failure;
+ if ((id = redis_sock_read_bulk_reply(redis_sock, id_len)) == NULL)
+ goto failure;
+ if (read_mbulk_header(redis_sock, &messages) < 0)
+ goto failure;
+ } else {
+ messages = count;
+ }
+
+ array_init(&z_msgs);
+
+ if (redis_read_xclaim_ids(redis_sock, messages, &z_msgs) < 0)
+ goto failure;
+
+ /* If XAUTOCLAIM we now need to consume the final array of message IDs */
+ if (is_xautoclaim) {
+ zval z_deleted = {0};
+
+ if (redis_sock_read_multibulk_reply_zval(redis_sock, &z_deleted) == NULL)
+ goto failure;
+
+ array_init(rv);
+
+ // Package up ID, message, and deleted messages in our reply
+ add_next_index_stringl(rv, id, id_len);
+ add_next_index_zval(rv, &z_msgs);
+ add_next_index_zval(rv, &z_deleted);
+
+ efree(id);
+ } else {
+ // We just want the messages
+ ZVAL_COPY_VALUE(rv, &z_msgs);
+ }
+
+ return 0;
+
+failure:
+ zval_dtor(&z_msgs);
+ zval_dtor(rv);
+ if (id) efree(id);
+
+ return -1;
+}
+
PHP_REDIS_API int
redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
zval *z_tab, void *ctx)
{
- zval z_ret;
- int messages;
+ zval z_ret = {0};
+ int count;
- /* All XCLAIM responses start multibulk */
- if (read_mbulk_header(redis_sock, &messages) < 0)
- goto failure;
+ ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR);
- array_init(&z_ret);
+ if (read_mbulk_header(redis_sock, &count) < 0)
+ goto failure;
- if (redis_read_xclaim_response(redis_sock, messages, &z_ret) < 0) {
- zval_dtor(&z_ret);
+ if (redis_read_xclaim_reply(redis_sock, count, ctx == PHPREDIS_CTX_PTR, &z_ret) < 0)
goto failure;
- }
if (IS_ATOMIC(redis_sock)) {
RETVAL_ZVAL(&z_ret, 0, 1);
} else {
add_next_index_zval(z_tab, &z_ret);
}
+
return 0;
failure:
diff --git a/library.h b/library.h
index c52b0f2f..4b88cc19 100644
--- a/library.h
+++ b/library.h
@@ -104,8 +104,12 @@ PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS,
RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS,
RedisSock *redis_sock, zval *z_tab, void *ctx);
+
PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS,
RedisSock *redis_sock, zval *z_tab, void *ctx);
+PHP_REDIS_API int redis_read_xclaim_reply(
+ RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv);
+
PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS,
RedisSock *redis_sock, zval *z_tab, void *ctx);
@@ -149,8 +153,6 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret);
PHP_REDIS_API int
redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_ret);
PHP_REDIS_API int
-redis_read_xclaim_response(RedisSock *redis_sock, int count, zval *rv);
-PHP_REDIS_API int
redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements);
PHP_REDIS_API int
diff --git a/redis.stub.php b/redis.stub.php
index 12f77301..ab0ebb9d 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -2469,6 +2469,25 @@ class Redis {
* Redis::REDIS_ZSET
* Redis::REDIS_HASH
* Redis::REDIS_STREAM
+ *
+ * <code>
+ * <?php
+ * $redis = new Redis(['host' => 'localhost']);
+ *
+ * // NOTE: Never use 'KEYS' in production!
+ * $keys = $redis->keys('*');
+ *
+ * $redis->pipeline();
+ * foreach ($keys as $key) {
+ * $redis->type($key);
+ * }
+ *
+ * $ktypes = array_combine($keys, $redis->exec());
+ *
+ * // Print each key with its corresponding type
+ * print_r($ktypes);
+ * ?>
+ * </code>
*/
public function type(string $key): Redis|int|false;
@@ -2508,6 +2527,22 @@ class Redis {
* @see https://redis.io/commands/unsubscribe
* @see Redis::subscribe()
*
+ * <code>
+ * <?php
+ * $redis = new Redis(['host' => 'localhost']);
+ *
+ * $redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
+ * if ($message == 'quit') {
+ * echo "$channel => 'quit' detected, unsubscribing!\n";
+ * $redis->unsubscribe([$channel]);
+ * } else {
+ * echo "$channel => $message\n";
+ * }
+ * });
+ *
+ * echo "We've unsubscribed from both channels, exiting\n";
+ * ?>
+ * </code>
*/
public function unsubscribe(array $channels): Redis|array|bool;
@@ -2523,9 +2558,48 @@ class Redis {
public function unwatch(): Redis|bool;
/**
- * @return bool|Redis
+ * Watch one or more keys for conditional execution of a transaction.
+ *
+ * @see https://redis.io/commands/watch
+ * @see https://redis.io/commands/unwatch
+ *
+ * @param array|string $key_or_keys Either an array with one or more key names, or a string key name
+ * @param string $other_keys If the first argument was passed as a string, any number of additional
+ * string key names may be passed variadically.
+ *
+ * @return Redis|bool
+ *
+ * <code>
+ * <?php
+ *
+ * $redis1 = new Redis(['host' => 'localhost']);
+ * $redis2 = new Redis(['host' => 'localhost']);
+ *
+ * // Start watching 'incr-key'
+ * $redis1->watch('incr-key');
+ *
+ * // Retrieve its value.
+ * $val = $redis1->get('incr-key');
+ *
+ * // A second client modifies 'incr-key' after we read it.
+ * $redis2->set('incr-key', 0);
+ *
+ * // Because another client changed the value of 'incr-key' after we read it, this
+ * // is no longer a proper increment operation, but because we are `WATCH`ing the
+ * // key, this transaction will fail and we can try again.
+ * //
+ * // If were to comment out the above `$redis2->set('incr-key', 0)` line the
+ * // transaction would succeed.
+ * $redis1->multi();
+ * $redis1->set('incr-key', $val + 1);
+ * $res = $redis1->exec();
+ *
+ * // bool(false)
+ * var_dump($res);
+ * </code>
+ *
*/
- public function watch(array|string $key, string ...$other_keys);
+ public function watch(array|string $key, string ...$other_keys): Redis|bool;
/**
* Block the client up to the provided timeout until a certain number of replicas have confirmed
@@ -2541,6 +2615,46 @@ class Redis {
*/
public function wait(int $numreplicas, int $timeout): int|false;
+ /**
+ * Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but
+ * not yet acknowledged by XACK.)
+ *
+ * @see https://redis.io/commands/xack
+ * @see https://redis.io/commands/xreadgroup
+ * @see Redis::xack()
+ *
+ * <code>
+ * <?php
+ * $redis = new Redis(['host' => 'localhost']);
+ *
+ * $redis->del('ships');
+ *
+ * $redis->xAdd('ships', '*', ['name' => 'Enterprise']);
+ * $redis->xAdd('ships', '*', ['name' => 'Defiant']);
+ *
+ * $redis->xGroup('CREATE', 'ships', 'Federation', '0-0');
+ *
+ * // Consume a single message with the consumer group 'Federation'
+ * $ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1);
+ *
+ * /* Retrieve the ID of the message we read.
+ * assert(isset($ship['ships']));
+ * $id = key($ship['ships']);
+ *
+ * // The message we just read is now pending.
+ * $res = $redis->xPending('ships', 'Federation'));
+ * var_dump($res);
+ *
+ * // We can tell Redis we were able to process the message by using XACK
+ * $res = $redis->xAck('ships', 'Federation', [$id]);
+ * assert($res === 1);
+ *
+ * // The message should no longer be pending.
+ * $res = $redis->xPending('ships', 'Federation');
+ * var_dump($res);
+ * ?>
+ * </code>
+ */
public function xack(string $key, string $group, array $ids): int|false;
/**
@@ -2593,9 +2707,193 @@ class Redis {
*/
public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false): Redis|string|false;
+ /**
+ * This command allows a consumer to claim pending messages that have been idle for a specified period of time.
+ * Its purpose is to provide a mechanism for picking up messages that may have had a failed consumer.
+ *
+ * @see https://redis.io/commands/xautoclaim
+ * @see https://redis.io/commands/xclaim
+ * @see https://redis.io/docs/data-types/streams-tutorial/
+ *
+ * @param string $key The stream to check.
+ * @param string $group The consumer group to query.
+ * @param string $consumer Which consumer to check.
+ * @param int $min_idle The minimum time in milliseconds for the message to have been pending.
+ * @param string $start The minimum message id to check.
+ * @param int $count An optional limit on how many messages are returned.
+ * @param bool $justid If the client only wants message IDs and not all of their data.
+ *
+ * @return Redis|array|bool An array of pending IDs or false if there are none, or on failure.
+ *
+ * <code>
+ * <?php
+ * $redis = new Redis(['host' => 'localhost']);
+ *
+ * $redis->del('ships');
+ *
+ * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
+ *
+ * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
+ *
+ * // Consume the ['name' => 'Defiant'] message
+ * $msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
+ *
+ * // The "Jem'Hadar" consumer has the message presently
+ * $pending = $redis->xPending('ships', 'combatants');
+ *
+ * //array(4) {
+ * // [0]=>
+ * // int(1)
+ * // [1]=>
+ * // string(10) "1424-74205"
+ * // [2]=>
+ * // string(10) "1424-74205"
+ * // [3]=>
+ * // array(1) {
+ * // [0]=>
+ * // array(2) {
+ * // [0]=>
+ * // string(9) "Jem'Hadar"
+ * // [1]=>
+ * // string(1) "1"
+ * // }
+ * // }
+ * //}
+ * var_dump($pending);
+ *
+ * // Asssume control of the pending message with a different consumer.
+ * $res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
+ *
+ * // Now the 'Sisko' consumer owns the message
+ * $pending = $redis->xPending('ships', 'combatants');
+ *
+ * // array(4) {
+ * // [0]=>
+ * // int(1)
+ * // [1]=>
+ * // string(10) "1424-74205"
+ * // [2]=>
+ * // string(10) "1424-74205"
+ * // [3]=>
+ * // array(1) {
+ * // [0]=>
+ * // array(2) {
+ * // [0]=>
+ * // string(5) "Sisko"
+ * // [1]=>
+ * // string(1) "1"
+ * // }
+ * // }
+ * // }
+ * var_dump($pending);
+ * ?>
+ * </code>
+ */
public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array;
- public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|bool|array;
+ /**
+ * This method allows a consumer to take ownership of pending stream entries, by ID. Another
+ * command that does much the same thing but does not require passing specific IDs is `Redis::xAutoClaim`.
+ *
+ * @see https://redis.io/commands/xclaim
+ * @see https://redis.io/commands/xautoclaim.
+ *
+ * @param string $key The stream we wish to claim messages for.
+ * @param string $group Our consumer group.
+ * @param string $consumer Our consumer.
+ * @param int $min_idle_time The minimum idle-time in milliseconds a message must have for ownership to be transferred.
+ * @param array $options An options array that modifies how the command operates.
+ *
+ * <code>
+ * // Following is an options array describing every option you can pass. Note that
+ * // 'IDLE', and 'TIME' are mutually exclusive.
+ * $options = [
+ * 'IDLE' => 3 // Set the idle time of the message to a 3. By default the
+ * // idle time is set to zero.
+ * 'TIME' => 1000*time() // Same as IDLE except it takes a unix timestamp in milliseconds.
+ * 'RETRYCOUNT' => 0 // Set the retry counter to zero. By default XCLAIM doesn't modify
+ * // the counter.
+ * 'FORCE' // Creates the pending message entry even if IDs are not already
+ * // in the PEL with another client.
+ * 'JUSTID' // Return only an array of IDs rather than the messages themselves.
+ * ];
+ * </code>
+ *
+ * @return Redis|array|bool An array of claimed messags or false on failure.
+ *
+ * <code>
+ * <?php
+ * $redis = new Redis(['host' => 'localhost']);
+ *
+ * $redis->del('ships');
+ *
+ * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
+ *
+ * $redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
+ *
+ * // Consume the ['name' => 'Defiant'] message
+ * $msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
+ *
+ * // The "Jem'Hadar" consumer has the message presently
+ * $pending = $redis->xPending('ships', 'combatants');
+ *
+ * //array(4) {
+ * // [0]=>
+ * // int(1)
+ * // [1]=>
+ * // string(10) "1424-74205"
+ * // [2]=>
+ * // string(10) "1424-74205"
+ * // [3]=>
+ * // array(1) {
+ * // [0]=>
+ * // array(2) {
+ * // [0]=>
+ * // string(9) "Jem'Hadar"
+ * // [1]=>
+ * // string(1) "1"
+ * // }
+ * // }
+ * //}
+ * var_dump($pending);
+ *
+ * assert($pending && isset($pending[1]));
+ *
+ * // Claim the message by ID.
+ * $claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']);
+ *
+ * // array(1) {
+ * // [0]=>
+ * // string(10) "1424-74205"
+ * // }
+ * var_dump($claimed);
+ *
+ * // Now the 'Sisko' consumer owns the message
+ * $pending = $redis->xPending('ships', 'combatants');
+ *
+ * // array(4) {
+ * // [0]=>
+ * // int(1)
+ * // [1]=>
+ * // string(10) "1424-74205"
+ * // [2]=>
+ * // string(10) "1424-74205"
+ * // [3]=>
+ * // array(1) {
+ * // [0]=>
+ * // array(2) {
+ * // [0]=>
+ * // string(5) "Sisko"
+ * // [1]=>
+ * // string(1) "1"
+ * // }
+ * // }
+ * // }
+ * var_dump($pending);
+ * ?>
+ * </code>
+ */
+ public function xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options): Redis|array|bool;
/**
* Remove one or more specific IDs from a stream.
diff --git a/redis_arginfo.h b/redis_arginfo.h
index a13002a1..84d0e846 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: 42952974e3686f29934dfff1ebba07150942a405 */
+ * Stub hash: 1cd52318293e6a57df77b2d4a8f31ef126b6321d */
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")
@@ -867,7 +867,7 @@ ZEND_END_ARG_INFO()
#define arginfo_class_Redis_unwatch arginfo_class_Redis_bgSave
-ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_watch, 0, 0, 1)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_watch, 0, 1, Redis, MAY_BE_BOOL)
ZEND_ARG_TYPE_MASK(0, key, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0)
ZEND_END_ARG_INFO()
@@ -902,7 +902,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xautoclaim, 0, 5
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false")
ZEND_END_ARG_INFO()
-ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, Redis, MAY_BE_BOOL|MAY_BE_ARRAY)
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xclaim, 0, 6, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
diff --git a/redis_cluster.c b/redis_cluster.c
index e611315d..66225e5c 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -2904,6 +2904,10 @@ PHP_METHOD(RedisCluster, xclaim) {
CLUSTER_PROCESS_CMD(xclaim, cluster_xclaim_resp, 0);
}
+PHP_METHOD(RedisCluster, xautoclaim) {
+ CLUSTER_PROCESS_CMD(xautoclaim, cluster_xclaim_resp, 0);
+}
+
PHP_METHOD(RedisCluster, xdel) {
CLUSTER_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, cluster_long_resp, 0);
}
diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php
index f4ff298f..f61413e2 100644
--- a/redis_cluster.stub.php
+++ b/redis_cluster.stub.php
@@ -518,9 +518,12 @@ class RedisCluster {
public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): RedisCluster|string|array|false;
+ public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): Redis|bool|array;
+
public function xdel(string $key, array $ids): RedisCluster|int|false;
- public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
+ public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null,
+ bool $mkstream = false, int $entries_read = -2): mixed;
public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h
index 88736406..3171891c 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: 65c7830c07ea86720c6089dbd0fa7943df0a2ca8 */
+ * Stub hash: 2353428a5f0097623b69a28662ee81e24fbaa89c */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -765,6 +765,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xclaim, 0
ZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xautoclaim, 0, 5, Redis, MAY_BE_BOOL|MAY_BE_ARRAY)
+ ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, consumer, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO(0, min_idle, IS_LONG, 0)
+ ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, justid, _IS_BOOL, 0, "false")
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xdel, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0)
@@ -773,9 +783,10 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, IS_MIXED, 0)
ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null")
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg3, _IS_BOOL, 0, "false")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 1, IS_MIXED, 0)
@@ -1120,6 +1131,7 @@ ZEND_METHOD(RedisCluster, watch);
ZEND_METHOD(RedisCluster, xack);
ZEND_METHOD(RedisCluster, xadd);
ZEND_METHOD(RedisCluster, xclaim);
+ZEND_METHOD(RedisCluster, xautoclaim);
ZEND_METHOD(RedisCluster, xdel);
ZEND_METHOD(RedisCluster, xgroup);
ZEND_METHOD(RedisCluster, xinfo);
@@ -1329,6 +1341,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC)
+ ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC)
diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h
index ee4cfafa..ead0ae76 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: 65c7830c07ea86720c6089dbd0fa7943df0a2ca8 */
+ * Stub hash: 2353428a5f0097623b69a28662ee81e24fbaa89c */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_INFO(0, name)
@@ -645,6 +645,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xclaim, 0, 0, 6)
ZEND_ARG_INFO(0, options)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xautoclaim, 0, 0, 5)
+ ZEND_ARG_INFO(0, key)
+ ZEND_ARG_INFO(0, group)
+ ZEND_ARG_INFO(0, consumer)
+ ZEND_ARG_INFO(0, min_idle)
+ ZEND_ARG_INFO(0, start)
+ ZEND_ARG_INFO(0, count)
+ ZEND_ARG_INFO(0, justid)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xdel, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, ids)
@@ -653,9 +663,10 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 0, 1)
ZEND_ARG_INFO(0, operation)
ZEND_ARG_INFO(0, key)
- ZEND_ARG_INFO(0, arg1)
- ZEND_ARG_INFO(0, arg2)
- ZEND_ARG_INFO(0, arg3)
+ ZEND_ARG_INFO(0, group)
+ ZEND_ARG_INFO(0, id_or_consumer)
+ ZEND_ARG_INFO(0, mkstream)
+ ZEND_ARG_INFO(0, entries_read)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xinfo, 0, 0, 1)
@@ -971,6 +982,7 @@ ZEND_METHOD(RedisCluster, watch);
ZEND_METHOD(RedisCluster, xack);
ZEND_METHOD(RedisCluster, xadd);
ZEND_METHOD(RedisCluster, xclaim);
+ZEND_METHOD(RedisCluster, xautoclaim);
ZEND_METHOD(RedisCluster, xdel);
ZEND_METHOD(RedisCluster, xgroup);
ZEND_METHOD(RedisCluster, xinfo);
@@ -1180,6 +1192,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC)
+ ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC)
diff --git a/redis_commands.c b/redis_commands.c
index e75b1892..11c5d59f 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5776,10 +5776,8 @@ static int64_t get_xclaim_i64_arg(const char *key, zval *zv) {
/* Helper to extract XCLAIM options */
static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) {
- HashTable *ht;
zend_string *zkey;
- char *kval;
- size_t klen;
+ HashTable *ht;
zval *zv;
/* Initialize options array to sane defaults */
@@ -5795,29 +5793,20 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) {
ht = Z_ARRVAL_P(z_arr);
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, zv) {
if (zkey) {
- kval = ZSTR_VAL(zkey);
- klen = ZSTR_LEN(zkey);
-
- if (klen == 4) {
- if (!strncasecmp(kval, "TIME", 4)) {
- opt->idle.type = "TIME";
- opt->idle.time = get_xclaim_i64_arg("TIME", zv);
- } else if (!strncasecmp(kval, "IDLE", 4)) {
- opt->idle.type = "IDLE";
- opt->idle.time = get_xclaim_i64_arg("IDLE", zv);
- }
- } else if (klen == 10 && !strncasecmp(kval, "RETRYCOUNT", 10)) {
+ if (zend_string_equals_literal_ci(zkey, "TIME")) {
+ opt->idle.type = "TIME";
+ opt->idle.time = get_xclaim_i64_arg("TIME", zv);
+ } else if (zend_string_equals_literal_ci(zkey, "IDLE")) {
+ opt->idle.type = "IDLE";
+ opt->idle.time = get_xclaim_i64_arg("IDLE", zv);
+ } else if (zend_string_equals_literal_ci(zkey, "RETRYCOUNT")) {
opt->retrycount = zval_get_long(zv);
}
- } else {
- if (Z_TYPE_P(zv) == IS_STRING) {
- kval = Z_STRVAL_P(zv);
- klen = Z_STRLEN_P(zv);
- if (klen == 5 && !strncasecmp(kval, "FORCE", 5)) {
- opt->force = 1;
- } else if (klen == 6 && !strncasecmp(kval, "JUSTID", 6)) {
- opt->justid = 1;
- }
+ } else if (Z_TYPE_P(zv) == IS_STRING) {
+ if (zend_string_equals_literal_ci(Z_STR_P(zv), "FORCE")) {
+ opt->force = 1;
+ } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "JUSTID")) {
+ opt->justid = 1;
}
}
} ZEND_HASH_FOREACH_END();
@@ -5898,6 +5887,10 @@ redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "JUSTID");
}
+ // Set the context to distinguish XCLAIM from XAUTOCLAIM which
+ // have slightly different reply structures.
+ *ctx = PHPREDIS_CTX_PTR;
+
*cmd = cmdstr.c;
*cmd_len = cmdstr.len;
return SUCCESS;
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index 196ea90a..2e80e2f9 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: 42952974e3686f29934dfff1ebba07150942a405 */
+ * Stub hash: 1cd52318293e6a57df77b2d4a8f31ef126b6321d */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
ZEND_ARG_INFO(0, options)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index a88d8009..da93e867 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6821,6 +6821,7 @@ class Redis_Test extends TestSuite
for ($n = count($ids); $n >= 0; $n--) {
$xp = $this->redis->xPending('s', 'group');
+
$this->assertEquals($xp[0], count($ids));
/* Verify we're seeing the IDs themselves */
@@ -6975,6 +6976,36 @@ class Redis_Test extends TestSuite
}
}
+ /* Make sure our XAUTOCLAIM handler works */
+ public function testXAutoClaim() {
+ $this->redis->del('ships');
+ $this->redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
+
+ // Test an empty xautoclaim reply
+ $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
+ $this->assertEquals(['0-0', [], []], $res);
+
+ $this->redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
+
+ // Consume the ['name' => 'Defiant'] message
+ $this->redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
+
+ // The "Jem'Hadar" consumer has the message presently
+ $pending = $this->redis->xPending('ships', 'combatants');
+ $this->assertTrue($pending && isset($pending[3][0][0]) && $pending[3][0][0] == "Jem'Hadar");
+
+ // Asssume control of the pending message with a different consumer.
+ $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
+
+ $this->assertTrue($res && count($res) == 3 && $res[0] == '0-0' &&
+ isset($res[1]['1424-74205']['name']) &&
+ $res[1]['1424-74205']['name'] == 'Defiant');
+
+ // Now the 'Sisko' consumer should own the message
+ $pending = $this->redis->xPending('ships', 'combatants');
+ $this->assertTrue(isset($pending[3][0][0]) && $pending[3][0][0] == 'Sisko');
+ }
+
public function testXInfo()
{
if (!$this->minVersionCheck("5.0"))