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

Skip to content

Commit 9a5196e

Browse files
CLIENT Commands
This commit adds support for the CLIENT commands (list, getname, setname, kill). You can call them like so: $redis->client('list'); $redis->client('getname'); $redis->client('setname', $name); $redis->client('kill', $ip_port); Solves issue phpredis#300
1 parent 55dd053 commit 9a5196e

File tree

6 files changed

+230
-1
lines changed

6 files changed

+230
-1
lines changed

README.markdown

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2881,6 +2881,32 @@ $redis->script('exists', $script1, [$script2, $script3, ...]);
28812881
* SCRIPT KILL will return true if a script was able to be killed and false if not
28822882
* SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script
28832883

2884+
### client
2885+
-----
2886+
_**Description**_: Issue the CLIENT command with various arguments.
2887+
2888+
The Redis CLIENT command can be used in four ways.
2889+
1. CLIENT LIST
2890+
1. CLIENT GETNAME
2891+
1. CLIENT SETNAME [name]
2892+
1. CLIENT KILL [ip:port]
2893+
##### *Usage*
2894+
~~~
2895+
$redis->client('list'); // Get a list of clients
2896+
$redis->client('getname'); // Get the name of the current connection
2897+
$redis->client('setname', 'somename'); // Set the name of the current connection
2898+
$redis->client('kill', <ip:port>); // Kill the process at ip:port
2899+
~~~
2900+
##### *Return value*
2901+
This will vary depending on which client command was executed.
2902+
2903+
CLIENT LIST will return an array of arrys with client information.
2904+
CLIENT GETNAME will return the client name or false if none has been set
2905+
CLIENT SETNAME will return true if it can be set and false if not
2906+
CLIENT KILL will return true if the client can be killed, and false if not
2907+
2908+
Note: phpredis will attempt to reconnect so you can actually kill your own connection
2909+
but may not notice losing it!
28842910
### getLastError
28852911
-----
28862912
_**Description**_: The last error message (if any)

library.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,128 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
608608
}
609609
}
610610

611+
/*
612+
* Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code
613+
* to handle.
614+
*/
615+
PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
616+
char *resp;
617+
int resp_len;
618+
zval *z_result, *z_sub_result;
619+
620+
// Make sure we can read a response from Redis
621+
if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
622+
RETURN_FALSE;
623+
}
624+
625+
// Allocate memory for our response
626+
MAKE_STD_ZVAL(z_result);
627+
array_init(z_result);
628+
629+
// Allocate memory for one user (there should be at least one, namely us!)
630+
ALLOC_INIT_ZVAL(z_sub_result);
631+
array_init(z_sub_result);
632+
633+
// Pointers for parsing
634+
char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value;
635+
636+
// Key length, done flag
637+
int klen, done = 0, is_numeric;
638+
639+
// While we've got more to parse
640+
while(!done) {
641+
// What character are we on
642+
switch(*p) {
643+
/* We're done */
644+
case '\0':
645+
done = 1;
646+
break;
647+
/* \n, ' ' mean we can pull a k/v pair */
648+
case '\n':
649+
case ' ':
650+
// Grab our value
651+
vpos = lpos;
652+
653+
// There is some communication error or Redis bug if we don't
654+
// have a key and value, but check anyway.
655+
if(kpos && vpos) {
656+
// Allocate, copy in our key
657+
key = emalloc(klen + 1);
658+
strncpy(key, kpos, klen);
659+
key[klen] = 0;
660+
661+
// Allocate, copy in our value
662+
value = emalloc(p-lpos+1);
663+
strncpy(value,lpos,p-lpos+1);
664+
value[p-lpos]=0;
665+
666+
// Treat numbers as numbers, strings as strings
667+
is_numeric = 1;
668+
for(p2 = value; *p; ++p) {
669+
if(*p < '0' || *p > '9') {
670+
is_numeric = 0;
671+
break;
672+
}
673+
}
674+
675+
// Add as a long or string, depending
676+
if(is_numeric == 1) {
677+
add_assoc_long(z_sub_result, key, atol(value));
678+
efree(value);
679+
} else {
680+
add_assoc_string(z_sub_result, key, value, 0);
681+
}
682+
683+
// If we hit a '\n', then we can add this user to our list
684+
if(*p == '\n') {
685+
// Add our user
686+
add_next_index_zval(z_result, z_sub_result);
687+
688+
// If we have another user, make another one
689+
if(*(p+1) != '\0') {
690+
ALLOC_INIT_ZVAL(z_sub_result);
691+
array_init(z_sub_result);
692+
}
693+
}
694+
695+
// Free our key
696+
efree(key);
697+
} else {
698+
// Something is wrong
699+
efree(resp);
700+
return -1;
701+
}
702+
703+
// Move forward
704+
lpos = p + 1;
705+
706+
break;
707+
/* We can pull the key and null terminate at our sep */
708+
case '=':
709+
// Key, key length
710+
kpos = lpos;
711+
klen = p - lpos;
712+
713+
// Move forward
714+
lpos = p + 1;
715+
716+
break;
717+
}
718+
719+
// Increment
720+
p++;
721+
}
722+
723+
// Free our respoonse
724+
efree(resp);
725+
726+
IF_MULTI_OR_PIPELINE() {
727+
add_next_index_zval(z_tab, z_result);
728+
} else {
729+
RETVAL_ZVAL(z_result, 0, 1);
730+
}
731+
}
732+
611733
PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) {
612734

613735
char *response;

library.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret
5959
PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC);
6060
PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
6161

