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

Skip to content

Commit 3c9e159

Browse files
committed
Refactor subscribe/unsubscribe
1 parent aaa4c91 commit 3c9e159

8 files changed

Lines changed: 135 additions & 65 deletions

File tree

cluster_library.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,8 +1857,8 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
18571857

18581858
// Set up our callback pointers
18591859
zval z_ret, z_args[4];
1860-
sctx->cb.retval = &z_ret;
1861-
sctx->cb.params = z_args;
1860+
sctx->cb.fci.retval = &z_ret;
1861+
sctx->cb.fci.params = z_args;
18621862

18631863
/* We're in a subscribe loop */
18641864
c->subscribed_slot = c->cmd_slot;
@@ -1911,12 +1911,10 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
19111911
}
19121912

19131913
// Set arg count
1914-
sctx->cb.param_count = tab_idx;
1914+
sctx->cb.fci.param_count = tab_idx;
19151915

19161916
// Execute our callback
1917-
if (zend_call_function(&(sctx->cb), &(sctx->cb_cache)) !=
1918-
SUCCESS)
1919-
{
1917+
if (zend_call_function(&sctx->cb.fci, &sctx->cb.fci_cache) != SUCCESS) {
19201918
break;
19211919
}
19221920

common.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ typedef struct {
289289
int persistent;
290290
int watching;
291291
zend_string *persistent_id;
292-
292+
HashTable *subs;
293293
redis_serializer serializer;
294294
int compression;
295295
int compression_level;

library.c

Lines changed: 97 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -438,58 +438,87 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
438438
}
439439
}
440440

441+
static void
442+
ht_free_subs(zval *data)
443+
{
444+
efree(Z_PTR_P(data));
445+
}
446+
441447
PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
442448
RedisSock *redis_sock, zval *z_tab,
443449
void *ctx)
444450
{
451+
HashTable *subs;
452+
subscribeCallback *cb;
445453
subscribeContext *sctx = (subscribeContext*)ctx;
446454
zval *z_tmp, z_resp;
447455

456+
ALLOC_HASHTABLE(subs);
457+
zend_hash_init(subs, 0, NULL, ht_free_subs, 0);
448458
// Consume response(s) from subscribe, which will vary on argc
449459
while(sctx->argc--) {
460+
ZVAL_NULL(&z_resp);
450461
if (!redis_sock_read_multibulk_reply_zval(
451462
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)
452463
) {
453-
efree(sctx);
454-
return -1;
464+
goto error;
455465
}
456466

457467
// We'll need to find the command response
458468
if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 0)) == NULL) {
459-
zval_dtor(&z_resp);
460-
efree(sctx);
461-
return -1;
469+
goto error;
462470
}
463471

464472
// Make sure the command response matches the command we called
465473
if(strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) !=0) {
466-
zval_dtor(&z_resp);
467-
efree(sctx);
468-
return -1;
474+
goto error;
469475
}
470476

477+
if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL) {
478+
goto error;
479+
}
480+
481+
zend_hash_str_update_mem(subs, Z_STRVAL_P(z_tmp), Z_STRLEN_P(z_tmp),
482+
&sctx->cb, sizeof(sctx->cb));
483+
471484
zval_dtor(&z_resp);
472485
}
473486

474-
zval z_ret, z_args[4];
475-
sctx->cb.retval = &z_ret;
476-
sctx->cb.params = z_args;
487+
efree(sctx);
488+
489+
if (redis_sock->subs) {
490+
zend_string *zkey;
491+
492+
ZEND_HASH_FOREACH_STR_KEY_PTR(subs, zkey, cb) {
493+
zend_hash_update_mem(redis_sock->subs, zkey, cb, sizeof(*cb));
494+
} ZEND_HASH_FOREACH_END();
495+
zend_hash_destroy(subs);
496+
efree(subs);
497+
498+
RETVAL_TRUE;
499+
return SUCCESS;
500+
}
477501

