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

Skip to content

Commit 6c37fa3

Browse files
Merge branch 'feature/pubsub_cmd' into develop
2 parents c81a954 + 48ae8e5 commit 6c37fa3

File tree

5 files changed

+235
-1
lines changed

5 files changed

+235
-1
lines changed

README.markdown

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,6 +2864,7 @@ while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) {
28642864
* [psubscribe](#psubscribe) - Subscribe to channels by pattern
28652865
* [publish](#publish) - Post a message to a channel
28662866
* [subscribe](#subscribe) - Subscribe to channels
2867+
* [pubsub](#pubsub) - Introspection into the pub/sub subsystem
28672868

28682869
### psubscribe
28692870
-----
@@ -2924,6 +2925,26 @@ function f($redis, $chan, $msg) {
29242925
$redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
29252926
~~~
29262927

2928+
### pubsub
2929+
-----
2930+
_**Description**_: A command allowing you to get information on the Redis pub/sub system.
2931+
2932+
##### *Parameters*
2933+
*keyword*: String, which can be: "channels", "numsub", or "numpat"
2934+
*argument*: Optional, variant. For the "channels" subcommand, you can pass a string pattern. For "numsub" an array of channel names.
2935+
2936+
##### *Return value*
2937+
*CHANNELS*: Returns an array where the members are the matching channels.
2938+
*NUMSUB*: Returns a key/value array where the keys are channel names and values are their counts.
2939+
*NUMPAT*: Integer return containing the number active pattern subscriptions
2940+
2941+
##### *Example*
2942+
~~~
2943+
$redis->pubsub("channels"); /*All channels */
2944+
$redis->pubsub("channels", "*pattern*"); /* Just channels matching your pattern */
2945+
$redis->pubsub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/
2946+
$redsi->pubsub("numpat"); /* Get the number of pattern subscribers */
2947+
```
29272948
29282949
## Transactions
29292950

common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ typedef enum _REDIS_SCAN_TYPE {
4545
TYPE_ZSCAN
4646
} REDIS_SCAN_TYPE;
4747

48+
/* PUBSUB subcommands */
49+
typedef enum _PUBSUB_TYPE {
50+
PUBSUB_CHANNELS,
51+
PUBSUB_NUMSUB,
52+
PUBSUB_NUMPAT
53+
} PUBSUB_TYPE;
54+
4855
/* options */
4956
#define REDIS_OPT_SERIALIZER 1
5057
#define REDIS_OPT_PREFIX 2

php_redis.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ PHP_METHOD(Redis, setOption);
185185
PHP_METHOD(Redis, config);
186186
PHP_METHOD(Redis, slowlog);
187187
PHP_METHOD(Redis, wait);
188+
PHP_METHOD(Redis, pubsub);
188189

189190
PHP_METHOD(Redis, client);
190191

redis.c

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ static zend_function_entry redis_functions[] = {
278278
PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC)
279279

280280
PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC)
281+
PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC)
281282

282283
/* aliases */
283284
PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC)
@@ -6132,6 +6133,169 @@ PHP_METHOD(Redis, wait) {
61326133
REDIS_PROCESS_RESPONSE(redis_long_response);
61336134
}
61346135

6136+
/*
6137+
* Construct a PUBSUB command
6138+
*/
6139+
PHPAPI int
6140+
redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
6141+
zval *arg TSRMLS_CC)
6142+
{
6143+
HashTable *ht_chan;
6144+
HashPosition ptr;
6145+
zval **z_ele;
6146+
char *key;
6147+
int cmd_len, key_len, key_free;
6148+
smart_str cmd = {0};
6149+
6150+
if(type == PUBSUB_CHANNELS) {
6151+
if(arg) {
6152+
// Get string argument and length.
6153+
key = Z_STRVAL_P(arg);
6154+
key_len = Z_STRLEN_P(arg);
6155+
6156+
// Prefix if necissary
6157+
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
6158+
6159+
// With a pattern
6160+
cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", "CHANNELS", sizeof("CHANNELS")-1,
6161+
key, key_len);
6162+
6163+
// Free the channel name if we prefixed it
6164+
if(key_free) efree(key);
6165+
6166+
// Return command length
6167+
return cmd_len;
6168+
} else {
6169+
// No pattern
6170+
return redis_cmd_format_static(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS")-1);
6171+
}
6172+
} else if(type == PUBSUB_NUMSUB) {
6173+
ht_chan = Z_ARRVAL_P(arg);
6174+
6175+
// Add PUBSUB and NUMSUB bits
6176+
redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
6177+
redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
6178+
6179+
// Iterate our elements
6180+
for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr);
6181+
zend_hash_get_current_data_ex(ht_chan, (void**)&z_ele, &ptr)==SUCCESS;
6182+
zend_hash_move_forward_ex(ht_chan, &ptr))
6183+
{
6184+
char *key;
6185+
int key_len, key_free;
6186+
zval *z_tmp = NULL;
6187+
6188+
if(Z_TYPE_PP(z_ele) == IS_STRING) {
6189+
key = Z_STRVAL_PP(z_ele);
6190+
key_len = Z_STRLEN_PP(z_ele);
6191+
} else {
6192+
MAKE_STD_ZVAL(z_tmp);
6193+
*z_tmp = **z_ele;
6194+
zval_copy_ctor(z_tmp);
6195+
convert_to_string(z_tmp);
6196+
6197+
key = Z_STRVAL_P(z_tmp);
6198+
key_len = Z_STRLEN_P(z_tmp);
6199+
}
6200+
6201+
// Apply prefix if required
6202+
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
6203+
6204+
// Append this channel
6205+
redis_cmd_append_sstr(&cmd, key, key_len);
6206+
6207+
// Free key if prefixed
6208+
if(key_free) efree(key);
6209+
6210+
// Free our temp var if we converted from something other than a string
6211+
if(z_tmp) {
6212+
zval_dtor(z_tmp);
6213+
efree(z_tmp);
6214+
z_tmp = NULL;
6215+
}
6216+
}
6217+
6218+
// Set return
6219+
*ret = cmd.c;
6220+
return cmd.len;
6221+
} else if(type == PUBSUB_NUMPAT) {
6222+
return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT")-1);
6223+
}
6224+
6225+
// Shouldn't ever happen
6226+
return -1;
6227+
}
6228+
6229+
/*
6230+
* {{{ proto Redis::pubsub("channels", pattern);
6231+
* proto Redis::pubsub("numsub", Array channels);
6232+
* proto Redis::pubsub("numpat"); }}}
6233+
*/
6234+
PHP_METHOD(Redis, pubsub) {
6235+
zval *object;
6236+
RedisSock *redis_sock;
6237+
char *keyword, *cmd;
6238+
int kw_len, cmd_len;
6239+
PUBSUB_TYPE type;
6240+
zval *arg=NULL;
6241+
6242+
// Parse arguments
6243+
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z",
6244+
&object, redis_ce, &keyword, &kw_len, &arg)
6245+
==FAILURE)
6246+
{
6247+
RETURN_FALSE;
6248+
}
6249+
6250+
// Validate our sub command keyword, and that we've got proper arguments
6251+
if(!strncasecmp(keyword, "channels", sizeof("channels"))) {
6252+
// One (optional) string argument
6253+
if(arg && Z_TYPE_P(arg) != IS_STRING) {
6254+
RETURN_FALSE;
6255+
}
6256+
type = PUBSUB_CHANNELS;
6257+
} else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
6258+
// One array argument
6259+
if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
6260+
zend_hash_num_elements(Z_ARRVAL_P(arg))==0)
6261+
{
6262+
RETURN_FALSE;
6263+
}
6264+
type = PUBSUB_NUMSUB;
6265+
} else if(!strncasecmp(keyword, "numpat", sizeof("numpat"))) {
6266+
type = PUBSUB_NUMPAT;
6267+
} else {
6268+
// Invalid keyword
6269+
RETURN_FALSE;
6270+
}
6271+
6272+
// Grab our socket context object
6273+
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
6274+
RETURN_FALSE;
6275+
}
6276+
6277+
// Construct our "PUBSUB" command
6278+
cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg TSRMLS_CC);
6279+
6280+
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
6281+
6282+
if(type == PUBSUB_NUMSUB) {
6283+
IF_ATOMIC() {
6284+
if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) {
6285+
RETURN_FALSE;
6286+
}
6287+
}
6288+
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
6289+
} else {
6290+
IF_ATOMIC() {
6291+
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL)<0) {
6292+
RETURN_FALSE;
6293+
}
6294+
}
6295+
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
6296+
}
6297+
}
6298+
61356299
// Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
61366300
PHPAPI int
61376301
redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) {

tests/TestRedis.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,48 @@ public function testPipelinePublish() {
7070
->exec();
7171

7272
$this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0);
73-
}
73+
}
74+
75+
// Run some simple tests against the PUBSUB command. This is problematic, as we
76+
// can't be sure what's going on in the instance, but we can do some things.
77+
public function testPubSub() {
78+
// Only available since 2.8.0
79+
if(version_compare($this->version, "2.8.0", "lt")) {
80+
$this->markTestSkipped();
81+
return;
82+
}
83+
84+
// PUBSUB CHANNELS ...
85+
$result = $this->redis->pubsub("channels", "*");
86+
$this->assertTrue(is_array($result));
87+
$result = $this->redis->pubsub("channels");
88+
$this->assertTrue(is_array($result));
89+
90+
// PUBSUB NUMSUB
91+
92+
$c1 = uniqid() . '-' . rand(1,100);
93+
$c2 = uniqid() . '-' . rand(1,100);
94+
95+
$result = $this->redis->pubsub("numsub", Array($c1, $c2));
96+
97+
// Should get an array back, with two elements
98+
$this->assertTrue(is_array($result));
99+
$this->assertEquals(count($result), 2);
100+
101+
// Make sure the elements are correct, and have zero counts
102+
foreach(Array($c1,$c2) as $channel) {
103+
$this->assertTrue(isset($result[$channel]));
104+
$this->assertEquals($result[$channel], "0");
105+
}
106+
107+
// PUBSUB NUMPAT
108+
$result = $this->redis->pubsub("numpat");
109+
$this->assertTrue(is_int($result));
110+
111+
// Invalid calls
112+
$this->assertFalse($this->redis->pubsub("notacommand"));
113+
$this->assertFalse($this->redis->pubsub("numsub", "not-an-array"));
114+
}
74115

75116
public function testBitsets() {
76117

0 commit comments

Comments
 (0)