Skip to content

Commit 91b5a37

Browse files
Implement rawCommand() properly, as it's not the COMMAND command in Redis.
1 parent 90a7cf4 commit 91b5a37

File tree

6 files changed

+184
-37
lines changed

6 files changed

+184
-37
lines changed

php_redis.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ PHP_METHOD(Redis, wait);
197197
PHP_METHOD(Redis, pubsub);
198198

199199
PHP_METHOD(Redis, client);
200+
PHP_METHOD(Redis, command);
200201
PHP_METHOD(Redis, rawcommand);
201202

202203
/* SCAN and friends */
@@ -220,7 +221,6 @@ PHP_METHOD(Redis, isConnected);
220221
PHP_METHOD(Redis, getPersistentID);
221222
PHP_METHOD(Redis, getAuth);
222223
PHP_METHOD(Redis, getMode);
223-
PHP_METHOD(Redis, rawcommand);
224224

225225
#ifdef ZTS
226226
#include "TSRM.h"

redis.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,8 @@ static zend_function_entry redis_functions[] = {
266266
PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)
267267

268268
PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
269-
269+
PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC)
270+
270271
/* SCAN and friends */
271272
PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC)
272273
PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC)
@@ -3684,11 +3685,50 @@ PHP_METHOD(Redis, client) {
36843685
}
36853686
}
36863687

3687-
/* proto array Redis::rawcommand()
3688-
* proto array Redis::rawcommand('info', string cmd)
3689-
* proto array Redis::rawcommand('getkeys', array cmd_args) */
3688+
/* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */
36903689
PHP_METHOD(Redis, rawcommand) {
3691-
REDIS_PROCESS_CMD(rawcommand, redis_read_variant_reply);
3690+
int argc = ZEND_NUM_ARGS(), cmd_len;
3691+
char *cmd = NULL;
3692+
RedisSock *redis_sock;
3693+
zval **z_args;
3694+
3695+
/* Sanity check on arguments */
3696+
z_args = emalloc(argc * sizeof(zval*));
3697+
if (argc < 1) {
3698+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
3699+
"Must pass at least one command keyword");
3700+
efree(z_args);
3701+
RETURN_FALSE;
3702+
} else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
3703+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
3704+
"Internal PHP error parsing arguments");
3705+
efree(z_args);
3706+
RETURN_FALSE;
3707+
} else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len TSRMLS_CC) < 0 ||
3708+
redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0)
3709+
{
3710+
if (cmd) efree(cmd);
3711+
efree(z_args);
3712+
RETURN_FALSE;
3713+
}
3714+
3715+
/* Clean up command array */
3716+
efree(z_args);
3717+
3718+
/* Execute our command */
3719+
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
3720+
IF_ATOMIC() {
3721+
redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL);
3722+
}
3723+
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
3724+
}
3725+
/* }}} */
3726+
3727+
/* {{{ proto array Redis::command()
3728+
* proto array Redis::command('info', string cmd)
3729+
* proto array Redis::command('getkeys', array cmd_args) */
3730+
PHP_METHOD(Redis, command) {
3731+
REDIS_PROCESS_CMD(command, redis_read_variant_reply);
36923732
}
36933733
/* }}} */
36943734

redis_cluster.c

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ zend_function_entry redis_cluster_functions[] = {
209209
PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC)
210210
PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC)
211211
PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC)
212+
PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC)
212213
PHP_ME(RedisCluster, rawcommand, NULL, ZEND_ACC_PUBLIC)
213214
PHP_ME(RedisCluster, cluster, NULL, ZEND_ACC_PUBLIC)
214215
PHP_ME(RedisCluster, client, NULL, ZEND_ACC_PUBLIC)
@@ -2199,27 +2200,8 @@ PHP_METHOD(RedisCluster, exec) {
21992200
CLUSTER_RESET_MULTI(c);
22002201
}
22012202