502+
redis_sock->subs = subs;
478503
/* Multibulk response, {[pattern], type, channel, payload } */
479-
while(1) {
480-
zval *z_type, *z_chan, *z_pat = NULL, *z_data;
504+
while (redis_sock->subs) {
505+
zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data;
481506
HashTable *ht_tab;
482-
int tab_idx=1, is_pmsg;
507+
int tab_idx = 1, is_pmsg = 0;
483508

509+
ZVAL_NULL(&z_resp);
484510
if (!redis_sock_read_multibulk_reply_zval(
485-
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break;
511+
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)
512+
) {
513+
goto failure;
514+
}
486515

487516
ht_tab = Z_ARRVAL(z_resp);
488517

489518
if ((z_type = zend_hash_index_find(ht_tab, 0)) == NULL ||
490519
Z_TYPE_P(z_type) != IS_STRING
491520
) {
492-
break;
521+
goto failure;
493522
}
494523

495524
// Check for message or pmessage
@@ -498,21 +527,26 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
498527
{
499528
is_pmsg = *Z_STRVAL_P(z_type)=='p';
500529
} else {
501-
break;
530+
zval_dtor(&z_resp);
531+
continue;
502532
}
503533

504534
// Extract pattern if it's a pmessage
505535
if(is_pmsg) {
506536
if ((z_pat = zend_hash_index_find(ht_tab, tab_idx++)) == NULL) {
507-
break;
537+
goto failure;
508538
}
509539
}
510540

511541
// Extract channel and data
512542
if ((z_chan = zend_hash_index_find(ht_tab, tab_idx++)) == NULL ||
513543
(z_data = zend_hash_index_find(ht_tab, tab_idx++)) == NULL
514544
) {
515-
break;
545+
goto failure;
546+
}
547+
548+
if ((cb = zend_hash_str_find_ptr(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) {
549+
goto failure;
516550
}
517551

518552
// Different args for SUBSCRIBE and PSUBSCRIBE
@@ -527,57 +561,78 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS,
527561
}
528562

529563
// Set arg count
530-
sctx->cb.param_count = tab_idx;
564+
cb->fci.param_count = tab_idx;
565+
cb->fci.retval = &z_ret;
566+
cb->fci.params = z_args;
531567

532568
// Execute callback
533-
if(zend_call_function(&(sctx->cb), &(sctx->cb_cache))
534-
==FAILURE)
535-
{
536-
break;
569+
if (zend_call_function(&cb->fci, &cb->fci_cache) != SUCCESS) {
570+
goto failure;
537571
}
538572

539573
// If we have a return value free it
540574
zval_ptr_dtor(&z_ret);
541575
zval_dtor(&z_resp);
542576
}
543577

578+
RETVAL_TRUE;
579+
return SUCCESS;
580+
544581
// This is an error state, clean up
545-
zval_dtor(&z_resp);
582+
error:
546583
efree(sctx);
547-
548-
return -1;
584+
zend_hash_destroy(subs);
585+
efree(subs);
586+
failure:
587+
zval_dtor(&z_resp);
588+
RETVAL_FALSE;
589+
return FAILURE;
549590
}
550591

551592
PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS,
552593
RedisSock *redis_sock, zval *z_tab,
553594
void *ctx)
554595
{
555596
subscribeContext *sctx = (subscribeContext*)ctx;
556-
zval *z_chan, zv, *z_ret = &zv, z_resp;
557-
int i;
597+
zval *z_chan, z_ret, z_resp;
558598

559-
array_init(z_ret);
599+
array_init(&z_ret);
560600

561-
for (i = 0; i < sctx->argc; i++) {
601+
while (sctx->argc--) {
602+
ZVAL_NULL(&z_resp);
562603
if (!redis_sock_read_multibulk_reply_zval(
563604
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp) ||
564605
(z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL
565606
) {
566-
zval_dtor(z_ret);
567-
return -1;
607+
efree(sctx);
608+
zval_dtor(&z_resp);
609+
zval_dtor(&z_ret);
610+
RETVAL_FALSE;
611+
return FAILURE;
568612
}
569613

570-
add_assoc_bool(z_ret, Z_STRVAL_P(z_chan), 1);
614+
if (!redis_sock->subs ||
615+
!zend_hash_str_exists(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))
616+
) {
617+
add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 0);
618+
} else {
619+
zend_hash_str_del(redis_sock->subs, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan));
620+
add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 1);
621+
}
571622

572623
zval_dtor(&z_resp);
573624
}
574625

575626
efree(sctx);
576627

577-
RETVAL_ZVAL(z_ret, 0, 1);
628+
if (redis_sock->subs && !zend_hash_num_elements(redis_sock->subs)) {
629+
zend_hash_destroy(redis_sock->subs);
630+
efree(redis_sock->subs);
631+
redis_sock->subs = NULL;
632+
}
578633

579-
// Success
580-
return 0;
634+
RETVAL_ZVAL(&z_ret, 0, 1);
635+
return SUCCESS;
581636
}
582637

583638
PHP_REDIS_API zval *
@@ -2870,6 +2925,11 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock)
28702925
if (redis_sock->host) {
28712926
zend_string_release(redis_sock->host);
28722927
}
2928+
if (redis_sock->subs) {
2929+
zend_hash_destroy(redis_sock->subs);
2930+
efree(redis_sock->subs);
2931+
redis_sock->subs = NULL;
2932+
}
28732933
redis_sock_free_auth(redis_sock);
28742934
free_reply_callbacks(redis_sock);
28752935
efree(redis_sock);