62+
PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);
6263

6364
#if ZEND_MODULE_API_NO >= 20100000
6465
#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \

php_redis.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ PHP_METHOD(Redis, setOption);
181181

182182
PHP_METHOD(Redis, config);
183183

184+
PHP_METHOD(Redis, client);
185+
184186
#ifdef PHP_WIN32
185187
#define PHP_REDIS_API __declspec(dllexport)
186188
#else

redis.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ static zend_function_entry redis_functions[] = {
227227
PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC)
228228
PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)
229229

230+
PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
230231

231232
/* options */
232233
PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
@@ -6306,5 +6307,56 @@ PHP_METHOD(Redis, time) {
63066307
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
63076308
}
63086309

6309-
/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */
6310+
/*
6311+
* $redis->client('list');
6312+
* $redis->client('kill', <ip:port>);
6313+
* $redis->client('setname', <name>);
6314+
* $redis->client('getname');
6315+
*/
6316+
PHP_METHOD(Redis, client) {
6317+
zval *object;
6318+
RedisSock *redis_sock;
6319+
char *cmd, *opt=NULL, *arg=NULL;
6320+
int cmd_len, opt_len, arg_len;
6321+
6322+
// Parse our method parameters
6323+
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s",
6324+
&object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE)
6325+
{
6326+
RETURN_FALSE;
6327+
}
6328+
6329+
// Grab our socket
6330+
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
6331+
RETURN_FALSE;
6332+
}
6333+
6334+
// Build our CLIENT command
6335+
if(ZEND_NUM_ARGS() == 2) {
6336+
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len,
6337+
arg, arg_len);
6338+
} else {
6339+
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
6340+
}
6341+
6342+
// Handle CLIENT LIST specifically
6343+
int is_list = !strncasecmp(opt, "list", 4);
6344+
6345+
// Execute our queue command
6346+
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
6347+
6348+
// We handle CLIENT LIST with a custom response function
6349+
if(!strncasecmp(opt, "list", 4)) {
6350+
IF_ATOMIC() {
6351+
redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
6352+
}
6353+
REDIS_PROCESS_RESPONSE(redis_client_list_reply);
6354+
} else {
6355+
IF_ATOMIC() {
6356+
redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
6357+
}
6358+
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
6359+
}
6360+
}
63106361

6362+
/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */

tests/TestRedis.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,32 @@ public function testPersist() {
16501650
$this->assertTrue(FALSE === $this->redis->persist('x')); // false if the key doesn’t exist.
16511651
}
16521652

1653+
public function testClient() {
1654+
/* CLIENT SETNAME */
1655+
$this->assertTrue($this->redis->client('setname', 'phpredis_unit_tests'));
1656+
1657+
/* CLIENT LIST */
1658+
$arr_clients = $this->redis->client('list');
1659+
$this->assertTrue(is_array($arr_clients));
1660+
1661+
// Figure out which ip:port is us!
1662+
$str_addr = NULL;
1663+
foreach($arr_clients as $arr_client) {
1664+
if($arr_client['name'] == 'phpredis_unit_tests') {
1665+
$str_addr = $arr_client['addr'];
1666+
}
1667+
}
1668+
1669+
// We should have found our connection
1670+
$this->assertFalse(empty($str_addr));
1671+
1672+
/* CLIENT GETNAME */
1673+
$this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests');
1674+
1675+
/* CLIENT KILL -- phpredis will reconnect, so we can do this */
1676+
$this->assertTrue($this->redis->client('kill', $str_addr));
1677+
}
1678+
16531679
public function testinfo() {
16541680
$info = $this->redis->info();
16551681

0 commit comments

Comments
 (0)