2202-
/* {{{ proto bool RedisCluster::discard() */
2203-
PHP_METHOD(RedisCluster, discard) {
2204-
redisCluster *c = GET_CONTEXT();
2205-
2206-
if(CLUSTER_IS_ATOMIC(c)) {
2207-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode");
2208-
RETURN_FALSE;
2209-
}
2210-
2211-
if(cluster_abort_exec(c TSRMLS_CC)<0) {
2212-
CLUSTER_RESET_MULTI(c);
2213-
}
2214-
2215-
CLUSTER_FREE_QUEUE(c);
2216-
2217-
RETURN_TRUE;
2218-
}
2219-
22202203
/* Get a slot either by key (string) or host/port array */
2221-
static short
2222-
cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC)
2204+
static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC)
22232205
{
22242206
int key_len, key_free;
22252207
zval **z_host, **z_port, *z_tmp = NULL;
@@ -2229,7 +2211,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC)
22292211
/* If it's a string, treat it as a key. Otherwise, look for a two
22302212
* element array */
22312213
if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG ||
2232-
Z_TYPE_P(z_arg)==IS_DOUBLE)
2214+
Z_TYPE_P(z_arg)==IS_DOUBLE)
22332215
{
22342216
/* Allow for any scalar here */
22352217
if (Z_TYPE_P(z_arg) != IS_STRING) {
@@ -2253,7 +2235,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC)
22532235
zval_dtor(z_tmp);
22542236
efree(z_tmp);
22552237
}
2256-
} else if (Z_TYPE_P(z_arg) == IS_ARRAY &&
2238+
} else if (Z_TYPE_P(z_arg) == IS_ARRAY &&
22572239
zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)!=FAILURE &&
22582240
zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)!=FAILURE &&
22592241
Z_TYPE_PP(z_host)==IS_STRING && Z_TYPE_PP(z_port)==IS_LONG)
@@ -2263,7 +2245,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC)
22632245
(unsigned short)Z_LVAL_PP(z_port));
22642246

22652247
/* Inform the caller if they've passed bad data */
2266-
if(slot < 0) {
2248+
if(slot < 0) {
22672249
php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld",
22682250
Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port));
22692251
}
@@ -2276,6 +2258,24 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC)
22762258
return slot;
22772259
}
22782260

2261+
/* {{{ proto bool RedisCluster::discard() */
2262+
PHP_METHOD(RedisCluster, discard) {
2263+
redisCluster *c = GET_CONTEXT();
2264+
2265+
if(CLUSTER_IS_ATOMIC(c)) {
2266+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode");
2267+
RETURN_FALSE;
2268+
}
2269+
2270+
if(cluster_abort_exec(c TSRMLS_CC)<0) {
2271+
CLUSTER_RESET_MULTI(c);
2272+
}
2273+
2274+
CLUSTER_FREE_QUEUE(c);
2275+
2276+
RETURN_TRUE;
2277+
}
2278+
22792279
/* Generic handler for things we want directed at a given node, like SAVE,
22802280
* BGSAVE, FLUSHDB, FLUSHALL, etc */
22812281
static void
@@ -2856,7 +2856,7 @@ PHP_METHOD(RedisCluster, echo) {
28562856
rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE;
28572857
if(cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) {
28582858
zend_throw_exception(redis_cluster_exception_ce,
2859-
"Unable to send commnad at the specificed node", 0 TSRMLS_CC);
2859+
"Unable to send command at the specificed node", 0 TSRMLS_CC);
28602860
efree(cmd);
28612861
RETURN_FALSE;
28622862
}
@@ -2873,11 +2873,66 @@ PHP_METHOD(RedisCluster, echo) {
28732873
}
28742874
/* }}} */
28752875

2876-
/* {{{ proto array RedisCluster::rawcommand()
2877-
* proto array RedisCluster::rawcommand('INFO', string cmd)
2878-
* proto array RedisCluster::rawcommand('GETKEYS', array cmd_args) */
2876+
/* {{{ proto mixed RedisCluster::rawcommand(string $key, string $cmd, [ $argv1 .. $argvN])
2877+
* proto mixed RedisCluster::rawcommand(array $host_port, string $cmd, [ $argv1 .. $argvN]) */
28792878
PHP_METHOD(RedisCluster, rawcommand) {
2880-
CLUSTER_PROCESS_CMD(rawcommand, cluster_variant_resp, 0);
2879+
REDIS_REPLY_TYPE rtype;
2880+
int argc = ZEND_NUM_ARGS(), cmd_len;
2881+
redisCluster *c = GET_CONTEXT();
2882+
char *cmd = NULL;
2883+
zval **z_args;
2884+
short slot;
2885+
2886+
/* Sanity check on our arguments */
2887+
z_args = emalloc(argc * sizeof(zval*));
2888+
if (argc < 2) {
2889+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
2890+
"You must pass at least node information as well as at least a command.");
2891+
efree(z_args);
2892+
RETURN_FALSE;
2893+
} else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
2894+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
2895+
"Internal PHP error parsing method parameters.");
2896+
efree(z_args);
2897+
RETURN_FALSE;
2898+
} else if (redis_build_raw_cmd(z_args+1, argc-1, &cmd, &cmd_len TSRMLS_CC) ||
2899+
(slot = cluster_cmd_get_slot(c, z_args[0] TSRMLS_CC))<0)
2900+
{
2901+
if (cmd) efree(cmd);
2902+
efree(z_args);
2903+
RETURN_FALSE;
2904+
}
2905+
2906+
/* Free argument array */
2907+
efree(z_args);
2908+
2909+
/* Direct the command */
2910+
rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE;
2911+
if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) {
2912+
zend_throw_exception(redis_cluster_exception_ce,
2913+
"Unable to send command to the specified node", 0 TSRMLS_CC);
2914+
efree(cmd);
2915+
efree(z_args);
2916+
RETURN_FALSE;
2917+
}
2918+
2919+
/* Process variant response */
2920+
if (CLUSTER_IS_ATOMIC(c)) {
2921+
cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2922+
} else {
2923+
void *ctx = NULL;
2924+
CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx);
2925+
}
2926+
2927+
efree(cmd);
2928+
}
2929+
/* }}} */
2930+
2931+
/* {{{ proto array RedisCluster::command()
2932+
* proto array RedisCluster::command('INFO', string cmd)
2933+
* proto array RedisCluster::command('GETKEYS', array cmd_args) */
2934+
PHP_METHOD(RedisCluster, command) {
2935+
CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0);
28812936
}
28822937
/* }}} */
28832938

