@@ -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+
441447PHP_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
551592PHP_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
583638PHP_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 );
0 commit comments