8000 CLIENT Commands · devsnippet/phpredis@9a5196e · GitHub
[go: up one dir, main page]

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)
0