Skip to content

Commit f9f2dc0

Browse files
Merge branch 'release/2.2.3'
2 parents 4c16a30 + 3e6d5b6 commit f9f2dc0

13 files changed

+350
-28
lines changed

README.markdown

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2522,7 +2522,6 @@ $redis->zAdd('key', 2, 'val2');
25222522
$redis->zAdd('key', 10, 'val10');
25232523
$redis->zRangeByScore('key', 0, 3); /* array('val0', 'val2') */
25242524
$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE); /* array('val0' => 0, 'val2' => 2) */
2525-
$redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2' => 2) */
25262525
$redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); /* array('val2') */
25272526
$redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); /* array('val2' => 2) */
25282527
~~~
@@ -2889,6 +2888,34 @@ $redis->script('exists', $script1, [$script2, $script3, ...]);
28892888
* SCRIPT KILL will return true if a script was able to be killed and false if not
28902889
* SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script
28912890

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

common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ typedef struct {
160160
char *auth;
161161
double timeout;
162162
double read_timeout;
163+
long retry_interval;
163164
int failed;
164165
int status;
165166
int persistent;

library.c

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
3838
int eof;
3939
int count = 0;
4040

41-
if (!redis_sock->stream)
41+
if (!redis_sock->stream) {
4242
return -1;
43+
}
4344

4445
eof = php_stream_eof(redis_sock->stream);
4546
for (; eof; count++) {
@@ -60,6 +61,12 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
6061
redis_sock->mode = ATOMIC;
6162
redis_sock->watching = 0;
6263
}
64+
// Wait for a while before trying to reconnect
65+
if (redis_sock->retry_interval) {
66+
// Random factor to avoid having several (or many) concurrent connections trying to reconnect at the same time
67+
long retry_interval = (count ? redis_sock->retry_interval : (random() % redis_sock->retry_interval));
68+
usleep(retry_interval);
69+
}
6370
redis_sock_connect(redis_sock TSRMLS_CC); /* reconnect */
6471
if(redis_sock->stream) { /* check for EOF again. */
6572
eof = php_stream_eof(redis_sock->stream);
@@ -608,6 +615,128 @@ PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
608615
}
609616
}
610617

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

613742
char *response;
@@ -840,7 +969,8 @@ PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s
840969
* redis_sock_create
841970
*/
842971
PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port,
843-
double timeout, int persistent, char *persistent_id)
972+
double timeout, int persistent, char *persistent_id,
973+
long retry_interval)
844974
{
845975
RedisSock *redis_sock;
846976

@@ -850,7 +980,7 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por
850980
redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED;
851981
redis_sock->watching = 0;
852982
redis_sock->dbNumber = 0;
853-
983+
redis_sock->retry_interval = retry_interval * 1000;
854984
redis_sock->persistent = persistent;
855985

856986
if(persistent_id) {

library.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ PHPAPI void redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis
2020
PHPAPI void redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
2121
PHPAPI void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
2222
PHPAPI void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
23-
PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id);
23+
PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval);
2424
PHPAPI int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC);
2525
PHPAPI int redis_sock_server_open(RedisSock *redis_sock, int force_connect TSRMLS_DC);
2626
PHPAPI int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC);
@@ -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: 3 additions & 1 deletion
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
PHP_METHOD(Redis, getHost);
185187
PHP_METHOD(Redis, getPort);
186188
PHP_METHOD(Redis, getDBNum);
@@ -254,7 +256,7 @@ extern zend_module_entry redis_module_entry;
254256

255257
#define phpext_redis_ptr redis_module_ptr
256258

257-
#define PHP_REDIS_VERSION "2.2.2"
259+
#define PHP_REDIS_VERSION "2.2.3"
258260

259261
#endif
260262

redis.c

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
| Original author: Alfonso Jimenez <[email protected]> |
1717
| Maintainer: Nicolas Favre-Felix <[email protected]> |
1818
| Maintainer: Nasreddine Bouafif <[email protected]> |
19+
| Maintainer: Michael Grunder <[email protected] |
1920
+----------------------------------------------------------------------+
2021
*/
2122