redis_cluster.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ PHP_METHOD(RedisCluster, config);
237237
PHP_METHOD(RedisCluster, pubsub);
238238
PHP_METHOD(RedisCluster, script);
239239
PHP_METHOD(RedisCluster, slowlog);
240+
PHP_METHOD(RedisCluster, command);
240241

241242
/* SCAN and friends */
242243
PHP_METHOD(RedisCluster, scan);

redis_commands.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,51 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
3434
return SUCCESS;
3535
}
3636

37+
/* Helper to construct a raw command. Given that the cluster and non cluster
38+
* versions are different (RedisCluster needs an additional argument to direct
39+
* the command) we take the start of our array and count */
40+
int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC)
41+
{
42+
smart_str cmdstr = {0};
43+
int i;
44+
45+
/* Make sure our first argument is a string */
46+
if (Z_TYPE_P(z_args[0]) != IS_STRING) {
47+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
48+
"When sending a 'raw' command, the first argument must be a string!");
49+
return FAILURE;
50+
}
51+
52+
/* Initialize our command string */
53+
redis_cmd_init_sstr(&cmdstr, argc-1, Z_STRVAL_P(z_args[0]), Z_STRLEN_P(z_args[0]));
54+
55+
for (i = 1; i < argc; i++) {
56+
switch (Z_TYPE_P(z_args[i])) {
57+
case IS_STRING:
58+
redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_args[i]),
59+
Z_STRLEN_P(z_args[i]));
60+
break;
61+
case IS_LONG:
62+
redis_cmd_append_sstr_long(&cmdstr,Z_LVAL_P(z_args[i]));
63+
break;
64+
case IS_DOUBLE:
65+
redis_cmd_append_sstr_dbl(&cmdstr,Z_DVAL_P(z_args[i]));
66+
break;
67+
default:
68+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
69+
"Raw command arguments must be scalar values!");
70+
efree(cmdstr.c);
71+
return FAILURE;
72+
}
73+
}
74+
75+
/* Push command and length to caller */
76+
*cmd = cmdstr.c;
77+
*cmd_len = cmdstr.len;
78+
79+
return SUCCESS;
80+
}
81+
3782
/* Generic command where we just take a string and do nothing to it*/
3883
int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw,
3984
char **cmd, int *cmd_len, short *slot, void **ctx)
@@ -2699,8 +2744,8 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
26992744
}
27002745

27012746
/* COMMAND */
2702-
int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
2703-
char **cmd, int *cmd_len, short *slot, void **ctx)
2747+
int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
2748+
char **cmd, int *cmd_len, short *slot, void **ctx)
27042749
{
27052750
char *kw=NULL;
27062751
zval *z_arg;

redis_commands.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ typedef struct subscribeContext {
2121
zend_fcall_info_cache cb_cache;
2222
} subscribeContext;
2323

24+
/* Construct a raw command */
25+
int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC);
26+
2427
/* Redis command generics. Many commands share common prototypes meaning that
2528
* we can write one function to handle all of them. For example, there are
2629
* many COMMAND key value commands, or COMMAND key commands. */
@@ -212,9 +215,12 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
212215
int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
213216
char **cmd, int *cmd_len, short *slot, void **ctx);
214217

215-
int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
218+
int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
216219
char **cmd, int *cmd_len, short *slot, void **ctx);
217220

221+
int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
222+
char **cmd, int *cmd_len, short *slot, void **ctx);
223+
218224
int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
219225
long it, char *pat, int pat_len, long count);
220226

0 commit comments

Comments
 (0)