Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7644736

Browse files
committed
Issue #2068, ssubscribe/sunsubscribe
1 parent 7a4cee2 commit 7644736

7 files changed

Lines changed: 180 additions & 42 deletions

File tree

common.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ typedef enum _PUBSUB_TYPE {
8484
PUBSUB_NUMPAT
8585
} PUBSUB_TYPE;
8686

87+
#define REDIS_SUBSCRIBE_IDX 0
88+
#define REDIS_PSUBSCRIBE_IDX 1
89+
#define REDIS_SSUBSCRIBE_IDX 2
90+
#define REDIS_SUBS_BUCKETS 3
91+
8792
/* options */
8893
#define REDIS_OPT_SERIALIZER 1
8994
#define REDIS_OPT_PREFIX 2
@@ -300,7 +305,7 @@ typedef struct {
300305
int persistent;
301306
int watching;
302307
zend_string *persistent_id;
303-
HashTable *subs;
308+
HashTable *subs[REDIS_SUBS_BUCKETS];
304309
redis_serializer serializer;
305310
int compression;
306311
int compression_level;

library.c

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
485485
subscribeCallback *cb;
486486
subscribeContext *sctx = (subscribeContext*)ctx;
487487
zval *z_tmp, z_resp;
488+
int i;
488489

489490
ALLOC_HASHTABLE(subs);
490491
zend_hash_init(subs, 0, NULL, ht_free_subs, 0);
@@ -515,13 +516,21 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
515516
zval_dtor(&z_resp);
516517
}
517518

519+
if (strcasecmp(sctx->kw, "ssubscribe") == 0) {
520+
i = REDIS_SSUBSCRIBE_IDX;
521+
} else if (strcasecmp(sctx->kw, "psubscribe") == 0) {
522+
i = REDIS_PSUBSCRIBE_IDX;
523+
} else {
524+
i = REDIS_SUBSCRIBE_IDX;
525+
}
526+
518527
efree(sctx);
519528