redis.stub.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,15 +319,15 @@ public function popen(string $host, int $port = 6379, float $timeout = 0, string
319319
/** @return bool|Redis */
320320
public function psetex(string $key, int $expire, mixed $value);
321321

322-
public function psubscribe(array $patterns): void;
322+
public function psubscribe(array $patterns, callable $cb): bool;
323323

324324
public function pttl(string $key): int;
325325

326326
public function publish(string $channel, string $message): int;
327327

328328
public function pubsub(string $command, mixed $arg = null): mixed;
329329

330-
public function punsubscribe(array $patterns): array;
330+
public function punsubscribe(array $patterns): bool|array;
331331

332332
public function rPop(string $key, int $count = 0): bool|string|array;
333333

@@ -439,7 +439,7 @@ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int
439439
/** @return int|Redis */
440440
public function strlen(string $key);
441441

442-
public function subscribe(string $channel, string ...$other_channels): array;
442+
public function subscribe(array $channels, callable $cb): bool;
443443

444444
public function swapdb(string $src, string $dst): bool;
445445

@@ -455,7 +455,7 @@ public function type(string $key);
455455
*/
456456
public function unlink(array|string $key, string ...$other_keys);
457457

458-
public function unsubscribe(string $channel, string ...$other_channels): array;
458+
public function unsubscribe(array $channels): bool|array;
459459

460460
/** @return bool|Redis */
461461
public function unwatch();

redis_arginfo.h

Lines changed: 10 additions & 7 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: 9671c30926e8d581a126833360b123c8ae2dd913 */
2+
* Stub hash: efcda1ed028d65d0b4848d32133dc0e32f17871f */
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")
@@ -541,8 +541,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_psetex, 0, 0, 3)
541541
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
542542
ZEND_END_ARG_INFO()
543543

544-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_psubscribe, 0, 1, IS_VOID, 0)
544+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_psubscribe, 0, 2, _IS_BOOL, 0)
545545
ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
546+
ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
546547
ZEND_END_ARG_INFO()
547548

548549
#define arginfo_class_Redis_pttl arginfo_class_Redis_hLen
@@ -557,7 +558,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pubsub, 0, 1, IS_MIX
557558
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_MIXED, 0, "null")
558559
ZEND_END_ARG_INFO()
559560

560-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_punsubscribe, 0, 1, IS_ARRAY, 0)
561+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_punsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
561562
ZEND_ARG_TYPE_INFO(0, patterns, IS_ARRAY, 0)
562563
ZEND_END_ARG_INFO()
563564

@@ -732,9 +733,9 @@ ZEND_END_ARG_INFO()
732733

733734
#define arginfo_class_Redis_strlen arginfo_class_Redis_decr
734735

735-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 1, IS_ARRAY, 0)
736-
ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0)
737-
ZEND_ARG_VARIADIC_TYPE_INFO(0, other_channels, IS_STRING, 0)
736+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_subscribe, 0, 2, _IS_BOOL, 0)
737+
ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
738+
ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0)
738739
ZEND_END_ARG_INFO()
739740

740741
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_swapdb, 0, 2, _IS_BOOL, 0)
@@ -750,7 +751,9 @@ ZEND_END_ARG_INFO()
750751

751752
#define arginfo_class_Redis_unlink arginfo_class_Redis_del
752753

753-
#define arginfo_class_Redis_unsubscribe arginfo_class_Redis_subscribe
754+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_unsubscribe, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
755+
ZEND_ARG_TYPE_INFO(0, channels, IS_ARRAY, 0)
756+
ZEND_END_ARG_INFO()
754757

755758
#define arginfo_class_Redis_unwatch arginfo_class_Redis___destruct
756759

redis_commands.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
11011101
char *key;
11021102

11031103
if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &z_arr,
1104-
&(sctx->cb), &(sctx->cb_cache)) == FAILURE)
1104+
&sctx->cb.fci, &sctx->cb.fci_cache) == FAILURE)
11051105
{
11061106
efree(sctx);
11071107
return FAILURE;

redis_commands.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@
1414
if (slot) *slot = cluster_hash_key(key,key_len);
1515

1616
/* Simple container so we can push subscribe context out */
17+
typedef struct {
18+
zend_fcall_info fci;
19+
zend_fcall_info_cache fci_cache;
20+
} subscribeCallback;
21+
1722
typedef struct subscribeContext {
1823
char *kw;
1924
int argc;
20-
zend_fcall_info cb;
21-
zend_fcall_info_cache cb_cache;
25+
subscribeCallback cb;
2226
} subscribeContext;
2327

2428
/* Construct a raw command */

0 commit comments

Comments
 (0)