@@ -227,6 +228,7 @@ static zend_function_entry redis_functions[] = {
227228
PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC)
228229
PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)
229230

231+
PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
230232

231233
/* options */
232234
PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
@@ -554,7 +556,7 @@ PHP_METHOD(Redis,__destruct) {
554556
}
555557
}
556558

557-
/* {{{ proto boolean Redis::connect(string host, int port [, double timeout])
559+
/* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]])
558560
*/
559561
PHP_METHOD(Redis, connect)
560562
{
@@ -590,6 +592,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
590592
int host_len, id;
591593
char *host = NULL;
592594
long port = -1;
595+
long retry_interval = 0;
593596

594597
char *persistent_id = NULL;
595598
int persistent_id_len = -1;
@@ -602,9 +605,10 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
602605
persistent = 0;
603606
#endif
604607

605-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|lds",
608+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|ldsl",
606609
&object, redis_ce, &host, &host_len, &port,
607-
&timeout, &persistent_id, &persistent_id_len) == FAILURE) {
610+
&timeout, &persistent_id, &persistent_id_len,
611+
&retry_interval) == FAILURE) {
608612
return FAILURE;
609613
}
610614

@@ -613,6 +617,11 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
613617
return FAILURE;
614618
}
615619

620+
if (retry_interval < 0L || retry_interval > INT_MAX) {
621+
zend_throw_exception(redis_exception_ce, "Invalid retry interval", 0 TSRMLS_CC);
622+
return FAILURE;
623+
}
624+
616625
if(port == -1 && host_len && host[0] != '/') { /* not unix socket, set to default value */
617626
port = 6379;
618627
}
@@ -629,7 +638,7 @@ PHPAPI int redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) {
629638
zend_clear_exception(TSRMLS_C); /* clear exception triggered by non-existent socket during connect(). */
630639
}
631640

632-
redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id);
641+
redis_sock = redis_sock_create(host, host_len, port, timeout, persistent, persistent_id, retry_interval);
633642

634643
if (redis_sock_server_open(redis_sock, 1 TSRMLS_CC) < 0) {
635644
redis_free_socket(redis_sock);
@@ -6456,5 +6465,56 @@ PHP_METHOD(Redis, getAuth) {
64566465
}
64576466
}
64586467

6459-
/* vim: set tabstop=4 softtabstop=4 noexpandtab shiftwidth=4: */
6468+
/*
6469+
* $redis->client('list');
6470+
* $redis->client('kill', <ip:port>);
6471+
* $redis->client('setname', <name>);
6472+
* $redis->client('getname');
6473+
*/
6474+
PHP_METHOD(Redis, client) {
6475+
zval *object;
6476+
RedisSock *redis_sock;
6477+
char *cmd, *opt=NULL, *arg=NULL;
6478+
int cmd_len, opt_len, arg_len;
6479+
6480+
// Parse our method parameters
6481+
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|s",
6482+
&object, redis_ce, &opt, &opt_len, &arg, &arg_len) == FAILURE)
6483+
{
6484+
RETURN_FALSE;
6485+
}
6486+
6487+
// Grab our socket
6488+
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
6489+
RETURN_FALSE;
6490+
}
6491+
6492+
// Build our CLIENT command
6493+
if(ZEND_NUM_ARGS() == 2) {
6494+
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "ss", opt, opt_len,
6495+
arg, arg_len);
6496+
} else {
6497+
cmd_len = redis_cmd_format_static(&cmd, "CLIENT", "s", opt, opt_len);
6498+
}
6499+
6500+
// Handle CLIENT LIST specifically
6501+
int is_list = !strncasecmp(opt, "list", 4);
6502+
6503+
// Execute our queue command
6504+
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
6505+
6506+
// We handle CLIENT LIST with a custom response function
6507+
if(!strncasecmp(opt, "list", 4)) {
6508+
IF_ATOMIC() {
6509+
redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
6510+
}
6511+
REDIS_PROCESS_RESPONSE(redis_client_list_reply);
6512+
} else {
6513+
IF_ATOMIC() {
6514+
redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL);
6515+
}
6516+
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
6517+
}
6518+
}
64606519

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

0 commit comments

Comments
 (0)