520-
if (redis_sock->subs) {
529+
if (redis_sock->subs[i]) {
521530
zend_string *zkey;
522531

523532
ZEND_HASH_FOREACH_STR_KEY_PTR(subs, zkey, cb) {
524-
zend_hash_update_mem(redis_sock->subs, zkey, cb, sizeof(*cb));
533+
zend_hash_update_mem(redis_sock->subs[i], zkey, cb, sizeof(*cb));
525534
} ZEND_HASH_FOREACH_END();
526535
zend_hash_destroy(subs);
527536
efree(subs);
@@ -530,9 +539,9 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
530539
return SUCCESS;
531540
}
532541

533-
redis_sock->subs = subs;
542+
redis_sock->subs[i] = subs;
534543
/* Multibulk response, {[pattern], type, channel, payload } */
535-
while (redis_sock->subs) {
544+
while (redis_sock->subs[i]) {
536545
zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data;
537546
HashTable *ht_tab;
538547
int tab_idx = 1, is_pmsg = 0;
@@ -551,9 +560,10 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
551560
}
552561

553562
// Check for message or pmessage
554-
if(!strncmp(Z_STRVAL_P(z_type), "message", 7) ||
555-
!strncmp(Z_STRVAL_P(z_type), "pmessage", 8))
556-
{
563+
if (zend_string_equals_literal_ci(Z_STR_P(z_type), "message") ||
564+
zend_string_equals_literal_ci(Z_STR_P(z_type), "pmessage") ||
565+
zend_string_equals_literal_ci(Z_STR_P(z_type), "smessage")
566+
) {
557567
is_pmsg = *Z_STRVAL_P(z_type)=='p';
558568
} else {
559569
zval_dtor(&z_resp);
@@ -574,7 +584,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
574584
goto failure;
575585
}
576586

577-
if ((cb = zend_hash_str_find_ptr(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) {
587+
if ((cb = zend_hash_str_find_ptr(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) {
578588
goto failure;
579589
}
580590

@@ -624,6 +634,18 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
624634
{
625635
subscribeContext *sctx = (subscribeContext*)ctx;
626636
zval *z_chan, z_ret, z_resp;
637+
int i;
638+
639+
if (strcasecmp(sctx->kw, "sunsubscribe") == 0) {
640+
i = REDIS_SSUBSCRIBE_IDX;
641+
} else if (strcasecmp(sctx->kw, "punsubscribe") == 0) {
642+
i = REDIS_PSUBSCRIBE_IDX;
643+
} else {
644+
i = REDIS_SUBSCRIBE_IDX;
645+
}
646+
if (!sctx->argc && redis_sock->subs[i]) {
647+
sctx->argc = zend_hash_num_elements(redis_sock->subs[i]);
648+
}
627649

628650
array_init(&z_ret);
629651

@@ -639,12 +661,12 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
639661
return FAILURE;
640662
}
641663

642-
if (!redis_sock->subs ||
643-
!zend_hash_str_exists(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))
664+
if (!redis_sock->subs[i] ||
665+
!zend_hash_str_exists(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))
644666
) {
645667
add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 0);
646668
} else {
647-
zend_hash_str_del(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan));
669+
zend_hash_str_del(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan));
648670
add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 1);
649671
}
650672

@@ -653,10 +675,10 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
653675

654676
efree(sctx);
655677

656-
if (redis_sock->subs && !zend_hash_num_elements(redis_sock->subs)) {
657-
zend_hash_destroy(redis_sock->subs);
658-
efree(redis_sock->subs);
659-
redis_sock->subs = NULL;
678+
if (redis_sock->subs[i] && !zend_hash_num_elements(redis_sock->subs[i])) {
679+
zend_hash_destroy(redis_sock->subs[i]);
680+
efree(redis_sock->subs[i]);
681+
redis_sock->subs[i] = NULL;
660682
}
661683

662684
RETVAL_ZVAL(&z_ret, 0, 1);
@@ -3300,6 +3322,8 @@ free_reply_callbacks(RedisSock *redis_sock)
33003322
*/
33013323
PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
33023324
{
3325+
int i;
3326+
33033327
if (redis_sock->prefix) {
33043328
zend_string_release(redis_sock->prefix);
33053329
}
@@ -3315,10 +3339,12 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
33153339
if (redis_sock->host) {
33163340
zend_string_release(redis_sock->host);
33173341
}
3318-
if (redis_sock->subs) {
3319-
zend_hash_destroy(redis_sock->subs);
3320-
efree(redis_sock->subs);
3321-
redis_sock->subs = NULL;
3342+
for (i = 0; i < REDIS_SUBS_BUCKETS; ++i) {
3343+
if (redis_sock->subs[i]) {
3344+
zend_hash_destroy(redis_sock->subs[i]);
3345+
efree(redis_sock->subs[i]);
3346+
redis_sock->subs[i] = NULL;
3347+
}
33223348
}
33233349
redis_sock_free_auth(redis_sock);
33243350
free_reply_callbacks(redis_sock);

redis.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,15 @@ PHP_METHOD(Redis, psubscribe)
23762376
REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd,
23772377
redis_subscribe_response);
23782378
}
2379+
/* }}} */
2380+
2381+
/* {{{ proto void Redis::ssubscribe(Array(shardchannel1, shardchannel2, ... shardchannelN)) */
2382+
PHP_METHOD(Redis, ssubscribe)
2383+
{
2384+
REDIS_PROCESS_KW_CMD("SSUBSCRIBE", redis_subscribe_cmd,
2385+
redis_subscribe_response);
2386+
}
2387+
/* }}} */
23792388

23802389
/* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */
23812390
PHP_METHOD(Redis, subscribe) {
@@ -2384,8 +2393,8 @@ PHP_METHOD(Redis, subscribe) {
23842393
}
23852394

23862395
/**
2387-
* [p]unsubscribe channel_0 channel_1 ... channel_n
2388-
* [p]unsubscribe(array(channel_0, channel_1, ..., channel_n))
2396+
* [ps]unsubscribe channel_0 channel_1 ... channel_n
2397+
* [ps]unsubscribe(array(channel_0, channel_1, ..., channel_n))
23892398
* response format :
23902399
* array(
23912400
* channel_0 => TRUE|FALSE,
@@ -2407,6 +2416,12 @@ PHP_METHOD(Redis, punsubscribe)
24072416
redis_unsubscribe_response);
24082417
}
24092418

2419+
PHP_METHOD(Redis, sunsubscribe)
2420+
{
2421+
REDIS_PROCESS_KW_CMD("SUNSUBSCRIBE", redis_unsubscribe_cmd,
2422+
redis_unsubscribe_response);
2423+
}
2424+
24102425
/* {{{ proto string Redis::bgrewriteaof() */
24112426
PHP_METHOD(Redis, bgrewriteaof)
24122427
{

redis.stub.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,6 +3235,37 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i
32353235
*/
32363236
public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false;
32373237

3238+
/**
3239+
* Subscribes the client to the specified shard channels.
3240+
*
3241+
* @param array $channels One or more channel names.
3242+
* @param callable $cb The callback PhpRedis will invoke when we receive a message
3243+
* from one of the subscribed channels.
3244+
*
3245+
* @return bool True on success, false on faiilure. Note that this command will block the
3246+
* client in a subscribe loop, waiting for messages to arrive.
3247+
*
3248+
* @see https://redis.io/commands/ssubscribe
3249+
*
3250+
* @example
3251+
* $redis = new Redis(['host' => 'localhost']);
3252+
*
3253+
* $redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
3254+
* echo "[$channel]: $message\n";
3255+
*
3256+
* // Unsubscribe from the message channel when we read 'quit'
3257+
* if ($message == 'quit') {
3258+
* echo "Unsubscribing from '$channel'\n";
3259+
* $redis->sunsubscribe([$channel]);
3260+
* }
3261+
* });
3262+
*
3263+
* // Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
3264+
* // broken and this command will execute.
3265+
* echo "Subscribe loop ended\n";
3266+
*/
3267+
public function ssubscribe(array $channels, callable $cb): bool;
3268+
32383269
/**
32393270
* Retrieve the length of a Redis STRING key.
32403271
*
@@ -3280,6 +3311,30 @@ public function strlen(string $key): Redis|int|false;
32803311
*/
32813312
public function subscribe(array $channels, callable $cb): bool;
32823313

3314+
/**
3315+
* Unsubscribes the client from the given shard channels,
3316+
* or from all of them if none is given.
3317+
*
3318+
* @param array $channels One or more channels to unsubscribe from.
3319+
* @return Redis|array|bool The array of unsubscribed channels.
3320+
*
3321+
* @see https://redis.io/commands/sunsubscribe
3322+
* @see Redis::ssubscribe()
3323+
*
3324+
* @example
3325+
* $redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
3326+
* if ($message == 'quit') {
3327+
* echo "$channel => 'quit' detected, unsubscribing!\n";
3328+
* $redis->sunsubscribe([$channel]);
3329+
* } else {
3330+
* echo "$channel => $message\n";
3331+
* }
3332+
* });
3333+
*
3334+
* echo "We've unsubscribed from both channels, exiting\n";
3335+
*/
3336+
public function sunsubscribe(array $channels): Redis|array|bool;
3337+
32833338
/**
32843339
* Atomically swap two Redis databases so that all of the keys in the source database will
32853340
* now be in the destination database and vice-versa.

redis_arginfo.h

Lines changed: 14 additions & 6 deletions
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: 2f941423f250241850fc678282e0656ecfb44018 */
2+
* Stub hash: a22731395ec32eed913fde7b2de60758fb1e75da */
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")
@@ -836,11 +836,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_
836836
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0")
837837
ZEND_END_ARG_INFO()
838838

839+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_ssubscribe, 0, 2, _IS_BOOL, 0)
840+
ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
841+
ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
842+
ZEND_END_ARG_INFO()
843+
839844
#define arginfo_class_Redis_strlen arginfo_class_Redis_expiretime
840845

841-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS_BOOL, 0)
846+
#define arginfo_class_Redis_subscribe arginfo_class_Redis_ssubscribe
847+
848+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sunsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
842849
ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
843-
ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
844850
ZEND_END_ARG_INFO()
845851

846852
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_swapdb, 0, 2, Redis, MAY_BE_BOOL)
@@ -857,9 +863,7 @@ ZEND_END_ARG_INFO()
857863

858864
#define arginfo_class_Redis_unlink arginfo_class_Redis_del
859865

860-
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_BOOL)
861-
ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
862-
ZEND_END_ARG_INFO()
866+
#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_sunsubscribe
863867

864868
#define arginfo_class_Redis_unwatch arginfo_class_Redis_bgSave
865869

@@ -1327,8 +1331,10 @@ ZEND_METHOD(Redis, sortDesc);
13271331
ZEND_METHOD(Redis, sortDescAlpha);
13281332
ZEND_METHOD(Redis, srem);
13291333
ZEND_METHOD(Redis, sscan);
1334+
ZEND_METHOD(Redis, ssubscribe);
13301335
ZEND_METHOD(Redis, strlen);
13311336
ZEND_METHOD(Redis, subscribe);
1337+
ZEND_METHOD(Redis, sunsubscribe);
13321338
ZEND_METHOD(Redis, swapdb);
13331339
ZEND_METHOD(Redis, time);
13341340
ZEND_METHOD(Redis, ttl);
@@ -1578,8 +1584,10 @@ static const zend_function_entry class_Redis_methods[] = {
15781584
ZEND_ME(Redis, sortDescAlpha, arginfo_class_Redis_sortDescAlpha, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
15791585
ZEND_ME(Redis, srem, arginfo_class_Redis_srem, ZEND_ACC_PUBLIC)
15801586
ZEND_ME(Redis, sscan, arginfo_class_Redis_sscan, ZEND_ACC_PUBLIC)
1587+
ZEND_ME(Redis, ssubscribe, arginfo_class_Redis_ssubscribe, ZEND_ACC_PUBLIC)
15811588
ZEND_ME(Redis, strlen, arginfo_class_Redis_strlen, ZEND_ACC_PUBLIC)
15821589
ZEND_ME(Redis, subscribe, arginfo_class_Redis_subscribe, ZEND_ACC_PUBLIC)
1590+
ZEND_ME(Redis, sunsubscribe, arginfo_class_Redis_sunsubscribe, ZEND_ACC_PUBLIC)
15831591
ZEND_ME(Redis, swapdb, arginfo_class_Redis_swapdb, ZEND_ACC_PUBLIC)
15841592
ZEND_ME(Redis, time, arginfo_class_Redis_time, ZEND_ACC_PUBLIC)
15851593
ZEND_ME(Redis, ttl, arginfo_class_Redis_ttl, ZEND_ACC_PUBLIC)

0 commit comments

Comments
 (0)