Skip to content

Commit

Permalink
CLIENT Commands
Browse files Browse the repository at this point in the history
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 #300
  • Loading branch information
michael-grunder committed Feb 17, 2013
1 parent 55dd053 commit 9a5196e
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 1 deletion.
26 changes: 26 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2881,6 +2881,32 @@ $redis->script('exists', $script1, [$script2, $script3, ...]);
* SCRIPT KILL will return true if a script was able to be killed and false if not
* SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script

### client
-----
_**Description**_: Issue the CLIENT command with various arguments.

The Redis CLIENT command can be used in four ways.
1. CLIENT LIST
1. CLIENT GETNAME
1. CLIENT SETNAME [name]
1. CLIENT KILL [ip:port]
##### *Usage*
~~~
$redis->client('list'); // Get a list of clients
$redis->client('getname'); // Get the name of the current connection
$redis->client('setname', 'somename'); // Set the name of the current connection
$redis->client('kill', <ip:port>); // Kill the process at ip:port
~~~
##### *Return value*
This will vary depending on which client command was executed.

CLIENT LIST will return an array of arrys with client information.
CLIENT GETNAME will return the client name or false if none has been set
CLIENT SETNAME will return true if it can be set and false if not
CLIENT KILL will return true if the client can be killed, and false if not

Note: phpredis will attempt to reconnect so you can actually kill your own connection
but may not notice losing it!
### getLastError
-----
_**Description**_: The last error message (if any)
Expand Down
122 changes: 122 additions & 0 deletions library.c
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,128 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
}
}

/*
* Specialized handling of the CLIENT LIST output so it comes out in a simple way for PHP userland code
* to handle.
*/
PHPAPI void redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) {
char *resp;
int resp_len;
zval *z_result, *z_sub_result;

// Make sure we can read a response from Redis
if((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
RETURN_FALSE;
}

// Allocate memory for our response
MAKE_STD_ZVAL(z_result);
array_init(z_result);

// Allocate memory for one user (there should be at least one, namely us!)
ALLOC_INIT_ZVAL(z_sub_result);
array_init(z_sub_result);

// Pointers for parsing
char *p = resp, *lpos = resp, *kpos = NULL, *vpos = NULL, *p2, *key, *value;

// Key length, done flag
int klen, done = 0, is_numeric;

// While we've got more to parse
while(!done) {
// What character are we on
switch(*p) {
/* We're done */
case '\0':
done = 1;
break;
/* \n, ' ' mean we can pull a k/v pair */
case '\n':
case ' ':
// Grab our value
vpos = lpos;

// There is some communication error or Redis bug if we don't
// have a key and value, but check anyway.
if(kpos && vpos) {
// Allocate, copy in our key
key = emalloc(klen + 1);
strncpy(key, kpos, klen);
key[klen] = 0;

// Allocate, copy in our value
value = emalloc(p-lpos+1);
strncpy(value,lpos,p-lpos+1);
value[p-lpos]=0;

// Treat numbers as numbers, strings as strings
is_numeric = 1;
for(p2 = value; *p; ++p) {
if(*p < '0' || *p > '9') {
is_numeric = 0;
break;
}
}

// Add as a long or string, depending
if(is_numeric == 1) {
add_assoc_long(z_sub_result, key, atol(value));
efree(value);
} else {
add_assoc_string(z_sub_result, key, value, 0);
}

// If we hit a '\n', then we can add this user to our list
if(*p == '\n') {
// Add our user
add_next_index_zval(z_result, z_sub_result);

// If we have another user, make another one
if(*(p+1) != '\0') {
ALLOC_INIT_ZVAL(z_sub_result);
array_init(z_sub_result);
}
}

// Free our key
efree(key);
} else {
// Something is wrong
efree(resp);
return -1;
}

// Move forward
lpos = p + 1;

break;
/* We can pull the key and null terminate at our sep */
case '=':
// Key, key length
kpos = lpos;
klen = p - lpos;

// Move forward
lpos = p + 1;

break;
}

// Increment
p++;
}

// Free our respoonse
efree(resp);

IF_MULTI_OR_PIPELINE() {
add_next_index_zval(z_tab, z_result);
} else {
RETVAL_ZVAL(z_result, 0, 1);
}
}

PHPAPI void redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback) {

char *response;
Expand Down
1 change: 1 addition & 0 deletions library.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ PHPAPI int redis_read_variant_bulk(RedisSock *redis_sock, int size, zval **z_ret
PHPAPI int redis_read_multibulk_recursive(RedisSock *redis_sock, int elements, zval **z_ret TSRMLS_DC);
PHPAPI int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab);

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

#if ZEND_MODULE_API_NO >= 20100000
#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
Expand Down
2 changes: 2 additions & 0 deletions php_redis.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ PHP_METHOD(Redis, setOption);

PHP_METHOD(Redis, config);

PHP_METHOD(Redis, client);

#ifdef PHP_WIN32
#define PHP_REDIS_API __declspec(dllexport)
#else
Expand Down
54 changes: 53 additions & 1 deletion redis.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)

PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)

/* options */
PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
Expand Down Expand Up @@ -6306,5 +6307,56 @@ PHP_METHOD(Redis, time) {
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_raw);
}

/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */
/*
* $redis->client('list');
* $redis->client('kill', <ip:port>);
* $redis->client('setname', <name>);
* $redis->client('getname');
*/
PHP_METHOD(Redis, client) {
zval *object;
RedisSock *redis_sock;
char *cmd, *opt=NULL, *arg=NULL;
int cmd_len, opt_len, arg_len;

// Parse our method parameters
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s",
&object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE)
{
RETURN_FALSE;
}

// Grab our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}

// Build our CLIENT command
if(ZEND_NUM_ARGS() == 2) {
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len,
arg, arg_len);
} else {
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
}

// Handle CLIENT LIST specifically
int is_list = !strncasecmp(opt, "list", 4);

// Execute our queue command
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);

// We handle CLIENT LIST with a custom response function
if(!strncasecmp(opt, "list", 4)) {
IF_ATOMIC() {
redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
}
REDIS_PROCESS_RESPONSE(redis_client_list_reply);
} else {
IF_ATOMIC() {
redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
}

/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */
26 changes: 26 additions & 0 deletions tests/TestRedis.php
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,32 @@ public function testPersist() {
$this->assertTrue(FALSE === $this->redis->persist('x')); // false if the key doesn’t exist.
}

public function testClient() {
/* CLIENT SETNAME */
$this->assertTrue($this->redis->client('setname', 'phpredis_unit_tests'));

/* CLIENT LIST */
$arr_clients = $this->redis->client('list');
$this->assertTrue(is_array($arr_clients));

// Figure out which ip:port is us!
$str_addr = NULL;
foreach($arr_clients as $arr_client) {
if($arr_client['name'] == 'phpredis_unit_tests') {
$str_addr = $arr_client['addr'];
}
}

// We should have found our connection
$this->assertFalse(empty($str_addr));

/* CLIENT GETNAME */
$this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests');

/* CLIENT KILL -- phpredis will reconnect, so we can do this */
$this->assertTrue($this->redis->client('kill', $str_addr));
}

public function testinfo() {
$info = $this->redis->info();

Expand Down

0 comments on commit 9a5196e

Please sign in to comment.