diff --git a/.gitignore b/.gitignore index 077c14b8..0d2826d1 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ doctum.phar run-tests.php vendor/ .agents.md +.codex diff --git a/cluster_library.c b/cluster_library.c index 439f7739..085ebac9 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -10,6 +10,8 @@ extern zend_class_entry *redis_cluster_exception_ce; int le_cluster_slot_cache; +extern RedisCmdCtx redis_empty_ctx; + /* Debugging methods/ static void cluster_dump_nodes(redisCluster *c) { redisClusterNode *p; @@ -90,17 +92,17 @@ static void dump_reply(clusterReply *reply, int indent) { /* MULTI BULK processing callbacks */ static int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx); + long long count, RedisCmdCtx ctx); static int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx); + long long count, RedisCmdCtx ctx); static int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx); + long long count, RedisCmdCtx ctx); static int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx); + long long count, RedisCmdCtx ctx); static int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx); + long long count, RedisCmdCtx ctx); static int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx); + long long count, RedisCmdCtx ctx); @@ -457,9 +459,7 @@ int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, } /* Provided a clusterKeyVal, add a value */ -void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val - ) -{ +void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val) { char *val; size_t val_len; int val_free; @@ -475,21 +475,24 @@ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val /* Free allocated memory for a clusterMultiCmd */ void cluster_multi_free(clusterMultiCmd *mc) { - efree(mc->cmd.c); - efree(mc->args.c); + redis_cmd_free(mc->cmd); } /* Add an argument to a clusterMultiCmd */ void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len) { + if (mc->cmd == NULL) { + mc->cmd = redis_cmd_create(NULL, mc->kw, mc->kw_len); + } + mc->argc++; - redis_cmd_append_sstr(&(mc->args), data, data_len); + redis_cmd_cat_str(mc->cmd, data, data_len); } -/* Finalize a clusterMultiCmd by constructing the whole thing */ +/* Finalize a clusterMultiCmd */ void cluster_multi_fini(clusterMultiCmd *mc) { - mc->cmd.len = 0; - redis_cmd_init_sstr(&(mc->cmd), mc->argc, mc->kw, mc->kw_len); - smart_string_appendl(&(mc->cmd), mc->args.c, mc->args.len); + if (mc->cmd == NULL) { + mc->cmd = redis_cmd_create(NULL, mc->kw, mc->kw_len); + } } /* Set our last error string encountered */ @@ -653,7 +656,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, zend_llist_init(&node->slots, sizeof(redisSlotRange), NULL, 0); // Attach socket - node->sock = redis_sock_create(host, host_len, port, + node->sock = redis_sock_create(REDIS_SOCK_CLUSTER, host, host_len, port, c->flags->timeout, c->flags->read_timeout, c->flags->persistent, NULL, 0); @@ -876,6 +879,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, /* Initialize flags and settings */ c->flags = ecalloc(1, sizeof(RedisSock)); + c->flags->type = REDIS_SOCK_CLUSTER; c->flags->timeout = timeout; c->flags->read_timeout = read_timeout; c->flags->persistent = persistent; @@ -1052,9 +1056,10 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) { keylen = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(cm->host.addr), cm->host.port); /* Create socket */ - sock = redis_sock_create(ZSTR_VAL(cm->host.addr), ZSTR_LEN(cm->host.addr), cm->host.port, - c->flags->timeout, c->flags->read_timeout, c->flags->persistent, - NULL, 0); + sock = redis_sock_create(REDIS_SOCK_CLUSTER, ZSTR_VAL(cm->host.addr), + ZSTR_LEN(cm->host.addr), cm->host.port, + c->flags->timeout, c->flags->read_timeout, + c->flags->persistent, NULL, 0); /* Stream context */ redis_sock_set_context(sock, c->flags->context); @@ -1108,8 +1113,9 @@ cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds) ZEND_ASSERT(sep != NULL); // Allocate a structure for this seed - sock = redis_sock_create(seed, sep - seed, atoi(sep + 1), - c->flags->timeout, c->flags->read_timeout, + sock = redis_sock_create(REDIS_SOCK_CLUSTER, seed, sep - seed, + atoi(sep + 1), c->flags->timeout, + c->flags->read_timeout, c->flags->persistent, NULL, 0); /* Credentials and context */ @@ -1578,8 +1584,9 @@ PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, } /* Send a command to a specific slot */ -PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, - int cmd_len, REDIS_REPLY_TYPE rtype) +PHP_REDIS_API int +cluster_send_slot(redisCluster *c, short slot, const char *cmd, int cmd_len, + REDIS_REPLY_TYPE rtype) { /* Point our cluster to this slot and it's socket */ c->cmd_slot = slot; @@ -1729,7 +1736,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char /* RAW bulk response handler */ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx) + redisCluster *c, RedisCmdCtx ctx) { char *resp; @@ -1751,7 +1758,8 @@ PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, } PHP_REDIS_API void -cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { char *p; @@ -1789,7 +1797,7 @@ static int cluster_bulk_resp_to_zval(redisCluster *c, zval *zdst) { /* BULK response handler */ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { zval zret; @@ -1804,7 +1812,7 @@ PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster PHP_REDIS_API void cluster_bulk_withmeta_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { zval zbulk, zmeta; @@ -1821,7 +1829,7 @@ cluster_bulk_withmeta_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* Bulk response where we expect a double */ PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { char *resp; double dbl; @@ -1843,7 +1851,7 @@ PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * /* A boolean response. If we get here, we've consumed the '+' reply * type and will now just verify we can read the OK */ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { // Check that we have +OK if (c->reply_type != TYPE_LINE || c->reply_len != 2 || @@ -1857,7 +1865,7 @@ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster /* Boolean response, specialized for PING */ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { if (c->reply_type != TYPE_LINE || c->reply_len != 4 || memcmp(c->line_reply,"PONG",sizeof("PONG")-1)) @@ -1869,55 +1877,42 @@ PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } PHP_REDIS_API void -cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { - if (ctx == NULL) { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + if (ctx.ptr == NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); } } PHP_REDIS_API void -cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, RedisCmdCtx ctx) { - if (ctx == NULL) { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + if (ctx.ptr == NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); } } PHP_REDIS_API void -cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, RedisCmdCtx ctx) { zval zret = {0}; c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; - if (redis_read_lpos_response(&zret, c->cmd_sock, c->reply_type, c->reply_len, ctx) < 0) { - ZVAL_FALSE(&zret); - } - - if (CLUSTER_IS_ATOMIC(c)) { - RETVAL_ZVAL(&zret, 0, 1); - } else { - add_next_index_zval(&c->multi_resp, &zret); - } -} - -PHP_REDIS_API void -cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - zval zret = {0}; - - c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; - if (c->reply_type != TYPE_MULTIBULK || - redis_read_geosearch_response(&zret, c->cmd_sock, c->reply_len, ctx != NULL) < 0) + if (redis_read_lpos_response(&zret, c->cmd_sock, c->reply_type, + c->reply_len, ctx) < 0) { ZVAL_FALSE(&zret); } @@ -1930,76 +1925,111 @@ cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) } PHP_REDIS_API void -cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - if (ctx == NULL) { - cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); +cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { + zval zret = {0}; + + c->cmd_sock->null_mbulk_as_null = c->flags->null_mbulk_as_null; + if (c->reply_type != TYPE_MULTIBULK || + redis_read_geosearch_response(&zret, c->cmd_sock, c->reply_len, + ctx.ptr != NULL) < 0) + { + ZVAL_FALSE(&zret); + } + + if (CLUSTER_IS_ATOMIC(c)) { + RETVAL_ZVAL(&zret, 0, 1); } else { - ZEND_ASSERT(!"memory corruption?"); + add_next_index_zval(&c->multi_resp, &zret); } } PHP_REDIS_API void -cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); - - if (ctx == NULL) { - cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - cluster_dbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } -} - - -PHP_REDIS_API void -cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - if (ctx == NULL) { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - ZEND_ASSERT(!"memory corruption?"); - } -} - -PHP_REDIS_API void -cluster_randmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - if (ctx == NULL) { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - ZEND_ASSERT(!"memory corruption?"); - } -} - -PHP_REDIS_API void -cluster_object_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); - - if (ctx == PHPREDIS_CTX_PTR) { - cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } -} - -PHP_REDIS_API void -cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { - if (ctx == NULL) { - cluster_bool_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + if (ctx.ptr == NULL) { + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); } else { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + ZEND_ASSERT(!"memory corruption?"); + } +} + +PHP_REDIS_API void +cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); + + if (ctx.ptr == NULL) { + cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } else { + cluster_dbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } +} + + +PHP_REDIS_API void +cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ + if (ctx.ptr == NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); + } else { + ZEND_ASSERT(!"memory corruption?"); + } +} + +PHP_REDIS_API void +cluster_randmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ + if (ctx.ptr == NULL) { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } else { + ZEND_ASSERT(!"memory corruption?"); + } +} + +PHP_REDIS_API void +cluster_object_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ + ZEND_ASSERT(ctx.ptr == PHPREDIS_CTX_PTR || ctx.ptr == PHPREDIS_CTX_PTR + 1); + + if (ctx.ptr == PHPREDIS_CTX_PTR) { + cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } else { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } +} + +PHP_REDIS_API void +cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, RedisCmdCtx ctx) +{ + if (ctx.ptr == NULL) { + cluster_bool_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); + } else { + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); } } /* 1 or 0 response, for things like SETNX */ PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { // Validate our reply type, and check for a zero if (c->reply_type != TYPE_INT || c->reply_len == 0) { @@ -2010,8 +2040,9 @@ PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } /* Generic integer response */ -PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +PHP_REDIS_API void +cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { if (c->reply_type != TYPE_INT) { CLUSTER_RETURN_FALSE(c); @@ -2020,8 +2051,9 @@ PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } /* TYPE response handler */ -PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +PHP_REDIS_API void +cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { // Make sure we got the right kind of response if (c->reply_type != TYPE_LINE) { @@ -2051,19 +2083,17 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster /* SUBSCRIBE/PSCUBSCRIBE handler */ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { - subscribeContext *sctx = ctx; + subscribeContext *sctx = ctx.ptr; zval z_tab, *z_tmp; int pull = 0; - // Consume each MULTI BULK response (one per channel/pattern) while (sctx->argc--) { if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) ) { - efree(sctx); RETURN_FALSE; } @@ -2071,7 +2101,6 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) != 0 ) { zval_ptr_dtor_nogc(&z_tab); - efree(sctx); RETURN_FALSE; } @@ -2153,27 +2182,27 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * // Cleanup zval_ptr_dtor_nogc(&z_tab); - efree(sctx); // Failure RETURN_FALSE; } /* UNSUBSCRIBE/PUNSUBSCRIBE */ -PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx) +PHP_REDIS_API void +cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { - subscribeContext *sctx = ctx; + subscribeContext *sctx = ctx.ptr; zval z_tab = {0}, *z_chan, *z_flag; int pull = 0, argc = sctx->argc; - efree(sctx); array_init(return_value); // Consume each response while (argc--) { // Fail if we didn't get an array or can't find index 1 - if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) || + if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, + mbulk_resp_loop_raw, &z_tab) || (z_chan = zend_hash_index_find(Z_ARRVAL(z_tab), 1)) == NULL ) { zval_ptr_dtor_nogc(&z_tab); @@ -2247,7 +2276,7 @@ static void cluster_mbulk_variant_resp(clusterReply *r, int null_mbulk_as_null, * where we just map the replies from Redis type values to PHP ones directly. */ static void cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - int status_strings, void *ctx) + int status_strings, RedisCmdCtx ctx) { clusterReply *r; zval zv, *z_arr = &zv; @@ -2336,31 +2365,35 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } PHP_REDIS_API void -cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { +cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ cluster_cb cb; - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); - cb = ctx ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; + cb = ctx.ptr ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); } -PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +PHP_REDIS_API void +cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 0, ctx); } PHP_REDIS_API void cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, c->flags->reply_literal, ctx); } -PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +PHP_REDIS_API void +cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, ctx); } @@ -2368,7 +2401,7 @@ PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, re /* Generic MULTI BULK response processor */ static void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - zend_bool init_array, mbulk_cb cb, void *ctx) + zend_bool init_array, mbulk_cb cb, RedisCmdCtx ctx) { zval z_result = {0}; @@ -2442,16 +2475,20 @@ cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, // Use the proper response callback depending on scan type switch(type) { case TYPE_SCAN: - cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c, + redis_empty_ctx); break; case TYPE_SSCAN: - cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c, + redis_empty_ctx); break; case TYPE_HSCAN: - cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); break; case TYPE_ZSCAN: - cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU,c,NULL); + cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); break; default: return FAILURE; @@ -2463,7 +2500,7 @@ cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* INFO response */ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { zval z_result; char *info; @@ -2488,7 +2525,7 @@ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster /* CLIENT LIST response */ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { char *info; zval z_result; @@ -2512,7 +2549,9 @@ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, redisC /* XRANGE */ PHP_REDIS_API void -cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { +cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ zval z_messages; array_init(&z_messages); @@ -2534,7 +2573,9 @@ cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { /* XREAD */ PHP_REDIS_API void -cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { +cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ zval z_streams; c->cmd_sock->serializer = c->flags->serializer; @@ -2559,14 +2600,18 @@ cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { /* XCLAIM */ PHP_REDIS_API void -cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { +cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ zval z_msg; array_init(&z_msg); - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); - if (redis_read_xclaim_reply(c->cmd_sock, c->reply_len, ctx == PHPREDIS_CTX_PTR, &z_msg) < 0) { + if (redis_read_xclaim_reply(c->cmd_sock, c->reply_len, + ctx.ptr == PHPREDIS_CTX_PTR, &z_msg) < 0) + { zval_ptr_dtor_nogc(&z_msg); CLUSTER_RETURN_FALSE(c); } @@ -2580,7 +2625,9 @@ cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { } PHP_REDIS_API void -cluster_vemb_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { +cluster_vemb_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ zval z_ret; ZVAL_FALSE(&z_ret); @@ -2607,7 +2654,9 @@ fail: } PHP_REDIS_API void -cluster_vinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { +cluster_vinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ zval z_ret; if (c->reply_len < 2 || c->reply_len % 2 != 0) { @@ -2628,7 +2677,8 @@ cluster_vinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { } PHP_REDIS_API void -cluster_vgetattr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_vgetattr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { zval z_ret; char *str; @@ -2639,7 +2689,7 @@ cluster_vgetattr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) CLUSTER_RETURN_FALSE(c); } - if (ctx != PHPREDIS_CTX_PTR || + if (ctx.ptr != PHPREDIS_CTX_PTR || redis_deserialize_vgetattr_reply(&z_ret, str, c->reply_len) != SUCCESS) { ZVAL_STRINGL(&z_ret, str, c->reply_len); @@ -2655,7 +2705,9 @@ cluster_vgetattr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) } PHP_REDIS_API void -cluster_vlinks_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { +cluster_vlinks_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ zval z_ret; if (c->reply_len < 0) { @@ -2677,7 +2729,8 @@ cluster_vlinks_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { /* XINFO */ PHP_REDIS_API void -cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { zval z_ret; @@ -2695,7 +2748,7 @@ cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) /* LMPOP, ZMPOP, BLMPOP, BZMPOP */ PHP_REDIS_API void -cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, RedisCmdCtx ctx) { zval z_ret; @@ -2711,8 +2764,8 @@ cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) } static void -cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx, - int (*cb)(RedisSock*, zval*, long)) +cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx, int (*cb)(RedisSock*, zval*, long)) { zval z_ret; @@ -2729,18 +2782,25 @@ cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx } PHP_REDIS_API void -cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, redis_read_acl_getuser_reply); +cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ + cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, + redis_read_acl_getuser_reply); } PHP_REDIS_API void -cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { - cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, redis_read_acl_log_reply); +cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) +{ + cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx, + redis_read_acl_log_reply); } /* MULTI BULK response loop where we might pull the next one */ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, int pull, mbulk_cb cb, zval *z_ret) + redisCluster *c, int pull, mbulk_cb cb, + zval *z_ret) { ZVAL_NULL(z_ret); @@ -2760,7 +2820,7 @@ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, array_init(z_ret); // Call our callback - if (cb(c->cmd_sock, z_ret, c->reply_len, NULL) == FAILURE) { + if (cb(c->cmd_sock, z_ret, c->reply_len, redis_empty_ctx) == FAILURE) { zval_ptr_dtor_nogc(z_ret); return NULL; } @@ -2770,7 +2830,7 @@ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, /* MULTI MULTI BULK reply (for EXEC) */ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx) + redisCluster *c, RedisCmdCtx ctx) { zval *multi_resp = &c->multi_resp; uint8_t flags = c->flags->flags; @@ -2807,17 +2867,18 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, } /* Generic handler for MGET */ -PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx) +PHP_REDIS_API void +cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { - clusterMultiCtx *mctx = ctx; + clusterMultiCtx *mctx = ctx.ptr; /* Protect against an invalid response type, -1 response length, and failure * to consume the responses. */ c->cmd_sock->serializer = c->flags->serializer; c->cmd_sock->compression = c->flags->compression; short fail = c->reply_type != TYPE_MULTIBULK || c->reply_len == -1 || - mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, NULL) == FAILURE; + mbulk_resp_loop(c->cmd_sock, mctx->z_multi, c->reply_len, redis_empty_ctx) == FAILURE; // If we had a failure, pad results with FALSE to indicate failure. Non // existent keys (e.g. for MGET will come back as NULL) @@ -2835,18 +2896,15 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, add_next_index_zval(&c->multi_resp, mctx->z_multi); } - efree(mctx->z_multi); } - - // Clean up this context item - efree(mctx); } /* Handler for MSETNX */ -PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +PHP_REDIS_API void +cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { - clusterMultiCtx *mctx = ctx; + clusterMultiCtx *mctx = ctx.ptr; int real_argc = mctx->count/2; // Protect against an invalid response type @@ -2871,24 +2929,19 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluste } else { add_next_index_zval(&c->multi_resp, mctx->z_multi); } - efree(mctx->z_multi); } - - // Free multi context - efree(mctx); } /* Handler for DEL */ -PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) +PHP_REDIS_API void +cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, RedisCmdCtx ctx) { - clusterMultiCtx *mctx = ctx; + clusterMultiCtx *mctx = ctx.ptr; // If we get an invalid reply, inform the client if (c->reply_type != TYPE_INT) { php_error_docref(0, E_WARNING, "Invalid reply type returned for DEL command"); - efree(mctx); return; } @@ -2901,17 +2954,14 @@ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * } else { add_next_index_long(&c->multi_resp, Z_LVAL_P(mctx->z_multi)); } - efree(mctx->z_multi); } - - efree(ctx); } /* Handler for MSET */ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { - clusterMultiCtx *mctx = ctx; + clusterMultiCtx *mctx = ctx.ptr; // If we get an invalid reply type something very wrong has happened, // and we have to abort. @@ -2919,8 +2969,6 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster php_error_docref(0, E_ERROR, "Invalid reply type returned for MSET command"); zval_ptr_dtor_nogc(mctx->z_multi); - efree(mctx->z_multi); - efree(mctx); RETURN_FALSE; } @@ -2931,50 +2979,49 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster } else { add_next_index_bool(&c->multi_resp, zend_is_true(mctx->z_multi)); } - efree(mctx->z_multi); } - - efree(mctx); } /* Raw MULTI BULK reply */ PHP_REDIS_API void -cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - 1, mbulk_resp_loop_raw, NULL); + 1, mbulk_resp_loop_raw, redis_empty_ctx); } /* Unserialize all the things */ PHP_REDIS_API void -cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) +cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + RedisCmdCtx ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - 1, mbulk_resp_loop, NULL); + 1, mbulk_resp_loop, redis_empty_ctx); } /* For handling responses where we get key, value, key, value that * we will turn into key => value, key => value. */ PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - 1, mbulk_resp_loop_zipstr, NULL); + 1, mbulk_resp_loop_zipstr, redis_empty_ctx); } /* Handling key,value to key=>value where the values are doubles */ PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, - 1, mbulk_resp_loop_zipdbl, NULL); + 1, mbulk_resp_loop_zipdbl, redis_empty_ctx); } PHP_REDIS_API void cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 1, mbulk_resp_loop_dbl, ctx); @@ -2983,7 +3030,7 @@ cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, /* Associate multi bulk response (for HMGET really) */ PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx) + RedisCmdCtx ctx) { cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, 0, mbulk_resp_loop_assoc, ctx); @@ -2994,7 +3041,7 @@ cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, */ static int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx) + long long count, RedisCmdCtx ctx) { char *line; int line_len; @@ -3014,7 +3061,7 @@ static int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result, /* MULTI BULK response where we don't touch the values (e.g. KEYS) */ static int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx) + long long count, RedisCmdCtx ctx) { char *line; int line_len; @@ -3036,7 +3083,7 @@ static int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result, /* MULTI BULK response where we unserialize everything */ static int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx) + long long count, RedisCmdCtx ctx) { char *line; int line_len; @@ -3061,7 +3108,7 @@ static int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result, /* MULTI BULK response where we turn key1,value1 into key1=>value1 */ static int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx) + long long count, RedisCmdCtx ctx) { char *line, *key = NULL; long long idx = 0; @@ -3097,7 +3144,7 @@ static int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result, /* MULTI BULK loop processor where we expect key,score key, score */ static int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx) + long long count, RedisCmdCtx ctx) { char *line, *key = NULL; int line_len, key_len = 0; @@ -3135,10 +3182,10 @@ static int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result, /* MULTI BULK where we're passed the keys, and we attach vals */ static int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, - long long count, void *ctx) + long long count, RedisCmdCtx ctx) { + HashTable *htctx = ctx.ptr; zval *zfield, z_unpacked; - HashTable *htctx = ctx; int line_len; char *line; @@ -3154,6 +3201,7 @@ static int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result, ZVAL_COPY_VALUE(zfield, &z_unpacked); } ZEND_HASH_FOREACH_END(); + GC_ADDREF(htctx); ZVAL_ARR(z_result, htctx); return SUCCESS; diff --git a/cluster_library.h b/cluster_library.h index 361a1e5d..a3ea4790 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -2,6 +2,7 @@ #define _PHPREDIS_CLUSTER_LIBRARY_H #include "common.h" +#include "redis_cmd.h" #ifdef ZTS #include "TSRM.h" @@ -111,10 +112,10 @@ } /* Macro to clear out a clusterMultiCmd structure */ -#define CLUSTER_MULTI_CLEAR(mc) \ - mc->cmd.len = 0; \ - mc->args.len = 0; \ - mc->argc = 0; \ +#define CLUSTER_MULTI_CLEAR(mc) do { \ + if ((mc)->cmd) redis_cmd_reset((mc)->cmd, (mc)->kw, (mc)->kw_len); \ + (mc)->argc = 0; \ +} while (0) /* Initialize a clusterMultiCmd with a keyword and length */ #define CLUSTER_MULTI_INIT(mc, keyword, keyword_len) \ @@ -131,7 +132,7 @@ typedef enum CLUSTER_REDIR_TYPE { } CLUSTER_REDIR_TYPE; /* MULTI BULK response callback typedef */ -typedef int (*mbulk_cb)(RedisSock*,zval*,long long, void*); +typedef int (*mbulk_cb)(RedisSock*,zval*,long long, RedisCmdCtx); /* A list of covered slot ranges */ typedef struct redisSlotRange { @@ -249,7 +250,7 @@ typedef struct redisCluster { } redisCluster; /* RedisCluster response processing callback */ -typedef void (*cluster_cb)(INTERNAL_FUNCTION_PARAMETERS, redisCluster*, void*); +typedef void (*cluster_cb)(INTERNAL_FUNCTION_PARAMETERS, redisCluster*, RedisCmdCtx); /* Context for processing transactions */ struct clusterFoldItem { @@ -259,8 +260,8 @@ struct clusterFoldItem { /* The actual socket where we send this request */ unsigned short slot; - /* Any context we need to send to our callback */ - void *ctx; + /* Context and possible context destructor */ + RedisCmdCtx ctx; /* Next item in our list */ struct clusterFoldItem *next; @@ -306,9 +307,8 @@ typedef struct clusterMultiCmd { /* Arguments in our payload */ int argc; - /* The full command, built into cmd, and args as we aggregate */ - smart_string cmd; - smart_string args; + /* Command builder */ + RedisCmd *cmd; } clusterMultiCmd; /* Hiredis like structure for processing any sort of reply Redis Cluster might @@ -334,8 +334,7 @@ HashTable *cluster_dist_create(void); void cluster_dist_free(HashTable *ht); int cluster_dist_add_key(redisCluster *c, HashTable *ht, char *key, size_t key_len, clusterKeyVal **kv); -void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val - ); +void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *val); /* Aggregation for multi commands like MGET, MSET, and MSETNX */ void cluster_multi_init(clusterMultiCmd *mc, char *kw, int kw_len); @@ -371,7 +370,7 @@ PHP_REDIS_API int cluster_abort_exec(redisCluster *c); PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host, unsigned short port); -PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd, +PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, const char *cmd, int cmd_len, REDIS_REPLY_TYPE rtype); PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout, @@ -398,104 +397,104 @@ void cluster_cache_clear(redisCluster *c); * Redis Cluster response handlers. Our response handlers generally take the * following form: * PHP_REDIS_API void handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - * void *ctx) + * RedisCmdCtx ctx) * * Reply handlers are responsible for setting the PHP return value (either to * something valid, or FALSE in the case of some failures). */ PHP_REDIS_API void cluster_bool_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_ping_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_pop_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_object_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_lpos_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_hrandfield_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_zdiff_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_randmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_geosearch_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_bulk_withmeta_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_1_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_vemb_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_vinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_vlinks_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_vgetattr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, - void *ctx); + RedisCmdCtx ctx); PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); /* MULTI BULK response functions */ PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, int pull, mbulk_cb cb, zval *z_ret); /* Handlers for things like DEL/MGET/MSET/MSETNX */ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); /* Response handler for ZSCAN, SSCAN, and HSCAN */ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, @@ -503,28 +502,30 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, /* INFO response handler */ PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); /* CLIENT LIST response handler */ PHP_REDIS_API void cluster_client_list_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); /* Custom STREAM handlers */ PHP_REDIS_API void cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); PHP_REDIS_API void cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS, - redisCluster *c, void *ctx); + redisCluster *c, RedisCmdCtx ctx); /* Custom ACL handlers */ -PHP_REDIS_API void cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); -PHP_REDIS_API void cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, RedisCmdCtx ctx); +PHP_REDIS_API void cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, + redisCluster *c, RedisCmdCtx ctx); #endif diff --git a/common.h b/common.h index 27e79d60..7b1c3691 100644 --- a/common.h +++ b/common.h @@ -200,6 +200,8 @@ typedef enum { Z_PARAM_ZVAL_EX(dest, 1, 0) #define Z_PARAM_BOOL_OR_NULL(dest, is_null) \ Z_PARAM_BOOL_EX(dest, is_null, 1, 0) +#define Z_PARAM_LONG_OR_NULL(dest, is_null) \ + Z_PARAM_LONG_EX(dest, is_null, 1, 0) #endif #if PHPREDIS_DEBUG_LOGGING == 1 @@ -262,8 +264,17 @@ typedef struct RedisHello { zend_string *version; } RedisHello; +typedef enum RedisSockType { + REDIS_SOCK_STANDALONE, + REDIS_SOCK_ARRAY, + REDIS_SOCK_CLUSTER, + REDIS_SOCK_SENTINEL, + REDIS_SOCK_SESSION +} RedisSockType; + /* {{{ struct RedisSock */ typedef struct { + RedisSockType type; php_stream *stream; zend_string *host; int port; @@ -307,16 +318,27 @@ typedef struct { } RedisSock; /* }}} */ +typedef void (RedisCmdCtxDtor)(void *ptr); +typedef struct RedisCmdCtx { + void *ptr; + RedisCmdCtxDtor *dtor; +} RedisCmdCtx; + /* Redis response handler function callback prototype */ typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock*, zval*, RedisCmdCtx); typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, - RedisSock*, zval*, void*); + RedisSock*, zval*, RedisCmdCtx); + +static zend_always_inline void redis_cmd_ctx_free(RedisCmdCtx ctx) { + if (ctx.dtor) + ctx.dtor(ctx.ptr); +} typedef struct fold_item { FailableResultCallback fun; + RedisCmdCtx ctx; uint8_t flags; - void *ctx; } fold_item; typedef struct { diff --git a/config.m4 b/config.m4 index c84ce1e9..1b49e7bb 100644 --- a/config.m4 +++ b/config.m4 @@ -325,5 +325,5 @@ if test "$PHP_REDIS" != "no"; then fi PHP_SUBST(REDIS_SHARED_LIBADD) - PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c $lzf_sources, $ext_shared) + PHP_NEW_EXTENSION(redis, redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c redis_cmd.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c $lzf_sources, $ext_shared) fi diff --git a/config.w32 b/config.w32 index 751bf73d..db5b0cae 100644 --- a/config.w32 +++ b/config.w32 @@ -5,7 +5,7 @@ ARG_ENABLE("redis-session", "whether to enable sessions", "yes"); ARG_ENABLE("redis-igbinary", "whether to enable igbinary serializer support", "no"); if (PHP_REDIS != "no") { - var sources = "redis.c redis_commands.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c"; + var sources = "redis.c redis_commands.c redis_cmd.c library.c redis_session.c redis_array.c redis_array_impl.c redis_cluster.c cluster_library.c redis_sentinel.c sentinel_library.c backoff.c"; if (PHP_REDIS_SESSION != "no") { ADD_EXTENSION_DEP("redis", "session"); ADD_FLAG("CFLAGS_REDIS", ' /D PHP_SESSION=1 '); diff --git a/library.c b/library.c index 2c036be3..cbbb84eb 100644 --- a/library.c +++ b/library.c @@ -123,6 +123,8 @@ extern zend_class_entry *redis_exception_ce; extern int le_redis_pconnect; +RedisCmdCtx redis_empty_ctx = {0}; + static int redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int count); static int redis_bulk_resp_to_zval(RedisSock *redis_sock, zval *zdst, @@ -173,17 +175,18 @@ static int redis_sock_response_ok(RedisSock *redis_sock, char *buf, int buf_size /* Helper to select the proper DB number */ static int redis_select_db(RedisSock *redis_sock) { char response[4096]; - smart_string cmd = {0}; + RedisCmd *cmd; - REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1, "SELECT"); - redis_cmd_append_sstr_long(&cmd, redis_sock->dbNumber); + cmd = redis_cmd_create_literal(redis_sock, "SELECT"); - if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) { - efree(cmd.c); + redis_cmd_cat_long(cmd, redis_sock->dbNumber); + + if (redis_sock_write(redis_sock, redis_cmd_str(cmd), redis_cmd_len(cmd)) < 0) { + redis_cmd_free(cmd); return -1; } - efree(cmd.c); + redis_cmd_free(cmd); if (!redis_sock_response_ok(redis_sock, response, sizeof(response))) { return -1; @@ -196,17 +199,21 @@ static int redis_select_db(RedisSock *redis_sock) { * append the new style AUTH , old style AUTH , or * append no command at all. Function returns 1 if we appended a command * and 0 otherwise. */ -static int redis_sock_append_auth(RedisSock *redis_sock, smart_string *str) { +static int redis_sock_append_auth(RedisSock *redis_sock, smart_str *str) { + int argc; + /* We need a password at least */ if (redis_sock->pass == NULL) return 0; - REDIS_CMD_INIT_SSTR_STATIC(str, !!redis_sock->user + !!redis_sock->pass, "AUTH"); + argc = !!redis_sock->user + !!redis_sock->pass; + + resp_str_cat_cmd_literal(str, "AUTH", argc); if (redis_sock->user) - redis_cmd_append_sstr_zstr(str, redis_sock->user); + resp_str_cat_zstr(str, redis_sock->user); - redis_cmd_append_sstr_zstr(str, redis_sock->pass); + resp_str_cat_zstr(str, redis_sock->pass); /* We appended a command */ return 1; @@ -274,35 +281,36 @@ void redis_sock_free_context(RedisSock *redis_sock) { redis_sock->context = NULL; } -PHP_REDIS_API char * -redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen) { - smart_string cmd = {0}; +PHP_REDIS_API zend_string * +redis_sock_auth_cmd(RedisSock *redis_sock) { + smart_str cmd = {0}; if (redis_sock_append_auth(redis_sock, &cmd) == 0) { return NULL; } - *cmdlen = cmd.len; - return cmd.c; + return cmd.s; } /* Send Redis AUTH and process response */ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) { - char *cmd, inbuf[4096]; - int cmdlen; + zend_string *cmd; + char inbuf[4096]; - if ((cmd = redis_sock_auth_cmd(redis_sock, &cmdlen)) == NULL) + if ((cmd = redis_sock_auth_cmd(redis_sock)) == NULL) return SUCCESS; - if (redis_sock_write(redis_sock, cmd, cmdlen) < 0) { - efree(cmd); + if (redis_sock_write(redis_sock, ZSTR_VAL(cmd), ZSTR_LEN(cmd)) < 0) { + zend_string_release(cmd); return FAILURE; } - efree(cmd); + + zend_string_release(cmd); if (!redis_sock_response_ok(redis_sock, inbuf, sizeof(inbuf))) { return FAILURE; } + return SUCCESS; } @@ -465,8 +473,9 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, uint64_t *cursor) { REDIS_REPLY_TYPE reply_type; - long reply_info; char err[4096], *p_iter; + RedisCmdCtx ctx = {0}; + long reply_info; size_t errlen; /* Our response should have two multibulk replies */ @@ -503,16 +512,16 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, switch(type) { case TYPE_SCAN: return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL); + redis_sock, NULL, ctx); case TYPE_SSCAN: return redis_sock_read_multibulk_reply( - INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); case TYPE_ZSCAN: return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL); + redis_sock, NULL, ctx); case TYPE_HSCAN: return redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL); + redis_sock, NULL, ctx); default: return -1; } @@ -520,14 +529,17 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHP_REDIS_API int redis_pubsub_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -540,13 +552,13 @@ ht_free_subs(zval *data) efree(Z_PTR_P(data)); } -PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, - void *ctx) +PHP_REDIS_API int +redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { HashTable *subs; subscribeCallback *cb; - subscribeContext *sctx = ctx; + subscribeContext *sctx = ctx.ptr; zval *z_tmp, z_resp; int i; @@ -587,8 +599,6 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, i = REDIS_SUBSCRIBE_IDX; } - efree(sctx); - if (redis_sock->subs[i]) { zend_string *zkey; @@ -687,7 +697,6 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, // This is an error state, clean up error: - efree(sctx); zend_hash_destroy(subs); efree(subs); failure: @@ -698,9 +707,9 @@ failure: PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, - void *ctx) + RedisCmdCtx ctx) { - subscribeContext *sctx = ctx; + subscribeContext *sctx = ctx.ptr; zval *z_chan, z_ret, z_resp; int i; @@ -722,7 +731,6 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp) || (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL ) { - efree(sctx); zval_ptr_dtor_nogc(&z_resp); zval_ptr_dtor_nogc(&z_ret); RETVAL_FALSE; @@ -741,8 +749,6 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, zval_ptr_dtor_nogc(&z_resp); } - efree(sctx); - if (redis_sock->subs[i] && !zend_hash_num_elements(redis_sock->subs[i])) { zend_hash_destroy(redis_sock->subs[i]); efree(redis_sock->subs[i]); @@ -750,6 +756,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, } RETVAL_ZVAL(&z_ret, 0, 1); + return SUCCESS; } @@ -883,17 +890,6 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len) return NULL; } -/* A simple union to store the various arg types we might handle in our - * redis_spprintf command formatting function */ -union resparg { - char *str; - zend_string *zstr; - zval *zv; - int ival; - long lval; - double dval; -}; - static zend_string *redis_hash_auth(zend_string *user, zend_string *pass) { zend_string *algo, *hex; smart_str salted = {0}; @@ -1002,270 +998,10 @@ redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...) { return str.s; } -/* A printf like method to construct a Redis RESP command. It has been extended - * to take a few different format specifiers that are convenient to phpredis. - * - * s - C string followed by length as a - * S - Pointer to a zend_string - * k - Same as 's' but the value will be prefixed if phpredis is set up do do - * that and the working slot will be set if it has been passed. - * K - Same as 'S' but the value will be prefixed if phpredis is set up to do - * v - A z_val which will be serialized if phpredis is configured to serialize. - * f - A double value - * F - Alias to 'f' - * i - An integer - * d - Alias to 'i' - * l - A long - * L - Alias to 'l' - */ PHP_REDIS_API int -redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...) { - smart_string cmd = {0}; - va_list ap; - union resparg arg; - char *dup; - int argfree; - size_t arglen; - - va_start(ap, fmt); - - /* Header */ - redis_cmd_init_sstr(&cmd, strlen(fmt), kw, strlen(kw)); - - while (*fmt) { - switch (*fmt) { - case 's': - arg.str = va_arg(ap, char*); - arglen = va_arg(ap, size_t); - redis_cmd_append_sstr(&cmd, arg.str, arglen); - break; - case 'S': - arg.zstr = va_arg(ap, zend_string*); - redis_cmd_append_sstr(&cmd, ZSTR_VAL(arg.zstr), ZSTR_LEN(arg.zstr)); - break; - case 'k': - arg.str = va_arg(ap, char*); - arglen = va_arg(ap, size_t); - argfree = redis_key_prefix(redis_sock, &arg.str, &arglen); - redis_cmd_append_sstr(&cmd, arg.str, arglen); - if (slot) *slot = cluster_hash_key(arg.str, arglen); - if (argfree) efree(arg.str); - break; - case 'K': - arg.zstr = va_arg(ap, zend_string*); - redis_cmd_append_sstr_key_zstr(&cmd, arg.zstr, redis_sock, slot); - break; - case 'v': - arg.zv = va_arg(ap, zval*); - argfree = redis_pack(redis_sock, arg.zv, &dup, &arglen); - redis_cmd_append_sstr(&cmd, dup, arglen); - if (argfree) efree(dup); - break; - case 'f': - case 'F': - arg.dval = va_arg(ap, double); - redis_cmd_append_sstr_dbl(&cmd, arg.dval); - break; - case 'i': - case 'd': - arg.ival = va_arg(ap, int); - redis_cmd_append_sstr_int(&cmd, arg.ival); - break; - case 'l': - case 'L': - arg.lval = va_arg(ap, long); - redis_cmd_append_sstr_long(&cmd, arg.lval); - break; - } - - fmt++; - } - /* varargs cleanup */ - va_end(ap); - - /* Null terminate */ - smart_string_0(&cmd); - - /* Push command string, return length */ - *ret = cmd.c; - return cmd.len; -} - -/* - * Given a smart string, number of arguments, a keyword, and the length of the keyword - * initialize our smart string with the proper Redis header for the command to follow - */ -int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len) { - smart_string_appendc(str, '*'); - smart_string_append_long(str, num_args + 1); - smart_string_appendl(str, _NL, sizeof(_NL) -1); - smart_string_appendc(str, '$'); - smart_string_append_long(str, keyword_len); - smart_string_appendl(str, _NL, sizeof(_NL) - 1); - smart_string_appendl(str, keyword, keyword_len); - smart_string_appendl(str, _NL, sizeof(_NL) - 1); - return str->len; -} - -/* - * Append a command sequence to a smart_string - */ -int redis_cmd_append_sstr(smart_string *str, char *append, int append_len) { - smart_string_appendc(str, '$'); - smart_string_append_long(str, append_len); - smart_string_appendl(str, _NL, sizeof(_NL) - 1); - smart_string_appendl(str, append, append_len); - smart_string_appendl(str, _NL, sizeof(_NL) - 1); - - /* Return our new length */ - return str->len; -} - -/* - * Append an integer to a smart string command - */ -int redis_cmd_append_sstr_int(smart_string *str, int append) { - return redis_cmd_append_sstr_long(str, (long) append); -} - -/* - * Append a long to a smart string command - */ -int redis_cmd_append_sstr_long(smart_string *str, long append) { - return redis_cmd_append_sstr_zend_long(str, (zend_long) append); -} - -/* - * Append a zend_long to a smart string command - */ -int redis_cmd_append_sstr_zend_long(smart_string *str, zend_long lval) { - char long_buf[32]; - char *result = zend_print_long_to_buf(long_buf + sizeof(long_buf) - 1, lval); - int int_len = long_buf + sizeof(long_buf) - 1 - result; - return redis_cmd_append_sstr(str, result, int_len); -} - -/* - * Append a 64-bit integer to our command - */ -int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) { - char nbuf[21]; - int len = snprintf(nbuf, sizeof(nbuf), "%" PRId64, append); - return redis_cmd_append_sstr(str, nbuf, len); -} - -/* - * Append a 64-bit unsigned integer to our command - */ -int redis_cmd_append_sstr_u64(smart_string *str, uint64_t append) { - char nbuf[21]; - int len = snprintf(nbuf, sizeof(nbuf), "%" PRIu64, append); - return redis_cmd_append_sstr(str, nbuf, len); -} - -/* - * Append a double to a smart string command - */ -int -redis_cmd_append_sstr_dbl(smart_string *str, double value) +redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - char tmp[64], *p; - int len; - - /* Convert to string */ - len = snprintf(tmp, sizeof(tmp), "%.17g", value); - - /* snprintf depends on locale, replace comma with point */ - if ((p = strchr(tmp, ',')) != NULL) *p = '.'; - - // Append the string - return redis_cmd_append_sstr(str, tmp, len); -} - -/* Append a zval to a redis command. If redis_sock is passed as non-null we will - * the value may be serialized, if we're configured to do that. */ -int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock) { - int valfree, retval; - zend_string *zstr, *tmp; - size_t vallen; - char *val; - - if (redis_sock != NULL) { - valfree = redis_pack(redis_sock, z, &val, &vallen); - retval = redis_cmd_append_sstr(str, val, vallen); - if (valfree) efree(val); - } else { - zstr = zval_get_tmp_string(z, &tmp); - retval = redis_cmd_append_sstr(str, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_tmp_string_release(tmp); - } - - return retval; -} - -int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr) { - return redis_cmd_append_sstr(str, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); -} - -/* Append a string key to a redis command. This function takes care of prefixing the key - * for the caller and setting the slot argument if it is passed non null */ -int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot) { - int valfree, retval; - - valfree = redis_key_prefix(redis_sock, &key, &len); - if (slot) *slot = cluster_hash_key(key, len); - retval = redis_cmd_append_sstr(str, key, len); - if (valfree) efree(key); - - return retval; -} - -int redis_cmd_append_sstr_key_zstr(smart_string *dst, zend_string *key, RedisSock *redis_sock, short *slot) { - return redis_cmd_append_sstr_key(dst, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); -} - -int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot) { - zend_string *key, *tmp; - int res; - - key = zval_get_tmp_string(zv, &tmp); - res = redis_cmd_append_sstr_key(dst, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); - zend_tmp_string_release(tmp); - - return res; -} - -int redis_cmd_append_sstr_key_long(smart_string *dst, zend_long lval, RedisSock *redis_sock, short *slot) { - char buf[64]; - size_t len; - int res; - - len = snprintf(buf, sizeof(buf), ZEND_LONG_FMT, lval); - res = redis_cmd_append_sstr_key(dst, buf, len, redis_sock, slot); - - return res; -} - -/* Append an array key to a redis smart string command. This function - * handles the boilerplate conditionals around string or integer keys */ -int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx) -{ - char *arg, kbuf[128]; - int len; - - if (kstr) { - len = ZSTR_LEN(kstr); - arg = ZSTR_VAL(kstr); - } else { - len = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); - arg = (char*)kbuf; - } - - return redis_cmd_append_sstr(cmd, arg, len); -} - -PHP_REDIS_API int -redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; @@ -1287,7 +1023,10 @@ redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +PHP_REDIS_API int +redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ char *response; int response_len; long l; @@ -1326,41 +1065,50 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r } PHP_REDIS_API int -redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - FailableResultCallback cb = ctx; +redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ + FailableResultCallback cb = ctx.ptr; - ZEND_ASSERT(cb == redis_boolean_response || cb == redis_mbulk_reply_zipped_raw); + ZEND_ASSERT(cb == redis_boolean_response || + cb == redis_mbulk_reply_zipped_raw); return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } PHP_REDIS_API int -redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ FailableResultCallback cb; /* Whether or not we have WITHSCORES */ - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); - cb = ctx ? redis_mbulk_reply_zipped_keys_dbl : redis_sock_read_multibulk_reply; + cb = ctx.ptr ? redis_mbulk_reply_zipped_keys_dbl : + redis_sock_read_multibulk_reply; return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } PHP_REDIS_API int redis_randmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { FailableResultCallback cb; /* Whether or not we have a COUNT argument */ - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); - cb = ctx ? redis_sock_read_multibulk_reply : redis_string_response; + cb = ctx.ptr ? redis_sock_read_multibulk_reply : redis_string_response; return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } -PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +PHP_REDIS_API int +redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ char *response; int response_len; zval z_ret; @@ -1451,8 +1199,9 @@ redis_parse_client_info(char *info, zval *z_ret) } } -static int -redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +int +redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { char *resp; int resp_len; @@ -1481,7 +1230,9 @@ redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva * to handle. */ PHP_REDIS_API int -redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ char *resp; int resp_len; zval z_ret; @@ -1523,26 +1274,31 @@ redis_parse_client_list_response(char *response, zval *z_ret) } PHP_REDIS_API int -redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { FailableResultCallback cb; - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); - cb = ctx ? redis_bulk_double_response : redis_long_response; + cb = ctx.ptr ? redis_bulk_double_response : redis_long_response; - return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, redis_empty_ctx); } PHP_REDIS_API int -redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -1550,12 +1306,15 @@ redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } PHP_REDIS_API int -redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -1563,12 +1322,15 @@ redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * } PHP_REDIS_API int -redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -1576,14 +1338,18 @@ redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ } PHP_REDIS_API int -redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -1591,12 +1357,15 @@ redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, z } PHP_REDIS_API int -redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -1604,25 +1373,29 @@ redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ } PHP_REDIS_API int -redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); +redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ + ZEND_ASSERT(ctx.ptr == PHPREDIS_CTX_PTR || ctx.ptr == PHPREDIS_CTX_PTR + 1); - if (ctx == PHPREDIS_CTX_PTR) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, redis_empty_ctx); } else { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } } PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, - long long elements, void *ctx) + long long elements, RedisCmdCtx ctx) { char inbuf[4096]; size_t len; int i; - if (ctx == NULL) { + if (ctx.ptr == NULL) { if (reply_type != TYPE_INT && reply_type != TYPE_BULK) return FAILURE; @@ -1631,7 +1404,7 @@ redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, } else { REDIS_ZVAL_NULL(redis_sock, zdst); } - } else if (ctx == PHPREDIS_CTX_PTR) { + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { if (reply_type != TYPE_MULTIBULK) return FAILURE; @@ -1654,7 +1427,8 @@ redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, PHP_REDIS_API int -redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { char inbuf[1024] = {0}; int res = SUCCESS; @@ -1675,19 +1449,20 @@ redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z } PHP_REDIS_API int -redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, - void *ctx) +redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL) < 0) + if (redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, redis_empty_ctx) < 0) return FAILURE; - redis_sock->dbNumber = (long)(uintptr_t)ctx; + redis_sock->dbNumber = (long)(uintptr_t)ctx.ptr; return SUCCESS; } PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx, + zval *z_tab, RedisCmdCtx ctx, SuccessCallback success_callback) { @@ -1713,22 +1488,24 @@ redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, - void *ctx) + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, ctx, NULL); + z_tab, redis_empty_ctx, NULL); } PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * z_tab, - void *ctx) + RedisCmdCtx ctx) { char *response; int response_len; - if ((response = redis_sock_read(redis_sock, &response_len)) == NULL || *response != TYPE_INT) { + if ((response = redis_sock_read(redis_sock, &response_len)) == NULL || + *response != TYPE_INT) + { REDIS_RESPONSE_ERROR(redis_sock, z_tab); if (response) efree(response); return FAILURE; @@ -1898,13 +1675,13 @@ geosearch_cast(zval *zv) PHP_REDIS_API int redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, - void *ctx) + RedisCmdCtx ctx) { int subele, keylen; zval zele = {0}; char *key; - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); if (elements < 0) { REDIS_ZVAL_NULL(redis_sock, zdst); @@ -1929,7 +1706,7 @@ redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, array_init_size(&zele, elements); - if (ctx == PHPREDIS_CTX_PTR) { + if (ctx.ptr == PHPREDIS_CTX_PTR) { int i; for (i = 0; i < elements; i++) { if (read_mbulk_header(redis_sock, &subele) < 0 || subele != 2) { @@ -1957,7 +1734,7 @@ fail: PHP_REDIS_API int redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { int elements, res = SUCCESS; zval zret = {0}; @@ -2038,13 +1815,14 @@ redis_read_geosearch_response(zval *zdst, RedisSock *redis_sock, PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval zret = {0}; int elements; if (read_mbulk_header(redis_sock, &elements) < 0 || - redis_read_geosearch_response(&zret, redis_sock, elements, ctx != NULL) < 0) + redis_read_geosearch_response(&zret, redis_sock, elements, + ctx.ptr != NULL) < 0) { ZVAL_FALSE(&zret); } @@ -2054,8 +1832,9 @@ redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -static int -redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +int redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { int numElems; zval z_ret; @@ -2075,20 +1854,27 @@ redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s } PHP_REDIS_API int -redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_client_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 2) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 3) { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 4) { - return redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_client_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 2) { + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 3) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 4) { + return redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -2096,8 +1882,8 @@ redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval } static int -redis_hello_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_hello_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { zval z_ret, *zv; int numElems; @@ -2133,9 +1919,9 @@ redis_hello_response(INTERNAL_FUNCTION_PARAMETERS, zval_ptr_dtor_nogc(&z_ret); - if (ctx == PHPREDIS_CTX_PTR) { + if (ctx.ptr == PHPREDIS_CTX_PTR) { ZVAL_STR_COPY(&z_ret, redis_sock->hello.server); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { ZVAL_STR_COPY(&z_ret, redis_sock->hello.version); } else { ZEND_ASSERT(!"memory corruption?"); @@ -2161,23 +1947,29 @@ fail: PHP_REDIS_API int -redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { + static RedisCmdCtx hello_ctx = { .ptr = PHPREDIS_CTX_PTR }; + return redis_hello_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, PHPREDIS_CTX_PTR); + z_tab, hello_ctx); } PHP_REDIS_API int redis_hello_version_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { + static RedisCmdCtx hello_ctx = { .ptr = PHPREDIS_CTX_PTR + 1 }; + return redis_hello_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, PHPREDIS_CTX_PTR + 1); + z_tab, hello_ctx); } -static int -redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +int +redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { int numElems; zval z_ret; @@ -2196,24 +1988,28 @@ redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * return SUCCESS; } - PHP_REDIS_API int -redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return redis_function_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return redis_function_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; } } -static int -redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +int +redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { int numElems; zval z_ret; @@ -2231,14 +2027,21 @@ redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv } PHP_REDIS_API int -redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_command_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_command_info_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 2) { + return redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -2248,9 +2051,7 @@ redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval /* Helper function to consume Redis stream message data. This is useful for * multiple stream callers (e.g. XREAD[GROUP], and X[REV]RANGE handlers). */ PHP_REDIS_API int -redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret - ) -{ +redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret) { zval z_message; int i, mhdr, fields; char *id = NULL; @@ -2285,7 +2086,7 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval z_messages; int messages; @@ -2306,8 +2107,8 @@ redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } PHP_REDIS_API int -redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_streams - ) +redis_read_stream_messages_multi(RedisSock *redis_sock, int count, + zval *z_streams) { zval z_messages; int i, shdr, messages; @@ -2341,7 +2142,7 @@ failure: PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval z_rv; int streams; @@ -2413,7 +2214,9 @@ redis_read_xclaim_ids(RedisSock *redis_sock, int count, zval *rv) { /* Read an X[AUTO]CLAIM reply having already consumed the reply-type byte. */ PHP_REDIS_API int -redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv) { +redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, + zval *rv) +{ REDIS_REPLY_TYPE type; zval z_msgs = {0}; char *id = NULL; @@ -2477,17 +2280,17 @@ failure: PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval z_ret = {0}; int count; - ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + ZEND_ASSERT(ctx.ptr == NULL || ctx.ptr == PHPREDIS_CTX_PTR); if (read_mbulk_header(redis_sock, &count) < 0) goto failure; - if (redis_read_xclaim_reply(redis_sock, count, ctx == PHPREDIS_CTX_PTR, &z_ret) < 0) + if (redis_read_xclaim_reply(redis_sock, count, ctx.ptr == PHPREDIS_CTX_PTR, &z_ret) < 0) goto failure; REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); @@ -2608,7 +2411,7 @@ redis_read_vinfo_response(RedisSock *redis_sock, zval *z_ret, long long count) { PHP_REDIS_API int redis_vinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval z_ret; int count; @@ -2678,7 +2481,7 @@ redis_read_vemb_response(RedisSock *redis_sock, zval *z_ret, long long count) { PHP_REDIS_API int redis_vemb_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval z_ret; int count; @@ -2703,7 +2506,7 @@ redis_vemb_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHP_REDIS_API int redis_read_vlinks_response(RedisSock *redis_sock, zval *z_ret, - long long elements, void *ctx) + long long elements, RedisCmdCtx ctx) { long long i; zval z_ele; @@ -2718,7 +2521,7 @@ redis_read_vlinks_response(RedisSock *redis_sock, zval *z_ret, array_init(&z_ele); redis_mbulk_reply_loop(redis_sock, &z_ele, links, UNSERIALIZE_KEYS); - if (ctx == PHPREDIS_CTX_PTR) { + if (ctx.ptr == PHPREDIS_CTX_PTR) { array_zip_values_and_scores(redis_sock, &z_ele, SCORE_DECODE_DOUBLE); } @@ -2730,7 +2533,7 @@ redis_read_vlinks_response(RedisSock *redis_sock, zval *z_ret, PHP_REDIS_API int redis_vlinks_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { int elements; zval z_ret; @@ -2786,7 +2589,7 @@ redis_deserialize_vgetattr_reply(zval *zret, const char *str, size_t len) { PHP_REDIS_API int redis_vgetattr_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval z_ret; char *attr; @@ -2800,7 +2603,7 @@ redis_vgetattr_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (len == 0) { ZVAL_FALSE(&z_ret); - } else if (ctx == PHPREDIS_CTX_PTR) { + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { if (redis_deserialize_vgetattr_reply(&z_ret, attr, len) != SUCCESS) { ZVAL_STRINGL(&z_ret, attr, len); } @@ -2817,7 +2620,7 @@ redis_vgetattr_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval z_ret; int elements; @@ -2836,20 +2639,27 @@ redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } PHP_REDIS_API int -redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - if (ctx == NULL) { - return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR) { - return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 1) { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 2) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 3) { - return redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); - } else if (ctx == PHPREDIS_CTX_PTR + 4) { - return redis_acl_log_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); + if (ctx.ptr == NULL) { + return redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR) { + return redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 1) { + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 2) { + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 3) { + return redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); + } else if (ctx.ptr == PHPREDIS_CTX_PTR + 4) { + return redis_acl_log_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, redis_empty_ctx); } else { ZEND_ASSERT(!"memory corruption?"); return FAILURE; @@ -2910,8 +2720,10 @@ redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long count) { return SUCCESS; } -int redis_acl_custom_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, - int (*cb)(RedisSock*, zval*, long)) { +int redis_acl_custom_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx, + int (*cb)(RedisSock*, zval*, long)) +{ REDIS_REPLY_TYPE type; int res = FAILURE; zval zret; @@ -2934,49 +2746,67 @@ int redis_acl_custom_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return res; } -int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, - redis_read_acl_getuser_reply); +int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ + return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, ctx, redis_read_acl_getuser_reply); } -int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, - redis_read_acl_log_reply); +int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) +{ + return redis_acl_custom_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, ctx, redis_read_acl_log_reply); } /* Zipped key => value reply but we don't touch anything (e.g. CONFIG GET) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int +redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE); + z_tab, UNSERIALIZE_NONE, SCORE_DECODE_NONE); } /* Zipped key => value reply unserializing keys and decoding the score as an integer (PUBSUB) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +PHP_REDIS_API int +redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { - return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_INT); + return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, UNSERIALIZE_KEYS, + SCORE_DECODE_INT); } -/* Zipped key => value reply unserializing keys and decoding the score as a double (ZSET commands) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +/* Zipped key => value reply unserializing keys and decoding the score as a + * double (ZSET commands) */ +PHP_REDIS_API int +redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { - return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, UNSERIALIZE_KEYS, SCORE_DECODE_DOUBLE); + return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, UNSERIALIZE_KEYS, + SCORE_DECODE_DOUBLE); } -/* Zipped key => value reply where only the values are unserialized (e.g. HMGET) */ -PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +/* Zipped key => value reply where only the values are unserialized (e.g. + * HMGET) */ +PHP_REDIS_API int +redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { return redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); + z_tab, UNSERIALIZE_VALS, SCORE_DECODE_NONE); } PHP_REDIS_API int -redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { char *response; int response_len; @@ -3016,7 +2846,7 @@ static int redis_bulk_resp_to_zval(RedisSock *redis_sock, zval *zdst, int *dstle PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { zval zret; int ret; @@ -3029,8 +2859,9 @@ redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } PHP_REDIS_API int -redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { zval zret, zbulk; int len, ret; @@ -3051,7 +2882,7 @@ redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock /* like string response, but never unserialized. */ PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { char *response; @@ -3163,14 +2994,14 @@ redis_sock_configure(RedisSock *redis_sock, HashTable *opts) * redis_sock_create */ PHP_REDIS_API RedisSock* -redis_sock_create(char *host, int host_len, int port, - double timeout, double read_timeout, - int persistent, char *persistent_id, - long retry_interval) +redis_sock_create(RedisSockType type, const char *host, int host_len, int port, + double timeout, double read_timeout, int persistent, + const char *persistent_id, long retry_interval) { RedisSock *redis_sock; redis_sock = ecalloc(1, sizeof(RedisSock)); + redis_sock->type = type; redis_sock->host = zend_string_init(host, host_len, 0); redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->retry_interval = retry_interval * 1000; @@ -3309,8 +3140,8 @@ static int redis_sock_check_liveness(RedisSock *redis_sock) { char id[64], inbuf[256] = {0}; + smart_str cmd = {0}; int idlen, auth; - smart_string cmd = {0}; size_t len; /* Short circuit if PHP detects the stream isn't live */ @@ -3332,15 +3163,17 @@ redis_sock_check_liveness(RedisSock *redis_sock) /* ECHO challenge/response */ idlen = redis_uniqid(id, sizeof(id)); - REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1, "ECHO"); - redis_cmd_append_sstr(&cmd, id, idlen); + resp_str_cat_cmd_literal(&cmd, "ECHO", 1); + + resp_str_cat_str(&cmd, id, idlen); /* Send command(s) and make sure we can consume reply(ies) */ - if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) { - smart_string_free(&cmd); + if (redis_sock_write(redis_sock, ZSTR_VAL(cmd.s), ZSTR_LEN(cmd.s)) < 0) { + smart_str_free(&cmd); goto failure; } - smart_string_free(&cmd); + + smart_str_free(&cmd); if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { goto failure; @@ -3710,7 +3543,7 @@ redis_sock_set_backoff(RedisSock *redis_sock, zval *options) */ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, - void *ctx) + RedisCmdCtx ctx) { zval z_multi_result; int numElems; @@ -3736,7 +3569,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, /* Like multibulk reply, but don't touch the values, they won't be unserialized * (this is used by HKEYS). */ PHP_REDIS_API int -redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { int numElems; @@ -3744,6 +3578,7 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } + zval z_multi_result; if (numElems < 1) { @@ -3759,7 +3594,8 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval } PHP_REDIS_API int -redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { char *line; int i, numElems, len; @@ -3878,27 +3714,27 @@ redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int coun * keys with their returned values */ PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { - HashTable *htctx; + HashTable *htctx = ctx.ptr; int numElems; zval *zfield; char *rresp; int len; - htctx = ctx; + ZEND_ASSERT(htctx != NULL); if (read_mbulk_header(redis_sock, &numElems) < 0 || zend_hash_num_elements(htctx) != numElems) { - zend_hash_destroy(htctx); - FREE_HASHTABLE(htctx); REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } zval z_multi_result, zunpacked; + GC_ADDREF(htctx); + ZEND_HASH_FOREACH_VAL(htctx, zfield) { rresp = redis_sock_read(redis_sock, &len); @@ -3922,7 +3758,7 @@ redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * redis_sock_write */ PHP_REDIS_API int -redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz) +redis_sock_write(RedisSock *redis_sock, const char *cmd, size_t sz) { if (redis_check_eof(redis_sock, 0, 0) == 0 && redis_sock_write_raw(redis_sock, cmd, sz) == sz) @@ -3953,6 +3789,12 @@ void redis_free_reply_callbacks(RedisSock *redis_sock) { if (redis_sock->reply_callback != NULL) { + size_t i; + + for (i = 0; i < redis_sock->reply_callback_count; i++) { + redis_cmd_ctx_free(redis_sock->reply_callback[i].ctx); + } + efree(redis_sock->reply_callback); redis_sock->reply_callback = NULL; redis_sock->reply_callback_count = 0; @@ -4743,7 +4585,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int st static int variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int status_strings, int null_mbulk_as_null, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { // Reply type, and reply size vars REDIS_REPLY_TYPE reply_type; @@ -4792,8 +4634,9 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } PHP_REDIS_API int -redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) +redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx) { return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, redis_sock->reply_literal, @@ -4803,7 +4646,7 @@ redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0, redis_sock->null_mbulk_as_null, z_tab, ctx); @@ -4811,9 +4654,10 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) + zval *z_tab, RedisCmdCtx ctx) { - return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, 0, z_tab, ctx); + return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + 1, 0, z_tab, ctx); } /* The user may wish to send us something like [NULL, 'password'] or @@ -5022,4 +4866,9 @@ uint64_t redisGetScanCursor(zval *zv, zend_bool *was_zero) { } } +void redis_cmd_ctx_efree_dtor(void *ptr) { + efree(ptr); +} + + /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/library.h b/library.h index 39227c62..fdb3b14a 100644 --- a/library.h +++ b/library.h @@ -2,10 +2,7 @@ #define REDIS_LIBRARY_H #include "php_redis.h" - -/* Non cluster command helper */ -#define REDIS_SPPRINTF(ret, kw, fmt, ...) \ - redis_spprintf(redis_sock, NULL, ret, kw, fmt, ##__VA_ARGS__) +#include "redis_cmd.h" #define REDIS_CMD_APPEND_SSTR_STATIC(sstr, str) \ redis_cmd_append_sstr(sstr, str, sizeof(str)-1); @@ -72,65 +69,50 @@ void redis_free_reply_callbacks(RedisSock *redis_sock); PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass); PHP_REDIS_API void redis_with_metadata(zval *zdst, zval *zsrc, zend_long length); -int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); -int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); -int redis_cmd_append_sstr_int(smart_string *str, int append); -int redis_cmd_append_sstr_long(smart_string *str, long append); -int redis_cmd_append_sstr_zend_long(smart_string *str, zend_long append); -int redis_cmd_append_sstr_i64(smart_string *str, int64_t append); -int redis_cmd_append_sstr_u64(smart_string *str, uint64_t append); -int redis_cmd_append_sstr_dbl(smart_string *str, double value); -int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr); -int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock); -int redis_cmd_append_sstr_key(smart_string *str, char *key, size_t len, RedisSock *redis_sock, short *slot); -int redis_cmd_append_sstr_key_zstr(smart_string *str, zend_string *key, RedisSock *redis_sock, short *slot); -int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot); -int redis_cmd_append_sstr_key_long(smart_string *dst, zend_long lval, RedisSock *redis_sock, short *slot); -int redis_cmd_append_sstr_arrkey(smart_string *cmd, zend_string *kstr, zend_ulong idx); - -PHP_REDIS_API int redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *fmt, ...); PHP_REDIS_API zend_string *redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...); PHP_REDIS_API char *redis_sock_read(RedisSock *redis_sock, int *buf_len); PHP_REDIS_API int redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t* line_len); -PHP_REDIS_API int redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, void *ctx); +PHP_REDIS_API int redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval* z_tab, RedisCmdCtx ctx); typedef void (*SuccessCallback)(RedisSock *redis_sock); -PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx, SuccessCallback success_callback); -PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx, SuccessCallback success_callback); +PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); -PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, int port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval); +PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API RedisSock* redis_sock_create(RedisSockType type, const char *host, + int host_len, int port, double timeout, double read_timeout, int persistent, + const char *persistent_id, long retry_interval); PHP_REDIS_API int redis_sock_configure(RedisSock *redis_sock, HashTable *opts); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock); -PHP_REDIS_API char *redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen); +PHP_REDIS_API zend_string *redis_sock_auth_cmd(RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass); PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv); PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes); -PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); +PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, RedisCmdCtx ctx); PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize); -PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_vals(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); void redisSetScanCursor(zval *zv, uint64_t cursor); uint64_t redisGetScanCursor(zval *zv, zend_bool *was_zero); @@ -138,54 +120,59 @@ uint64_t redisGetScanCursor(zval *zv, zend_bool *was_zero); PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, uint64_t *cursor); PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_read_xclaim_reply( RedisSock *redis_sock, int count, int is_xautoclaim, zval *rv); PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_vinfo_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_read_vinfo_response(RedisSock *redis_sock, zval *z_ret, long long count); PHP_REDIS_API int redis_vemb_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_read_vemb_response(RedisSock *redis_sock, zval *z_ret, long long count); PHP_REDIS_API int redis_read_vlinks_response(RedisSock *redis_sock, zval *z_ret, - long long elements, void *ctx); + long long elements, RedisCmdCtx ctx); PHP_REDIS_API int redis_vlinks_reply(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_deserialize_vgetattr_reply(zval *z_ret, const char *reply, size_t len); PHP_REDIS_API int redis_vgetattr_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx); + zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_pubsub_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); -PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz); +PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, const char *cmd, size_t sz); PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw); PHP_REDIS_API RedisSock *redis_sock_get(zval *id, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); PHP_REDIS_API void redis_sock_clear_err(RedisSock *redis_sock); +static zend_always_inline int +redis_sock_write_cmd(RedisSock *redis_sock, RedisCmd *cmd) { + return redis_sock_write(redis_sock, redis_cmd_str(cmd), redis_cmd_len(cmd)); +} + void redis_sock_set_context(RedisSock *redis_sock, HashTable *ht); void redis_sock_free_context(RedisSock *redis_sock); int redis_sock_set_context_zval(RedisSock *redis_sock, zval *zv); @@ -221,17 +208,17 @@ redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements); PHP_REDIS_API int redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx); + zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int -redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, void *ctx); +redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, RedisCmdCtx ctx); /* Specialized ACL reply handlers */ -PHP_REDIS_API int redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_acl_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_read_acl_getuser_reply(RedisSock *redis_sock, zval *zret, long len); -PHP_REDIS_API int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_acl_getuser_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_read_acl_log_reply(RedisSock *redis_sock, zval *zret, long count); -PHP_REDIS_API int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); /* * Variant Read methods, mostly to implement eval @@ -240,33 +227,41 @@ PHP_REDIS_API int redis_acl_log_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r PHP_REDIS_API int redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, long *reply_info); PHP_REDIS_API int redis_read_variant_bulk(RedisSock *redis_sock, long size, zval *z_ret); PHP_REDIS_API int redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int status_strings, zval *z_ret); -PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); - -PHP_REDIS_API int redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +int redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx); +int redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_zadd_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_zrandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_zdiff_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_set_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_read_geosearch_response(zval *zdst, RedisSock *redis_sock, long long elements, int with_aux_data); -PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); PHP_REDIS_API int redis_randmember_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx); + RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, RedisCmdCtx ctx); -PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +int redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, + RedisCmdCtx ctx); +PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx); +int redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); -PHP_REDIS_API int redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_hello_version_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); +PHP_REDIS_API int redis_hello_version_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); /* Helper methods to get configuration values from a HashTable. */ diff --git a/package.xml b/package.xml index af4b93f3..79ed177f 100644 --- a/package.xml +++ b/package.xml @@ -178,6 +178,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + diff --git a/redis.c b/redis.c index 8bfb1e3a..92e9fff9 100644 --- a/redis.c +++ b/redis.c @@ -60,6 +60,8 @@ extern ps_module ps_mod_redis_cluster; zend_class_entry *redis_ce; zend_class_entry *redis_exception_ce; +extern RedisCmdCtx redis_empty_ctx; + #if PHP_VERSION_ID < 80000 #include "redis_legacy_arginfo.h" #else @@ -484,7 +486,8 @@ PHP_METHOD(Redis, __construct) ZEND_PARSE_PARAMETERS_END_EX(RETURN_THROWS()); redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, getThis()); - redis->sock = redis_sock_create(ZEND_STRL("127.0.0.1"), 6379, 0, 0, 0, NULL, 0); + redis->sock = redis_sock_create(REDIS_SOCK_STANDALONE, ZEND_STRL("127.0.0.1"), + 6379, 0, 0, 0, NULL, 0); if (opts != NULL && redis_sock_configure(redis->sock, opts) != SUCCESS) { RETURN_THROWS(); } @@ -494,7 +497,7 @@ PHP_METHOD(Redis, __construct) /* {{{ proto Redis Redis::__destruct() Public Destructor */ -PHP_METHOD(Redis,__destruct) { +PHP_METHOD(Redis, __destruct) { if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } @@ -508,8 +511,6 @@ PHP_METHOD(Redis,__destruct) { // If we think we're in MULTI mode, send a discard if (IS_MULTI(redis_sock)) { if (!IS_PIPELINE(redis_sock) && redis_sock->stream) { - // Discard any multi commands, and free any callbacks that have been - // queued redis_send_discard(redis_sock); } redis_free_reply_callbacks(redis_sock); @@ -604,8 +605,9 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) redis_free_socket(redis->sock); } - redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, - persistent, persistent_id, retry_interval); + redis->sock = redis_sock_create(REDIS_SOCK_STANDALONE, host, host_len, port, + timeout, read_timeout, persistent, + persistent_id, retry_interval); if (context) { /* Stream context (e.g. TLS) */ @@ -631,13 +633,20 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) return SUCCESS; } -static inline void -pipeline_enqueue_command(RedisSock *redis_sock, const char *cmd, int cmd_len) { - smart_string_appendl(&redis_sock->pipeline_cmd, cmd, cmd_len); +static zend_always_inline void +pipeline_enqueue_cmd_strl(RedisSock *redis_sock, const char *cmd, int len) { + smart_string_appendl(&redis_sock->pipeline_cmd, cmd, len); +} + +static zend_always_inline void +pipeline_enqueue_cmd(RedisSock *redis_sock, RedisCmd *cmd) { + pipeline_enqueue_cmd_strl(redis_sock, redis_cmd_str(cmd), + redis_cmd_len(cmd)); } static void -redis_save_callback(RedisSock *redis_sock, FailableResultCallback cb, void *ctx) +redis_save_callback(RedisSock *redis_sock, FailableResultCallback cb, + RedisCmdCtx ctx) { fold_item *fi; @@ -647,57 +656,73 @@ redis_save_callback(RedisSock *redis_sock, FailableResultCallback cb, void *ctx) fi->ctx = ctx; } -#define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ - if (!IS_PIPELINE(redis_sock)) { \ - if (redis_response_enqueued(redis_sock) != SUCCESS) { \ - RETURN_FALSE; \ - } \ - } \ - redis_save_callback(redis_sock, function, closure_context); \ - RETURN_ZVAL(getThis(), 1, 0); \ - - -static int redis_process_request(RedisSock *redis_sock, char *cmd, int cmdlen) { +static int +redis_process_request_strl(RedisSock *redis_sock, const char *cmd, int len) { int res = SUCCESS; if (IS_PIPELINE(redis_sock)) { - pipeline_enqueue_command(redis_sock, cmd, cmdlen); - } else if (UNEXPECTED(redis_sock_write(redis_sock, cmd, cmdlen) < 0)) { + pipeline_enqueue_cmd_strl(redis_sock, cmd, len); + } else if (UNEXPECTED(redis_sock_write(redis_sock, cmd, len) < 0)) { res = FAILURE; } - efree(cmd); return res; } +static int +redis_process_request(RedisSock *redis_sock, RedisCmd *cmd) { + return redis_process_request_strl(redis_sock, redis_cmd_str(cmd), + redis_cmd_len(cmd)); +} + static void -redis_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redis_cmd_cb cmd_cb, +redis_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redis_cmd_cb *cmd_cb, FailableResultCallback resp_cb) { RedisSock *redis_sock; - void *ctx = NULL; - int cmd_len; - char *cmd; + RedisCmdCtx ctx; + RedisCmd *cmd; + int res; redis_sock = redis_sock_get(getThis(), 0); if (UNEXPECTED(redis_sock == NULL)) { RETURN_FALSE; } - if (UNEXPECTED(cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, - &cmd_len, NULL, &ctx) == FAILURE)) - { + cmd = cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); + if (UNEXPECTED(cmd == NULL)) { RETURN_FALSE; } - if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { + ctx = redis_cmd_pop_ctx(cmd); + + if (IS_PIPELINE(redis_sock)) { + pipeline_enqueue_cmd(redis_sock, cmd); + res = SUCCESS; + } else { + res = redis_sock_write_cmd(redis_sock, cmd) < 0 ? FAILURE : SUCCESS; + } + + redis_cmd_free(cmd); + + if (UNEXPECTED(res != SUCCESS)) { + redis_cmd_ctx_free(ctx); RETURN_FALSE; } if (IS_ATOMIC(redis_sock)) { resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); + redis_cmd_ctx_free(ctx); } else { - REDIS_PROCESS_RESPONSE_CLOSURE(resp_cb, ctx); + if (!IS_PIPELINE(redis_sock)) { + if (redis_response_enqueued(redis_sock) != SUCCESS) { + redis_cmd_ctx_free(ctx); + RETURN_FALSE; + } + } + + redis_save_callback(redis_sock, resp_cb, ctx); + RETURN_ZVAL(getThis(), 1, 0); } } @@ -707,32 +732,52 @@ redis_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redis_cmd_cb cmd_cb, void redis_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, - redis_kw_cmd_cb cmd_cb, FailableResultCallback resp_cb, - void *ctx) + redis_kw_cmd_cb *cmd_cb, FailableResultCallback resp_cb) { RedisSock *redis_sock; - int cmd_len; - char *cmd; + RedisCmdCtx ctx; + RedisCmd *cmd; + int res; redis_sock = redis_sock_get(getThis(), 0); if (UNEXPECTED(redis_sock == NULL)) { RETURN_FALSE; } - if (UNEXPECTED(cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, (char*)kw, &cmd, - &cmd_len, NULL, &ctx) == FAILURE)) - { + cmd = cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, (char*)kw); + if (UNEXPECTED(cmd == NULL)) { RETURN_FALSE; } - if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { + ctx = redis_cmd_pop_ctx(cmd); + + if (IS_PIPELINE(redis_sock)) { + pipeline_enqueue_cmd(redis_sock, cmd); + res = SUCCESS; + } else { + res = redis_sock_write_cmd(redis_sock, cmd) < 0 ? FAILURE : SUCCESS; + } + + redis_cmd_free(cmd); + + if (UNEXPECTED(res != SUCCESS)) { + redis_cmd_ctx_free(ctx); RETURN_FALSE; } if (IS_ATOMIC(redis_sock)) { resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); + redis_cmd_ctx_free(ctx); } else { - REDIS_PROCESS_RESPONSE_CLOSURE(resp_cb, ctx); + if (!IS_PIPELINE(redis_sock)) { + if (redis_response_enqueued(redis_sock) != SUCCESS) { + redis_cmd_ctx_free(ctx); + RETURN_FALSE; + } + } + + redis_save_callback(redis_sock, resp_cb, ctx); + RETURN_ZVAL(getThis(), 1, 0); } } @@ -847,7 +892,7 @@ PHP_METHOD(Redis, reset) char *response; int response_len; RedisSock *redis_sock; - smart_string cmd = {0}; + RedisCmd *cmd; zend_bool ret = 0; if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) { @@ -855,15 +900,18 @@ PHP_METHOD(Redis, reset) } if (IS_PIPELINE(redis_sock)) { - php_error_docref(NULL, E_ERROR, "Reset isn't allowed in pipeline mode!"); + php_error_docref(NULL, E_ERROR, + "Reset isn't allowed in pipeline mode!"); RETURN_FALSE; } - redis_cmd_init_sstr(&cmd, 0, ZEND_STRL("RESET")); + cmd = redis_cmd_create_literal(NULL, "RESET"); - if (redis_process_request(redis_sock, cmd.c, cmd.len) != SUCCESS) { + if (redis_process_request(redis_sock, cmd) != SUCCESS) { + redis_cmd_free(cmd); RETURN_FALSE; } + redis_cmd_free(cmd); if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) { ret = REDIS_STRCMP_STATIC(response, response_len, "+RESET"); @@ -1010,11 +1058,12 @@ PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock) redis_sock->watching = 1; } -PHP_REDIS_API int redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, - RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int +redis_watch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { - return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - z_tab, ctx, redis_set_watch); + return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, z_tab, ctx, redis_set_watch); } /* {{{ proto boolean Redis::watch(string key1, string key2...) @@ -1031,7 +1080,7 @@ PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock) PHP_REDIS_API int redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, - void *ctx) + RedisCmdCtx ctx) { return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx, redis_clear_watch); @@ -1360,63 +1409,46 @@ PHP_METHOD(Redis, sort_ro) { REDIS_PROCESS_KW_CMD("SORT_RO", redis_sort_cmd, redis_read_variant_reply); } -static void -generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) +static RedisCmd * +generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int desc, + int alpha) { + zend_string *key = NULL, *pattern = NULL, *store = NULL, *zpattern; zval *object, *zele, *zget = NULL; - RedisSock *redis_sock; - zend_string *zpattern; - char *key = NULL, *pattern = NULL, *store = NULL; - size_t keylen, patternlen, storelen; zend_long offset = -1, count = -1; - int argc = 1; /* SORT key is the simplest SORT command */ - smart_string cmd = {0}; + RedisCmd *cmd; /* Parse myriad of sort arguments */ if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Os|s!z!lls", &object, redis_ce, &key, - &keylen, &pattern, &patternlen, &zget, - &offset, &count, &store, &storelen) + "OS|S!z!llS", &object, redis_ce, &key, + &pattern, &zget, &offset, &count, &store) == FAILURE) { - RETURN_FALSE; + return NULL; } /* Ensure we're sorting something, and we can get context */ - if (keylen == 0 || !(redis_sock = redis_sock_get(object, 0))) - RETURN_FALSE; - - /* Start calculating argc depending on input arguments */ - if (pattern && patternlen) argc += 2; /* BY pattern */ - if (offset >= 0 && count >= 0) argc += 3; /* LIMIT offset count */ - if (alpha) argc += 1; /* ALPHA */ - if (store) argc += 2; /* STORE destination */ - if (desc) argc += 1; /* DESC (ASC is the default) */ - - /* GET is special. It can be 0 .. N arguments depending what we have */ - if (zget) { - if (Z_TYPE_P(zget) == IS_ARRAY) - argc += zend_hash_num_elements(Z_ARRVAL_P(zget)); - else if (Z_STRLEN_P(zget) > 0) { - argc += 2; /* GET pattern */ - } - } + if (ZSTR_LEN(key) == 0) + return NULL; /* Start constructing final command and append key */ - redis_cmd_init_sstr(&cmd, argc, ZEND_STRL("SORT")); - redis_cmd_append_sstr_key(&cmd, key, keylen, redis_sock, NULL); + cmd = redis_cmd_create_literal(redis_sock, "SORT"); + if (!redis_cmd_cat_key_zstr(cmd, key)) { + redis_cmd_free(cmd); + return NULL; + } /* BY pattern */ - if (pattern && patternlen) { - redis_cmd_append_sstr(&cmd, ZEND_STRL("BY")); - redis_cmd_append_sstr(&cmd, pattern, patternlen); + if (pattern && ZSTR_LEN(pattern)) { + redis_cmd_cat_literal(cmd, "BY"); + redis_cmd_cat_zstr(cmd, pattern); } /* LIMIT offset count */ if (offset >= 0 && count >= 0) { - redis_cmd_append_sstr(&cmd, ZEND_STRL("LIMIT")); - redis_cmd_append_sstr_long(&cmd, offset); - redis_cmd_append_sstr_long(&cmd, count); + redis_cmd_cat_literal(cmd, "LIMIT"); + redis_cmd_cat_long(cmd, offset); + redis_cmd_cat_long(cmd, count); } /* Handle any number of GET pattern arguments we've been passed */ @@ -1424,74 +1456,61 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) if (Z_TYPE_P(zget) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) { zpattern = zval_get_string(zele); - redis_cmd_append_sstr(&cmd, ZEND_STRL("GET")); - redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern)); + redis_cmd_cat_literal(cmd, "GET"); + redis_cmd_cat_zstr(cmd, zpattern); zend_string_release(zpattern); } ZEND_HASH_FOREACH_END(); } else { zpattern = zval_get_string(zget); - redis_cmd_append_sstr(&cmd, ZEND_STRL("GET")); - redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern)); + redis_cmd_cat_literal(cmd, "GET"); + redis_cmd_cat_zstr(cmd, zpattern); zend_string_release(zpattern); } } /* Append optional DESC and ALPHA modifiers */ - if (desc) redis_cmd_append_sstr(&cmd, ZEND_STRL("DESC")); - if (alpha) redis_cmd_append_sstr(&cmd, ZEND_STRL("ALPHA")); + if (desc) redis_cmd_cat_literal(cmd, "DESC"); + if (alpha) redis_cmd_cat_literal(cmd, "ALPHA"); /* Finally append STORE if we've got it */ - if (store && storelen) { - redis_cmd_append_sstr(&cmd, ZEND_STRL("STORE")); - redis_cmd_append_sstr_key(&cmd, store, storelen, redis_sock, NULL); - } - - if (redis_process_request(redis_sock, cmd.c, cmd.len) != SUCCESS) { - RETURN_FALSE; - } - - if (IS_ATOMIC(redis_sock)) { - if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) - { - RETURN_FALSE; + if (store && ZSTR_LEN(store)) { + redis_cmd_cat_literal(cmd, "STORE"); + if (!redis_cmd_cat_key_zstr(cmd, store)) { + redis_cmd_free(cmd); + return NULL; } - } else { - REDIS_PROCESS_RESPONSE_CLOSURE(redis_read_variant_reply, NULL); } + + return cmd; } -/* {{{ proto array Redis::sortAsc(string key, string pattern, string get, - * int start, int end, bool getList]) */ -PHP_METHOD(Redis, sortAsc) -{ - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); -} -/* }}} */ +#define REDIS_SORT_CMD_FUNC(name, desc, alpha) \ + RedisCmd * \ + redis_##name##_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { \ + return generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, \ + desc, alpha); \ + } -/* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get, - * int start, int end, bool getList]) */ -PHP_METHOD(Redis, sortAscAlpha) -{ - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1); -} -/* }}} */ +REDIS_SORT_CMD_FUNC(sort_asc_numeric, 0, 0) +REDIS_SORT_CMD_FUNC(sort_asc_alpha, 0, 1) +REDIS_SORT_CMD_FUNC(sort_desc_numeric, 1, 0) +REDIS_SORT_CMD_FUNC(sort_desc_alpha, 1, 1) -/* {{{ proto array Redis::sortDesc(string key, string pattern, string get, - * int start, int end, bool getList]) */ -PHP_METHOD(Redis, sortDesc) -{ - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0); +PHP_METHOD(Redis, sortAsc) { + REDIS_PROCESS_CMD(sort_asc_numeric, redis_read_variant_reply); } -/* }}} */ -/* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get, - * int start, int end, bool getList]) */ -PHP_METHOD(Redis, sortDescAlpha) -{ - generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1); +PHP_METHOD(Redis, sortAscAlpha){ + REDIS_PROCESS_CMD(sort_asc_alpha, redis_read_variant_reply); +} + +PHP_METHOD(Redis, sortDesc) { + REDIS_PROCESS_CMD(sort_desc_numeric, redis_read_variant_reply); +} + +PHP_METHOD(Redis, sortDescAlpha) { + REDIS_PROCESS_CMD(sort_desc_alpha, redis_read_variant_reply); } -/* }}} */ /* {{{ proto array Redis::expire(string key, int timeout) */ PHP_METHOD(Redis, expire) { @@ -2122,8 +2141,8 @@ PHP_METHOD(Redis, multi) /* Don't want to do anything if we're already in MULTI mode */ if (!IS_MULTI(redis_sock)) { if (IS_PIPELINE(redis_sock)) { - pipeline_enqueue_command(redis_sock, ZEND_STRL(RESP_MULTI_CMD)); - redis_save_callback(redis_sock, NULL, NULL); + pipeline_enqueue_cmd_strl(redis_sock, ZEND_STRL(RESP_MULTI_CMD)); + redis_save_callback(redis_sock, NULL, redis_empty_ctx); redis_sock->mode |= MULTI; } else { if (redis_sock_write(redis_sock, ZEND_STRL(RESP_MULTI_CMD)) < 0) { @@ -2222,8 +2241,8 @@ PHP_METHOD(Redis, exec) if (IS_MULTI(redis_sock)) { if (IS_PIPELINE(redis_sock)) { - pipeline_enqueue_command(redis_sock, ZEND_STRL(RESP_EXEC_CMD)); - redis_save_callback(redis_sock, NULL, NULL); + pipeline_enqueue_cmd_strl(redis_sock, ZEND_STRL(RESP_EXEC_CMD)); + redis_save_callback(redis_sock, NULL, redis_empty_ctx); redis_sock->mode &= ~MULTI; RETURN_ZVAL(getThis(), 1, 0); } @@ -2910,36 +2929,21 @@ PHP_METHOD(Redis, client) { } /* }}} */ -/* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */ -PHP_METHOD(Redis, rawcommand) { - int argc, cmd_len; - char *cmd = NULL; - RedisSock *redis_sock; - zval *z_args; +static RedisCmd * +redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zval *argv; + int argc; ZEND_PARSE_PARAMETERS_START(1, -1) - Z_PARAM_VARIADIC('+', z_args, argc) - ZEND_PARSE_PARAMETERS_END(); + Z_PARAM_VARIADIC('+', argv, argc) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 || - (redis_sock = redis_sock_get(getThis(), 0)) == NULL - ) { - if (cmd) efree(cmd); - RETURN_FALSE; - } - - if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { - RETURN_FALSE; - } - - if (IS_ATOMIC(redis_sock)) { - redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock,NULL,NULL); - } else { - REDIS_PROCESS_RESPONSE_CLOSURE(redis_read_variant_reply, NULL); - } + return redis_build_raw_cmd(argv, argc); +} + +PHP_METHOD(Redis, rawcommand) { + REDIS_PROCESS_CMD(rawcommand, redis_read_raw_variant_reply); } -/* }}} */ /* {{{ proto array Redis::command() * proto array Redis::command('info', string cmd) @@ -2956,18 +2960,12 @@ PHP_METHOD(Redis, copy) { /* }}} */ /* Helper to format any combination of SCAN arguments */ -static int -redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, - uint64_t cursor, char *pattern, int pattern_len, int count, - zend_string *match_type) +static RedisCmd * +redis_build_scan_cmd(REDIS_SCAN_TYPE type, zend_string *key, uint64_t cursor, + zend_string *pattern, int count, zend_string *match_type) { - smart_string cmdstr = {0}; + RedisCmd *cmd; char *keyword; - int argc; - - /* Count our arguments +1 for key if it's got one, and + 2 for pattern */ - /* or count given that they each carry keywords with them. */ - argc = 1 + (key_len > 0) + (pattern_len > 0 ? 2 : 0) + (count > 0 ? 2 : 0) + (match_type ? 2 : 0); /* Turn our type into a keyword */ switch(type) { @@ -2986,63 +2984,61 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, break; } + /* Start the command */ - redis_cmd_init_sstr(&cmdstr, argc, keyword, strlen(keyword)); - if (key_len) redis_cmd_append_sstr(&cmdstr, key, key_len); - redis_cmd_append_sstr_u64(&cmdstr, cursor); + cmd = redis_cmd_create(NULL, keyword, strlen(keyword)); + if (key && ZSTR_LEN(key) > 0) redis_cmd_cat_zstr(cmd, key); + redis_cmd_cat_u64(cmd, cursor); /* Append COUNT if we've got it */ if(count) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_int(&cmdstr, count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); } /* Append MATCH if we've got it */ - if(pattern_len) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MATCH"); - redis_cmd_append_sstr(&cmdstr, pattern, pattern_len); + if(pattern && ZSTR_LEN(pattern) > 0) { + redis_cmd_cat_literal(cmd, "MATCH"); + redis_cmd_cat_zstr(cmd, pattern); } if (match_type) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TYPE"); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(match_type), ZSTR_LEN(match_type)); + redis_cmd_cat_literal(cmd, "TYPE"); + redis_cmd_cat_zstr(cmd, match_type); } - /* Return our command length */ - *cmd = cmdstr.c; - return cmdstr.len; + return cmd; } /* {{{ proto redis::scan(&$cursor, [pattern, [count, [type]]]) */ PHP_REDIS_API void generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { + zend_string *key = NULL, *pattern = NULL; + zend_string *match_type = NULL; zval *object, *z_cursor; RedisSock *redis_sock; - HashTable *hash; - char *pattern = NULL, *cmd, *key = NULL; - int cmd_len, num_elements, key_free = 0, pattern_free = 0; - size_t key_len = 0, pattern_len = 0; - zend_string *match_type = NULL; + zend_bool pattern_free = 0; zend_long count = 0; zend_bool completed; + HashTable *hash; + int num_elements; uint64_t cursor; + RedisCmd *cmd; /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { // Requires a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Os!z/|s!l", &object, redis_ce, &key, - &key_len, &z_cursor, &pattern, - &pattern_len, &count)==FAILURE) + "OS!z/|S!l", &object, redis_ce, &key, + &z_cursor, &pattern, &count)==FAILURE) { RETURN_FALSE; } } else { // Doesn't require a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Oz/|s!lS!", &object, redis_ce, &z_cursor, - &pattern, &pattern_len, &count, &match_type) - == FAILURE) + "Oz/|S!lS!", &object, redis_ce, &z_cursor, + &pattern, &count, &match_type) == FAILURE) { RETURN_FALSE; } @@ -3065,13 +3061,12 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { if (completed) RETURN_FALSE; - /* Prefix our key if we've got one and we have a prefix set */ - if(key_len) { - key_free = redis_key_prefix(redis_sock, &key, &key_len); - } + if(key) + key = redis_key_prefix_zstr(redis_sock, key); - if (redis_sock->scan & REDIS_SCAN_PREFIX) { - pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len); + if (pattern && redis_sock->scan & REDIS_SCAN_PREFIX) { + pattern = redis_key_prefix_zstr(redis_sock, pattern); + pattern_free = 1; } /** @@ -3091,31 +3086,32 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } // Format our SCAN command - cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, cursor, - pattern, pattern_len, count, match_type); + cmd = redis_build_scan_cmd(type, key, cursor, pattern, count, match_type); - if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { - RETURN_FALSE; - } - - if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock,type, &cursor) < 0) + if (redis_process_request(redis_sock, cmd) != SUCCESS || + redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, type, &cursor) < 0) { - if(key_free) efree(key); + if (key) + zend_string_release(key); + if (pattern_free) + zend_string_release(pattern); + redis_cmd_free(cmd); RETURN_FALSE; } /* Get the number of elements */ hash = Z_ARRVAL_P(return_value); num_elements = zend_hash_num_elements(hash); + + redis_cmd_free(cmd); } while (redis_sock->scan & REDIS_SCAN_RETRY && cursor != 0 && - num_elements == 0); + num_elements == 0); - /* Free our pattern if it was prefixed */ - if (pattern_free) efree(pattern); - - /* Free our key if it was prefixed */ - if(key_free) efree(key); + if (pattern_free) + zend_string_release(pattern); + if(key) + zend_string_release(key); /* Update our cursor reference */ redisSetScanCursor(z_cursor, cursor); diff --git a/redis_array_impl.c b/redis_array_impl.c index be009793..ae79ed08 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -64,9 +64,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, zend_string *user, redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, &ra->redis[i]); /* create socket */ - redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, - ra->read_timeout, ra->pconnect, NULL, - retry_interval); + redis->sock = redis_sock_create(REDIS_SOCK_ARRAY, host, host_len, port, + ra->connect_timeout, ra->read_timeout, + ra->pconnect, NULL, retry_interval); redis_sock_set_auth(redis->sock, user, pass); diff --git a/redis_cluster.c b/redis_cluster.c index e3ebf00d..66b513a0 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -36,6 +36,8 @@ zend_class_entry *redis_cluster_ce; /* Exception handler */ zend_class_entry *redis_cluster_exception_ce; +extern RedisCmdCtx redis_empty_ctx; + #if PHP_VERSION_ID < 80000 #include "redis_cluster_legacy_arginfo.h" #else @@ -44,7 +46,7 @@ zend_class_entry *redis_cluster_exception_ce; #endif static void -cluster_enqueue_response(redisCluster *c, short slot, cluster_cb cb, void *ctx) +cluster_enqueue_response(redisCluster *c, short slot, cluster_cb cb, RedisCmdCtx ctx) { clusterFoldItem *item; @@ -69,6 +71,7 @@ static void cluster_free_queue(redisCluster *c) { while (item) { tmp = item->next; + redis_cmd_ctx_free(item->ctx); efree(item); item = tmp; } @@ -94,25 +97,27 @@ void cluster_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, redis_cmd_cb cmd_cb, cluster_cb resp_cb, int readonly) { - void *ctx = NULL; - int cmd_len; + RedisCmdCtx ctx; + RedisCmd *cmd; short slot; - char *cmd; c->readonly = readonly && CLUSTER_IS_ATOMIC(c); - if (cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, &cmd_len, &slot, - &ctx) == FAILURE) - { + cmd = cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); + if (cmd == NULL) { RETURN_FALSE; } - if (cluster_send_command(c, slot, cmd, cmd_len) < 0 || c->err != NULL) { - efree(cmd); + ctx = redis_cmd_pop_ctx(cmd); + slot = cmd->slot; + + if (cluster_send_command(c, slot, redis_cmd_str(cmd), redis_cmd_len(cmd)) < 0 || c->err != NULL) { + redis_cmd_ctx_free(ctx); + redis_cmd_free(cmd); RETURN_FALSE; } - efree(cmd); + redis_cmd_free(cmd); if (c->flags->mode == MULTI) { cluster_enqueue_response(c, slot, resp_cb, ctx); @@ -120,6 +125,7 @@ cluster_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); + redis_cmd_ctx_free(ctx); } void @@ -127,26 +133,29 @@ cluster_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, const char *kw, redis_kw_cmd_cb cmd_cb, cluster_cb resp_cb, int readonly) { - void *ctx = NULL; - int cmd_len; + RedisCmdCtx ctx; + RedisCmd *cmd; short slot; - char *cmd; c->readonly = readonly && CLUSTER_IS_ATOMIC(c); - /* TODO: Update kw commands to take a const char * (and len to avoid strlen) */ - if (cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, (char*)kw, &cmd, &cmd_len, - &slot, &ctx) == FAILURE) + cmd = cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, (char*)kw); + if (cmd == NULL) { + RETURN_FALSE; + } + + ctx = redis_cmd_pop_ctx(cmd); + slot = cmd->slot; + + if (cluster_send_command(c, slot, redis_cmd_str(cmd), redis_cmd_len(cmd)) < 0 || + c->err != NULL) { + redis_cmd_ctx_free(ctx); + redis_cmd_free(cmd); RETURN_FALSE; } - if (cluster_send_command(c, slot, cmd, cmd_len) < 0 || c->err != NULL) { - efree(cmd); - RETURN_FALSE; - } - - efree(cmd); + redis_cmd_free(cmd); if (c->flags->mode == MULTI) { cluster_enqueue_response(c, slot, resp_cb, ctx); @@ -154,6 +163,7 @@ cluster_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); + redis_cmd_ctx_free(ctx); } PHP_MINIT_FUNCTION(redis_cluster) @@ -249,6 +259,7 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time if (context) redis_sock_set_context_zval(c->flags, context); + c->flags->type = REDIS_SOCK_CLUSTER; c->flags->timeout = timeout; c->flags->read_timeout = read_timeout; c->flags->persistent = persistent; @@ -413,31 +424,48 @@ PHP_METHOD(RedisCluster, set) { } /* }}} */ +static void cluster_multi_ctx_dtor(void *ptr) +{ + clusterMultiCtx *mctx = ptr; + + if (mctx->last) { + efree(mctx->z_multi); + } + efree(mctx); +} + /* Generic handler for MGET/MSET/MSETNX */ static int distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, clusterMultiCmd *mc, zval *z_ret, int last, cluster_cb cb) { - clusterMultiCtx *ctx; + clusterMultiCtx *mctx; + RedisCmdCtx ctx = {0}; // Finalize multi command cluster_multi_fini(mc); // Spin up multi context - ctx = emalloc(sizeof(clusterMultiCtx)); - ctx->z_multi = z_ret; - ctx->count = mc->argc; - ctx->last = last; + mctx = emalloc(sizeof(clusterMultiCtx)); + mctx->z_multi = z_ret; + mctx->count = mc->argc; + mctx->last = last; // Attempt to send the command - if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len) < 0 || c->err != NULL) { - efree(ctx); + if (cluster_send_command(c,slot,redis_cmd_str(mc->cmd), + redis_cmd_len(mc->cmd)) < 0 || c->err != NULL) + { + efree(mctx); return -1; } + ctx.ptr = mctx; + ctx.dtor = cluster_multi_ctx_dtor; + if (CLUSTER_IS_ATOMIC(c)) { // Process response now cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); + redis_cmd_ctx_free(ctx); } else { cluster_enqueue_response(c, slot, cb, ctx); } @@ -824,8 +852,8 @@ PHP_METHOD(RedisCluster, mget) { array_init(z_ret); // Parse args, process - if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MGET", - sizeof("MGET")-1, z_ret, cluster_mbulk_mget_resp) < 0) + if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MGET"), + z_ret, cluster_mbulk_mget_resp) < 0) { zval_ptr_dtor_nogc(z_ret); efree(z_ret); @@ -840,8 +868,8 @@ PHP_METHOD(RedisCluster, mset) { ZVAL_TRUE(z_ret); // Parse args and process. If we get a failure, free zval and return FALSE. - if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", - sizeof("MSET")-1, z_ret, cluster_mset_resp) ==-1) + if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MSET"), + z_ret, cluster_mset_resp) == -1) { efree(z_ret); RETURN_FALSE; @@ -855,8 +883,8 @@ PHP_METHOD(RedisCluster, msetnx) { array_init(z_ret); // Parse args and process. If we get a failure, free mem and return FALSE - if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", - sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp) ==-1) + if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MSETNX"), + z_ret, cluster_msetnx_resp) ==-1) { zval_ptr_dtor_nogc(z_ret); efree(z_ret); @@ -904,24 +932,39 @@ PHP_METHOD(RedisCluster, touch) { CLUSTER_PROCESS_KW_CMD("TOUCH", redis_varkey_cmd, cluster_long_resp, 0); } +static zend_always_inline int +cluster_send_slot_cmd(redisCluster *c, short slot, RedisCmd *cmd, + REDIS_REPLY_TYPE type) +{ + const char *str; + size_t len; + + cmd->slot = slot; + + str = redis_cmd_str(cmd); + len = redis_cmd_len(cmd); + + return cluster_send_slot(c, slot, str, len, type); +} + /* }}} */ /* {{{ proto array Redis::keys(string pattern) */ PHP_METHOD(RedisCluster, keys) { redisCluster *c = GET_CONTEXT(); redisClusterNode *node; - size_t pat_len; - char *pat, *cmd; clusterReply *resp; - int i, cmd_len; + zend_string *pat; + RedisCmd *cmd; + int i; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pat, &pat_len) - == FAILURE) - { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &pat) == FAILURE) { RETURN_FALSE; } /* Prefix and then build our command */ - cmd_len = redis_spprintf(c->flags, NULL, &cmd, "KEYS", "k", pat, pat_len); + cmd = redis_cmd_create_literal(c->flags, "KEYS"); + + redis_cmd_cat_key_zstr(cmd, pat); array_init(return_value); @@ -931,13 +974,11 @@ PHP_METHOD(RedisCluster, keys) { /* Iterate over our known nodes */ ZEND_HASH_FOREACH_PTR(c->nodes, node) { if (node == NULL) continue; - if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK - ) < 0) - { + if (cluster_send_slot_cmd(c, node->slot, cmd, TYPE_MULTIBULK) < 0) { php_error_docref(0, E_ERROR, "Can't send KEYS to %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); zval_ptr_dtor_nogc(return_value); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } @@ -965,7 +1006,7 @@ PHP_METHOD(RedisCluster, keys) { cluster_free_reply(resp, 1); } ZEND_HASH_FOREACH_END(); - efree(cmd); + redis_cmd_free(cmd); } /* }}} */ @@ -1821,10 +1862,8 @@ PHP_METHOD(RedisCluster, psubscribe) { static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, char *kw) { - char *cmd; - int cmd_len; - void *ctx; - short slot; + RedisCmdCtx ctx; + RedisCmd *cmd; // There is not reason to unsubscribe outside of a subscribe loop if (c->subscribed_slot == -1) { @@ -1834,18 +1873,20 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, } // Call directly because we're going to set the slot manually - if (redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, - &cmd, &cmd_len, &slot, &ctx) - == FAILURE) - { + cmd = redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw); + if (cmd == NULL) { RETURN_FALSE; } + ctx = redis_cmd_pop_ctx(cmd); + // This has to operate on our subscribe slot - if (cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK - ) == FAILURE) + if (cluster_send_slot_cmd(c, c->subscribed_slot, cmd, TYPE_MULTIBULK) + == FAILURE) { CLUSTER_THROW_EXCEPTION("Failed to UNSUBSCRIBE within our subscribe loop!", 0); + redis_cmd_ctx_free(ctx); + redis_cmd_free(cmd); RETURN_FALSE; } @@ -1853,7 +1894,8 @@ static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, cluster_unsub_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); // Cleanup our command - efree(cmd); + redis_cmd_ctx_free(ctx); + redis_cmd_free(cmd); } /* {{{ proto array RedisCluster::unsubscribe(array chans) */ @@ -2113,13 +2155,13 @@ PHP_METHOD(RedisCluster, multi) { /* {{{ proto bool RedisCluster::watch() */ PHP_METHOD(RedisCluster, watch) { redisCluster *c = GET_CONTEXT(); - HashTable *ht_dist; clusterDistList *dl; - smart_string cmd = {0}; - zval *z_args; - int argc = ZEND_NUM_ARGS(), i; - zend_ulong slot; + HashTable *ht_dist; zend_string *zstr; + zend_ulong slot; + RedisCmd *cmd; + zval *argv; + int argc; // Disallow in MULTI mode if (c->flags->mode == MULTI) { @@ -2129,69 +2171,55 @@ PHP_METHOD(RedisCluster, watch) { } // Don't need to process zero arguments - if (!argc) RETURN_FALSE; + if (!ZEND_NUM_ARGS()) + RETURN_FALSE; // Create our distribution HashTable ht_dist = cluster_dist_create(); - // Allocate args, and grab them - z_args = emalloc(sizeof(zval) * argc); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - cluster_dist_free(ht_dist); - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('+', argv, argc) + ZEND_PARSE_PARAMETERS_END(); // Loop through arguments, prefixing if needed - for(i = 0 ; i < argc; i++) { + for(int i = 0 ; i < argc; i++) { // We'll need the key as a string - zstr = zval_get_string(&z_args[i]); + zstr = zval_get_string(&argv[i]); // Add this key to our distribution handler - if (cluster_dist_add_key(c, ht_dist, ZSTR_VAL(zstr), ZSTR_LEN(zstr), NULL) == FAILURE) { - CLUSTER_THROW_EXCEPTION("Can't issue WATCH command as the keyspace isn't fully mapped", 0); + if (cluster_dist_add_key(c, ht_dist, ZSTR_VAL(zstr), ZSTR_LEN(zstr), + NULL) == FAILURE) + { + CLUSTER_THROW_EXCEPTION( + "Can't issue WATCH command as the keyspace isn't fully mapped", 0); zend_string_release(zstr); RETURN_FALSE; } + zend_string_release(zstr); } // Iterate over each node we'll be sending commands to - ZEND_HASH_FOREACH_PTR(ht_dist, dl) { - // Grab the clusterDistList pointer itself - if (dl == NULL) { - CLUSTER_THROW_EXCEPTION("Internal error in a PHP HashTable", 0); - cluster_dist_free(ht_dist); - efree(z_args); - efree(cmd.c); - RETURN_FALSE; - } else if (zend_hash_get_current_key(ht_dist, NULL, &slot) != HASH_KEY_IS_LONG) { - break; - } - - // Construct our watch command for this node - redis_cmd_init_sstr(&cmd, dl->len, "WATCH", sizeof("WATCH")-1); - for (i = 0; i < dl->len; i++) { - redis_cmd_append_sstr(&cmd, dl->entry[i].key, - dl->entry[i].key_len); + ZEND_HASH_FOREACH_NUM_KEY_PTR(ht_dist, slot, dl) { + cmd = redis_cmd_create_literal(NULL, "WATCH"); + for (int i = 0; i < dl->len; i++) { + redis_cmd_cat_str(cmd, dl->entry[i].key, dl->entry[i].key_len); } // If we get a failure from this, we have to abort - if (cluster_send_command(c,(short)slot,cmd.c,cmd.len) ==-1) { + if (cluster_send_command(c, slot, redis_cmd_str(cmd), + redis_cmd_len(cmd)) < 0) + { + redis_cmd_free(cmd); RETURN_FALSE; } - // This node is watching - SLOT_SOCK(c, (short)slot)->watching = 1; + SLOT_SOCK(c, slot)->watching = 1; - // Zero out our command buffer - cmd.len = 0; + redis_cmd_free(cmd); } ZEND_HASH_FOREACH_END(); - // Cleanup cluster_dist_free(ht_dist); - efree(z_args); - efree(cmd.c); RETURN_TRUE; } @@ -2204,9 +2232,8 @@ PHP_METHOD(RedisCluster, unwatch) { // Send UNWATCH to nodes that need it for(slot = 0; slot < REDIS_CLUSTER_SLOTS; slot++) { if (c->master[slot] && SLOT_SOCK(c,slot)->watching) { - if (cluster_send_slot(c, slot, RESP_UNWATCH_CMD, - sizeof(RESP_UNWATCH_CMD)-1, - TYPE_LINE) ==-1) + if (cluster_send_slot(c, slot, ZEND_STRL(RESP_UNWATCH_CMD), + TYPE_LINE) == -1) { CLUSTER_RETURN_BOOL(c, 0); } @@ -2251,7 +2278,7 @@ PHP_METHOD(RedisCluster, exec) { } // MULTI multi-bulk response handler - cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); // Free our callback queue, any enqueued distributed command context items // and reset our MULTI state. @@ -2328,13 +2355,12 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg) /* Generic handler for things we want directed at a given node, like SAVE, * BGSAVE, FLUSHDB, FLUSHALL, etc */ -static void -cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, - REDIS_REPLY_TYPE reply_type, cluster_cb cb) +static void cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, + size_t kwlen, REDIS_REPLY_TYPE reply_type, + cluster_cb cb) { redisCluster *c = GET_CONTEXT(); - char *cmd; - int cmd_len; + RedisCmd *cmd; zval *z_arg; short slot; @@ -2350,28 +2376,28 @@ cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, } // Construct our command - cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, ""); + cmd = redis_cmd_create(NULL, kw, kwlen); // Kick off our command - if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) { + if (cluster_send_slot_cmd(c, slot, cmd, reply_type) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } // Our response callback - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); // Free our command - efree(cmd); + redis_cmd_free(cmd); } static void -cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply_type, cluster_cb cb) +cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, + REDIS_REPLY_TYPE reply_type, cluster_cb cb) { redisCluster *c = GET_CONTEXT(); - char *cmd; - int cmd_len; + RedisCmd *cmd; zval *z_arg; zend_bool async = 0; short slot; @@ -2387,26 +2413,22 @@ cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply RETURN_FALSE; } - // Construct our command - if (async) { - cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1); - } else { - cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, ""); - } + cmd = redis_cmd_create(NULL, kw, strlen(kw)); + redis_cmd_cat_literal_if(cmd, async, "ASYNC"); // Kick off our command - if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) { + if (cluster_send_slot_cmd(c, slot, cmd, reply_type) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } // Our response callback - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); // Free our command - efree(cmd); + redis_cmd_free(cmd); } /* Generic routine for handling various commands which need to be directed at @@ -2415,10 +2437,10 @@ cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) { redisCluster *c = GET_CONTEXT(); - smart_string cmd = {0}; - zval *z_args; + RedisCmd *cmd; + int i, argc; + zval *argv; short slot; - int i, argc = ZEND_NUM_ARGS(); /* Commands using this pass-through don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { @@ -2428,50 +2450,39 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } /* We at least need the key or [host,port] argument */ - if (argc < 1) { + if (ZEND_NUM_ARGS() < 1) { php_error_docref(0, E_WARNING, "Command requires at least an argument to direct to a node"); RETURN_FALSE; } - /* Allocate an array to process arguments */ - z_args = emalloc(argc * sizeof(zval)); - - /* Grab args */ - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('*', argv, argc) + ZEND_PARSE_PARAMETERS_END(); /* First argument needs to be the "where" */ - if ((slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) { - efree(z_args); + if ((slot = cluster_cmd_get_slot(c, &argv[0])) < 0) { RETURN_FALSE; } - /* Initialize our command */ - redis_cmd_init_sstr(&cmd, argc-1, kw, kw_len); + cmd = redis_cmd_create(NULL, kw, kw_len); /* Iterate, appending args */ for(i = 1; i < argc; i++) { - zend_string *zstr = zval_get_string(&z_args[i]); - redis_cmd_append_sstr(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); + redis_cmd_cat_zval_zstr(cmd, &argv[i]); } /* Send it off */ - if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) { + if (cluster_send_slot_cmd(c, slot, cmd, TYPE_EOF) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); - efree(cmd.c); - efree(z_args); + redis_cmd_free(cmd); RETURN_FALSE; } /* Read the response variant */ - cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); - efree(cmd.c); - efree(z_args); + redis_cmd_free(cmd); } /* Generic method for HSCAN, SSCAN, and ZSCAN */ @@ -2479,9 +2490,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { redisCluster *c = GET_CONTEXT(); - char *cmd, *pat = NULL, *key = NULL; + char *pat = NULL, *key = NULL; size_t key_len = 0, pat_len = 0, pat_free = 0; - int cmd_len, key_free = 0; + int key_free = 0; + RedisCmd *cmd; short slot; zval *z_it; HashTable *hash; @@ -2529,15 +2541,14 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, } // Create command - cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, cursor, pat, pat_len, - count); + cmd = redis_fmt_scan_cmd(type, key, key_len, cursor, pat, pat_len, count); // Send it off - if (cluster_send_command(c, slot, cmd, cmd_len) == FAILURE) + if (cluster_send_command(c, slot, redis_cmd_str(cmd), redis_cmd_len(cmd)) == FAILURE) { CLUSTER_THROW_EXCEPTION("Couldn't send SCAN command", 0); if (key_free) efree(key); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } @@ -2547,7 +2558,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, { CLUSTER_THROW_EXCEPTION("Couldn't read SCAN response", 0); if (key_free) efree(key); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } @@ -2556,7 +2567,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, num_ele = zend_hash_num_elements(hash); // Free our command - efree(cmd); + redis_cmd_free(cmd); } while (c->flags->scan & REDIS_SCAN_RETRY && cursor != 0 && num_ele == 0); // Free our pattern @@ -2584,82 +2595,69 @@ static int redis_acl_op_readonly(zend_string *op) { PHP_METHOD(RedisCluster, acl) { redisCluster *c = GET_CONTEXT(); - smart_string cmdstr = {0}; - int argc = ZEND_NUM_ARGS(), i, readonly; + zval *argv, *znode; + zend_string *op; cluster_cb cb; - zend_string *zs; - zval *zargs; - void *ctx = NULL; - short slot; + RedisCmd *cmd; + int argc, i; - /* ACL in cluster needs a slot argument, and then at least the op */ - if (argc < 2) { - zend_wrong_param_count(); + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_ZVAL(znode) + Z_PARAM_STR(op) + Z_PARAM_OPTIONAL + Z_PARAM_VARIADIC('+', argv, argc) + ZEND_PARSE_PARAMETERS_END(); + + cmd = redis_cmd_create_literal(c->flags, "ACL"); + + if ((cmd->slot = cluster_cmd_get_slot(c, znode)) < 0) { + redis_cmd_free(cmd); RETURN_FALSE; } - /* Grab all our arguments and determine the command slot */ - zargs = emalloc(argc * sizeof(*zargs)); - if (zend_get_parameters_array(ht, argc, zargs) == FAILURE || - (slot = cluster_cmd_get_slot(c, &zargs[0]) < 0)) - { - efree(zargs); - RETURN_FALSE; - } + redis_cmd_cat_zstr(cmd, op); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1, "ACL"); - - /* Read the op, determine if it's readonly, and add it */ - zs = zval_get_string(&zargs[1]); - readonly = redis_acl_op_readonly(zs); - redis_cmd_append_sstr_zstr(&cmdstr, zs); - - /* We have specialized handlers for GETUSER and LOG, whereas every - * other ACL command can be handled generically */ - if (zend_string_equals_literal_ci(zs, "GETUSER")) { + /* We have specialized handlers for GETUSER and LOG, whereas every other ACL + * command can be handled generically */ + if (zend_string_equals_literal_ci(op, "GETUSER")) { cb = cluster_acl_getuser_resp; - } else if (zend_string_equals_literal_ci(zs, "LOG")) { + } else if (zend_string_equals_literal_ci(op, "LOG")) { cb = cluster_acl_log_resp; } else { cb = cluster_variant_resp; } - zend_string_release(zs); - /* Process remaining args */ - for (i = 2; i < argc; i++) { - zs = zval_get_string(&zargs[i]); - redis_cmd_append_sstr_zstr(&cmdstr, zs); - zend_string_release(zs); + for (i = 0; i < argc; i++) { + redis_cmd_cat_zval_zstr(cmd, &argv[i]); } /* Can we use replicas? */ - c->readonly = readonly && CLUSTER_IS_ATOMIC(c); + c->readonly = redis_acl_op_readonly(op) && CLUSTER_IS_ATOMIC(c); /* Kick off our command */ - if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, TYPE_EOF) < 0) { + if (cluster_send_slot_cmd(c, cmd->slot, cmd, TYPE_EOF) < 0) { + redis_cmd_free(cmd); CLUSTER_THROW_EXCEPTION("Unabler to send ACL command", 0); - efree(zargs); RETURN_FALSE; } if (CLUSTER_IS_ATOMIC(c)) { - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); } else { - cluster_enqueue_response(c, slot, cb, ctx); + cluster_enqueue_response(c, cmd->slot, cb, redis_empty_ctx); } - efree(cmdstr.c); - efree(zargs); + redis_cmd_free(cmd); } /* {{{ proto RedisCluster::scan(string master, long it [, string pat, long cnt]) */ PHP_METHOD(RedisCluster, scan) { redisCluster *c = GET_CONTEXT(); - char *cmd, *pat = NULL; + char *pat = NULL; size_t pat_len = 0; - int cmd_len; short slot; + RedisCmd *cmd; zval *zcursor, *z_node; long num_ele, pat_free = 0; zend_long count = 0; @@ -2701,18 +2699,18 @@ PHP_METHOD(RedisCluster, scan) { } /* Construct our command */ - cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, cursor, pat, pat_len, - count); + cmd = redis_fmt_scan_cmd(TYPE_SCAN, NULL, 0, cursor, pat, pat_len, count); if ((slot = cluster_cmd_get_slot(c, z_node)) < 0) { + redis_cmd_free(cmd); RETURN_FALSE; } // Send it to the node in question - if (cluster_send_command(c, slot, cmd, cmd_len) < 0) + if (cluster_send_command(c, slot, redis_cmd_str(cmd), redis_cmd_len(cmd)) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send SCAN to node", 0); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } @@ -2720,11 +2718,11 @@ PHP_METHOD(RedisCluster, scan) { &cursor) == FAILURE || Z_TYPE_P(return_value) != IS_ARRAY) { CLUSTER_THROW_EXCEPTION("Couldn't process SCAN response from node", 0); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } - efree(cmd); + redis_cmd_free(cmd); num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value)); } while (c->flags->scan & REDIS_SCAN_RETRY && cursor != 0 && num_ele == 0); @@ -2756,16 +2754,16 @@ PHP_METHOD(RedisCluster, hscan) { /* {{{ proto RedisCluster::save(string key) * proto RedisCluster::save(array host_port) */ PHP_METHOD(RedisCluster, save) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE", TYPE_LINE, - cluster_bool_resp); + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SAVE"), + TYPE_LINE, cluster_bool_resp); } /* }}} */ /* {{{ proto RedisCluster::bgsave(string key) * proto RedisCluster::bgsave(array host_port) */ PHP_METHOD(RedisCluster, bgsave) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE", - TYPE_LINE, cluster_bool_resp); + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("BGSAVE"), TYPE_LINE, cluster_bool_resp); } /* }}} */ @@ -2788,24 +2786,26 @@ PHP_METHOD(RedisCluster, flushall) { /* {{{ proto RedisCluster::dbsize(string key) * proto RedisCluster::dbsize(array host_port) */ PHP_METHOD(RedisCluster, dbsize) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DBSIZE", - TYPE_INT, cluster_long_resp); + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("DBSIZE"), TYPE_INT, cluster_long_resp); } /* }}} */ /* {{{ proto RedisCluster::bgrewriteaof(string key) * proto RedisCluster::bgrewriteaof(array host_port) */ PHP_METHOD(RedisCluster, bgrewriteaof) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGREWRITEAOF", - TYPE_LINE, cluster_bool_resp); + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("BGREWRITEAOF"), TYPE_LINE, + cluster_bool_resp); } /* }}} */ /* {{{ proto RedisCluster::lastsave(string key) * proto RedisCluster::lastsave(array $host_port) */ PHP_METHOD(RedisCluster, lastsave) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LASTSAVE", - TYPE_INT, cluster_long_resp); + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("LASTSAVE"), TYPE_INT, + cluster_long_resp); } /* }}} */ @@ -2814,10 +2814,8 @@ PHP_METHOD(RedisCluster, lastsave) { PHP_METHOD(RedisCluster, info) { redisCluster *c = GET_CONTEXT(); zval *node = NULL, *args = NULL; - smart_string cmdstr = {0}; REDIS_REPLY_TYPE rtype; - zend_string *section; - void *ctx = NULL; + RedisCmd *cmd; int i, argc; short slot; @@ -2830,31 +2828,29 @@ PHP_METHOD(RedisCluster, info) { if ((slot = cluster_cmd_get_slot(c, node)) < 0) RETURN_FALSE; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "INFO"); + cmd = redis_cmd_create_literal(c->flags, "INFO"); /* Direct this command at the master */ c->readonly = 0; for (i = 0; i < argc; i++) { - section = zval_get_string(&args[i]); - redis_cmd_append_sstr_zstr(&cmdstr, section); - zend_string_release(section); + redis_cmd_cat_zval_zstr(cmd, &args[i]); } rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; - if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, rtype) < 0) { + if (cluster_send_slot_cmd(c, slot, cmd, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send INFO command to specific node", 0); - efree(cmdstr.c); + redis_cmd_free(cmd); RETURN_FALSE; } if (CLUSTER_IS_ATOMIC(c)) { - cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); } else { - cluster_enqueue_response(c, slot, cluster_info_resp, ctx); + cluster_enqueue_response(c, slot, cluster_info_resp, redis_empty_ctx); } - efree(cmdstr.c); + redis_cmd_free(cmd); } /* }}} */ @@ -2865,17 +2861,16 @@ PHP_METHOD(RedisCluster, info) { */ PHP_METHOD(RedisCluster, client) { redisCluster *c = GET_CONTEXT(); - char *cmd, *opt = NULL, *arg = NULL; - int cmd_len; - size_t opt_len, arg_len = 0; + zend_string *op, *arg = NULL; REDIS_REPLY_TYPE rtype; + cluster_cb cb; + RedisCmd *cmd; zval *z_node; short slot; - cluster_cb cb; /* Parse args */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs|s", &z_node, &opt, - &opt_len, &arg, &arg_len) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zS|S", &z_node, &op, &arg) + == FAILURE) { RETURN_FALSE; } @@ -2885,15 +2880,15 @@ PHP_METHOD(RedisCluster, client) { if (slot < 0) RETURN_FALSE; /* Our return type and reply callback is different for all subcommands */ - if (opt_len == 4 && !strncasecmp(opt, "list", 4)) { + if (zend_string_equals_literal_ci(op, "LIST")) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; cb = cluster_client_list_resp; - } else if ((opt_len == 4 && !strncasecmp(opt, "kill", 4)) || - (opt_len == 7 && !strncasecmp(opt, "setname", 7))) + } else if (zend_string_equals_literal_ci(op, "KILL") || + zend_string_equals_literal_ci(op, "SETNAME")) { rtype = TYPE_LINE; cb = cluster_bool_resp; - } else if (opt_len == 7 && !strncasecmp(opt, "getname", 7)) { + } else if (zend_string_equals_literal_ci(op, "GETNAME")) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; cb = cluster_bulk_resp; } else { @@ -2902,40 +2897,32 @@ PHP_METHOD(RedisCluster, client) { RETURN_FALSE; } - /* Construct the command */ - if (ZEND_NUM_ARGS() == 3) { - cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "ss", - opt, opt_len, arg, arg_len); - } else if (ZEND_NUM_ARGS() == 2) { - cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "s", - opt, opt_len); - } else { - zend_wrong_param_count(); - RETURN_FALSE; - } + cmd = redis_cmd_create_literal(c->flags, "CLIENT"); + + redis_cmd_cat_zstr(cmd, op); + if (arg) + redis_cmd_cat_zstr(cmd, arg); /* Attempt to write our command */ - if (cluster_send_slot(c, slot, cmd, cmd_len, rtype) < 0) { + if (cluster_send_slot_cmd(c, slot, cmd, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send CLIENT command to specific node", 0); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } /* Now enqueue or process response */ if (CLUSTER_IS_ATOMIC(c)) { - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); } else { - void *ctx = NULL; - cluster_enqueue_response(c, slot, cb, ctx); + cluster_enqueue_response(c, slot, cb, redis_empty_ctx); } - efree(cmd); + redis_cmd_free(cmd); } /* {{{ proto mixed RedisCluster::cluster(variant) */ PHP_METHOD(RedisCluster, cluster) { - cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CLUSTER", - sizeof("CLUSTER")-1); + cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("CLUSTER")); } /* }}} */ @@ -2944,16 +2931,14 @@ PHP_METHOD(RedisCluster, cluster) { /* {{{ proto mixed RedisCluster::config(string key, ...) * proto mixed RedisCluster::config(array host_port, ...) */ PHP_METHOD(RedisCluster, config) { - cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CONFIG", - sizeof("CONFIG")-1); + cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("CONFIG")); } /* }}} */ /* {{{ proto mixed RedisCluster::pubsub(string key, ...) * proto mixed RedisCluster::pubsub(array host_port, ...) */ PHP_METHOD(RedisCluster, pubsub) { - cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUBSUB", - sizeof("PUBSUB")-1); + cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PUBSUB")); } /* }}} */ @@ -2961,7 +2946,7 @@ PHP_METHOD(RedisCluster, pubsub) { * proto mixed RedisCluster::script(array host_port, ...) */ PHP_METHOD(RedisCluster, script) { redisCluster *c = GET_CONTEXT(); - smart_string cmd = {0}; + RedisCmd *cmd; zval *z_args; short slot; int argc = ZEND_NUM_ARGS(); @@ -2986,24 +2971,24 @@ PHP_METHOD(RedisCluster, script) { /* Grab args */ if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0 || - redis_build_script_cmd(&cmd, argc - 1, &z_args[1]) == NULL + (cmd = redis_build_script_cmd(argc - 1, &z_args[1])) == NULL ) { efree(z_args); RETURN_FALSE; } /* Send it off */ - if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) { + if (cluster_send_slot_cmd(c, slot, cmd, TYPE_EOF) < 0) { CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0); - efree(cmd.c); + redis_cmd_free(cmd); efree(z_args); RETURN_FALSE; } /* Read the response variant */ - cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); - efree(cmd.c); + redis_cmd_free(cmd); efree(z_args); } /* }}} */ @@ -3011,8 +2996,7 @@ PHP_METHOD(RedisCluster, script) { /* {{{ proto mixed RedisCluster::slowlog(string key, ...) * proto mixed RedisCluster::slowlog(array host_port, ...) */ PHP_METHOD(RedisCluster, slowlog) { - cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SLOWLOG", - sizeof("SLOWLOG")-1); + cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SLOWLOG")); } /* }}} */ @@ -3068,14 +3052,14 @@ PHP_METHOD(RedisCluster, geosearchstore) { /* {{{ proto array RedisCluster::role(string key) * proto array RedisCluster::role(array host_port) */ PHP_METHOD(RedisCluster, role) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ROLE", + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLE"), TYPE_MULTIBULK, cluster_variant_resp); } /* {{{ proto array RedisCluster::time(string key) * proto array RedisCluster::time(array host_port) */ PHP_METHOD(RedisCluster, time) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "TIME", + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TIME"), TYPE_MULTIBULK, cluster_variant_resp); } /* }}} */ @@ -3083,8 +3067,9 @@ PHP_METHOD(RedisCluster, time) { /* {{{ proto string RedisCluster::randomkey(string key) * proto string RedisCluster::randomkey(array host_port) */ PHP_METHOD(RedisCluster, randomkey) { - cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RANDOMKEY", - TYPE_BULK, cluster_bulk_resp); + cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + ZEND_STRL("RANDOMKEY"), TYPE_BULK, + cluster_bulk_resp); } /* }}} */ @@ -3094,9 +3079,7 @@ void cluster_gen_wait_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, { zend_long numreplicas, timeout, numlocal = 0; redisCluster *c = GET_CONTEXT(); - smart_string cmdstr = {0}; - void *ctx = NULL; - short slot; + RedisCmd *cmd; zval *node; int argc; @@ -3116,33 +3099,36 @@ void cluster_gen_wait_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, RETURN_FALSE; } - slot = cluster_cmd_get_slot(c, node); - if (slot < 0) { + cmd = redis_cmd_create(c->flags, kw, kwlen); + + cmd->slot = cluster_cmd_get_slot(c, node); + if (cmd->slot < 0) { RETURN_FALSE; } - redis_cmd_init_sstr(&cmdstr, argc - 1, (char*)kw, kwlen); if (has_local) { - redis_cmd_append_sstr_long(&cmdstr, numlocal); + redis_cmd_cat_long(cmd, numlocal); } - redis_cmd_append_sstr_long(&cmdstr, numreplicas); - redis_cmd_append_sstr_long(&cmdstr, timeout); + + redis_cmd_cat_long(cmd, numreplicas); + redis_cmd_cat_long(cmd, timeout); c->readonly = 0; - if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, reply_type) < 0) { + if (cluster_send_slot_cmd(c, cmd->slot, cmd, reply_type) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0); - smart_string_free(&cmdstr); + redis_cmd_free(cmd); RETURN_FALSE; } if (CLUSTER_IS_ATOMIC(c)) { - cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); } else { - cluster_enqueue_response(c, slot, cluster_variant_resp, ctx); + cluster_enqueue_response(c, cmd->slot, cluster_variant_resp, + redis_empty_ctx); } - smart_string_free(&cmdstr); + redis_cmd_free(cmd); } PHP_METHOD(RedisCluster, wait) { @@ -3160,15 +3146,12 @@ PHP_METHOD(RedisCluster, waitaof) { PHP_METHOD(RedisCluster, ping) { redisCluster *c = GET_CONTEXT(); REDIS_REPLY_TYPE rtype; - void *ctx = NULL; + zend_string *arg = NULL; zval *z_node; - char *cmd, *arg = NULL; - int cmdlen; - size_t arglen; + RedisCmd *cmd; short slot; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!", &z_node, &arg, - &arglen) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|S!", &z_node, &arg) == FAILURE) { RETURN_FALSE; } @@ -3182,28 +3165,27 @@ PHP_METHOD(RedisCluster, ping) { RETURN_FALSE; } - /* Construct our command */ - if (arg != NULL) { - cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", "s", arg, arglen); - } else { - cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", ""); - } + cmd = redis_cmd_create_literal(c->flags, "PING"); + if (arg) + redis_cmd_cat_zstr(cmd, arg); /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) && arg != NULL ? TYPE_BULK : TYPE_LINE; - if (cluster_send_slot(c, slot, cmd, cmdlen, rtype) < 0) { - CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0); - efree(cmd); + if (cluster_send_slot_cmd(c, slot, cmd, rtype) < 0) { + CLUSTER_THROW_EXCEPTION( + "Unable to send command at the specified node", 0); + redis_cmd_free(cmd); RETURN_FALSE; } /* We're done with our command */ - efree(cmd); + redis_cmd_free(cmd); /* Process response */ if (CLUSTER_IS_ATOMIC(c)) { if (arg != NULL) { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); } else { /* If we're atomic and didn't send an argument then we have already * processed the reply (which must have been successful. */ @@ -3211,9 +3193,11 @@ PHP_METHOD(RedisCluster, ping) { } } else { if (arg != NULL) { - cluster_enqueue_response(c, slot, cluster_bulk_resp, ctx); + cluster_enqueue_response(c, slot, cluster_bulk_resp, + redis_empty_ctx); } else { - cluster_enqueue_response(c, slot, cluster_variant_resp, ctx); + cluster_enqueue_response(c, slot, cluster_variant_resp, + redis_empty_ctx); } RETURN_ZVAL(getThis(), 1, 0); @@ -3352,15 +3336,12 @@ PHP_METHOD(RedisCluster, xtrim) { PHP_METHOD(RedisCluster, echo) { redisCluster *c = GET_CONTEXT(); REDIS_REPLY_TYPE rtype; + zend_string *msg; + RedisCmd *cmd; zval *z_arg; - char *cmd, *msg; - int cmd_len; - size_t msg_len; short slot; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &z_arg, &msg, - &msg_len) == FAILURE) - { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zS", &z_arg, &msg) == FAILURE) { RETURN_FALSE; } @@ -3374,25 +3355,25 @@ PHP_METHOD(RedisCluster, echo) { } /* Construct our command */ - cmd_len = redis_spprintf(NULL, NULL, &cmd, "ECHO", "s", msg, msg_len); + cmd = redis_cmd_fmt(NULL, "ECHO", "S", msg); /* Send it off */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; - if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) { - CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0); - efree(cmd); + if (cluster_send_slot_cmd(c, slot, cmd, rtype) < 0) { + CLUSTER_THROW_EXCEPTION( + "Unable to send command at the specified node", 0); + redis_cmd_free(cmd); RETURN_FALSE; } /* Process bulk response */ if (CLUSTER_IS_ATOMIC(c)) { - cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, redis_empty_ctx); } else { - void *ctx = NULL; - cluster_enqueue_response(c, slot, cluster_bulk_resp, ctx); + cluster_enqueue_response(c, slot, cluster_bulk_resp, redis_empty_ctx); } - efree(cmd); + redis_cmd_free(cmd); } /* }}} */ @@ -3400,11 +3381,10 @@ PHP_METHOD(RedisCluster, echo) { * proto mixed RedisCluster::rawcommand(array $host_port, string $cmd, [ $argv1 .. $argvN]) */ PHP_METHOD(RedisCluster, rawcommand) { REDIS_REPLY_TYPE rtype; - int argc = ZEND_NUM_ARGS(), cmd_len; + int argc = ZEND_NUM_ARGS(); redisCluster *c = GET_CONTEXT(); - char *cmd = NULL; + RedisCmd *cmd; zval *z_args; - short slot; /* Sanity check on our arguments */ if (argc < 2) { @@ -3418,10 +3398,10 @@ PHP_METHOD(RedisCluster, rawcommand) { "Internal PHP error parsing method parameters."); efree(z_args); RETURN_FALSE; - } else if (redis_build_raw_cmd(&z_args[1], argc-1, &cmd, &cmd_len) || - (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) + } else if ((cmd = redis_build_raw_cmd(&z_args[1], argc-1)) == NULL || + (cmd->slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) { - if (cmd) efree(cmd); + redis_cmd_free(cmd); efree(z_args); RETURN_FALSE; } @@ -3431,21 +3411,22 @@ PHP_METHOD(RedisCluster, rawcommand) { /* Direct the command */ rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE; - if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) { + if (cluster_send_slot_cmd(c, cmd->slot, cmd, rtype) < 0) { CLUSTER_THROW_EXCEPTION("Unable to send command to the specified node", 0); - efree(cmd); + redis_cmd_free(cmd); RETURN_FALSE; } /* Process variant response */ if (CLUSTER_IS_ATOMIC(c)) { - cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, + redis_empty_ctx); } else { - void *ctx = NULL; - cluster_enqueue_response(c, slot, cluster_variant_raw_resp, ctx); + cluster_enqueue_response(c, cmd->slot, cluster_variant_raw_resp, + redis_empty_ctx); } - efree(cmd); + redis_cmd_free(cmd); } /* }}} */ @@ -3453,7 +3434,7 @@ PHP_METHOD(RedisCluster, rawcommand) { * proto array RedisCluster::command('INFO', string cmd) * proto array RedisCluster::command('GETKEYS', array cmd_args) */ PHP_METHOD(RedisCluster, command) { - CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); + CLUSTER_PROCESS_CMD(command, cluster_variant_resp_strings, 0); } PHP_METHOD(RedisCluster, copy) { @@ -3465,4 +3446,3 @@ PHP_METHOD(RedisCluster, digest) { } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ - diff --git a/redis_cmd.c b/redis_cmd.c new file mode 100644 index 00000000..c7cd667c --- /dev/null +++ b/redis_cmd.c @@ -0,0 +1,490 @@ +#include "php.h" +#include "common.h" +#include "redis_cmd.h" +#include "library.h" +#include "cluster_library.h" +#include "Zend/zend_smart_str.h" + +// Max possible: "*4294967295\r\n" +#define REDIS_MB_HDR "PHPREDISPHP\r\n" + +#define REDIS_MB_HDR_LEN (sizeof(REDIS_MB_HDR) - 1) + +typedef struct RespHeader { + char *str; + size_t len; +} RespHeader; + +static const RespHeader bulk_header[] = { + {ZEND_STRL("$0\r\n")}, {ZEND_STRL("$1\r\n")}, {ZEND_STRL("$2\r\n")}, + {ZEND_STRL("$3\r\n")}, {ZEND_STRL("$4\r\n")}, {ZEND_STRL("$5\r\n")}, + {ZEND_STRL("$6\r\n")}, {ZEND_STRL("$7\r\n")}, {ZEND_STRL("$8\r\n")}, + {ZEND_STRL("$9\r\n")}, {ZEND_STRL("$10\r\n")}, {ZEND_STRL("$11\r\n")}, + {ZEND_STRL("$12\r\n")}, {ZEND_STRL("$13\r\n")}, {ZEND_STRL("$14\r\n")}, + {ZEND_STRL("$15\r\n")}, {ZEND_STRL("$16\r\n")}, {ZEND_STRL("$17\r\n")}, + {ZEND_STRL("$18\r\n")}, {ZEND_STRL("$19\r\n")}, {ZEND_STRL("$20\r\n")}, + {ZEND_STRL("$21\r\n")}, {ZEND_STRL("$22\r\n")}, {ZEND_STRL("$23\r\n")}, + {ZEND_STRL("$24\r\n")}, {ZEND_STRL("$25\r\n")}, {ZEND_STRL("$26\r\n")}, + {ZEND_STRL("$27\r\n")}, {ZEND_STRL("$28\r\n")}, {ZEND_STRL("$29\r\n")}, + {ZEND_STRL("$30\r\n")}, {ZEND_STRL("$31\r\n")}, {ZEND_STRL("$32\r\n")}, +}; + +#define BULK_HDR_STRL(n) bulk_header[n].str, bulk_header[n].len + +/* zend_print_ulong_to_buf will null terminate the string which we do not want */ +static inline char *print_u64_to_buf(char *buf, uint64_t n) { + do { + *--buf = (char)(n % 10) + '0'; + n /= 10; + } while (n > 0); + + return buf; +} + +/* zend_print_ulong_to_buf will null terminate the string which we do not want */ +static inline char *print_u32_to_buf(char *buf, uint32_t n) { + return print_u64_to_buf(buf, n); +} + +static RedisCmd *redis_cmd_alloc(RedisSock *redis_sock) { + RedisCmd *cmd; + + cmd = ecalloc(1, sizeof(*cmd)); + cmd->redis_sock = redis_sock; + cmd->slot = -1; + + return cmd; +} + +RedisCmd * +redis_cmd_create(RedisSock *redis_sock, const char *kw, size_t kwlen) { + RedisCmd *cmd; + + cmd = redis_cmd_alloc(redis_sock); + + smart_string_appendl_ex(&cmd->s, ZEND_STRL(REDIS_MB_HDR), 0); + + redis_cmd_cat_str(cmd, kw, kwlen); + + return cmd; +} + +void redis_cmd_reset(RedisCmd *cmd, const char *kw, size_t kwlen) { + redis_cmd_free_ctx(cmd); + smart_string_reset(&cmd->s); + + cmd->argc = 0; + cmd->head = NULL; + cmd->slot = -1; + + smart_string_appendl_ex(&cmd->s, ZEND_STRL(REDIS_MB_HDR), 0); + redis_cmd_cat_str(cmd, kw, kwlen); +} + +void redis_cmd_free_ctx(RedisCmd *cmd) { + redis_cmd_ctx_free(cmd->ctx); + memset(&cmd->ctx, 0, sizeof(RedisCmdCtx)); +} + +void redis_cmd_free_ex(RedisCmd *cmd, int free_ctx) { + if (cmd == NULL) + return; + + smart_string_free(&cmd->s); + + if (free_ctx) { + redis_cmd_free_ctx(cmd); + } + + efree(cmd); +} + +static void redis_cmd_finalize(RedisCmd *cmd) { + char *eptr; + + if (cmd->head != NULL) + return; + + if (cmd->redis_sock != NULL && cmd->redis_sock->type == REDIS_SOCK_CLUSTER) { + ZEND_ASSERT(cmd->slot >= 0 && cmd->slot < REDIS_CLUSTER_SLOTS); + + if (UNEXPECTED(cmd->slot < 0 || cmd->slot >= REDIS_CLUSTER_SLOTS)) { + zend_error_noreturn(E_ERROR, "Invalid Redis Cluster slot %d", cmd->slot); + } + } + + /* Set the end pointer right before the trailing \r\n and print argc */ + eptr = cmd->s.c + REDIS_MB_HDR_LEN - 2; + cmd->head = print_u32_to_buf(eptr, cmd->argc); + + /* Prepend the multibulk reply-type */ + *--cmd->head = '*'; + + /* Null-terminate the string */ + smart_string_0(&cmd->s); +} + +void redis_cmd_cat_str(RedisCmd *cmd, const char *str, size_t len) { + if (cmd->argc == UINT32_MAX) { + zend_error_noreturn(E_ERROR, "Too many arguments"); + } + + if (EXPECTED(len < sizeof(bulk_header) / sizeof(RespHeader))) { + smart_string_appendl_ex(&cmd->s, BULK_HDR_STRL(len), 0); + } else { + smart_string_appendc(&cmd->s, '$'); + smart_string_append_unsigned(&cmd->s, len); + smart_string_appendl_ex(&cmd->s, ZEND_STRL("\r\n"), 0); + } + + smart_string_appendl(&cmd->s, str, len); + smart_string_appendl_ex(&cmd->s, ZEND_STRL("\r\n"), 0); + + cmd->argc++; +} + +void redis_cmd_cat_arrkey(RedisCmd *cmd, zend_string *str, zend_ulong idx) { + if (str) { + redis_cmd_cat_zstr(cmd, str); + } else { + redis_cmd_cat_ulong(cmd, idx); + } +} + +static inline zend_bool +update_slot(RedisCmd *cmd, const char *key, size_t keylen) { + short slot; + + if (cmd->redis_sock == NULL || cmd->redis_sock->type != REDIS_SOCK_CLUSTER) + return 1; + + slot = cluster_hash_key(key, keylen); + + if (cmd->slot == -1) { + cmd->slot = slot; + return 1; + } + + return cmd->slot == slot ? 1 : 0; +} + +zend_bool redis_cmd_cat_key_str(RedisCmd *cmd, const char *key, size_t len) { + char *aux = (char*)key; + int keyfree; + + if (UNEXPECTED(cmd->redis_sock == NULL)) { + redis_cmd_cat_str(cmd, key, len); + return 1; + } + + keyfree = redis_key_prefix(cmd->redis_sock, &aux, &len); + + if (!update_slot(cmd, aux, len)) { + if (keyfree) efree(aux); + return 0; + } + + redis_cmd_cat_str(cmd, aux, len); + if (keyfree) efree(aux); + + return 1; +} + +zend_bool redis_cmd_cat_key_zval(RedisCmd *cmd, zval *zv) { + zend_string *zstr, *tmp; + int ret; + + zstr = zval_get_tmp_string(zv, &tmp); + ret = redis_cmd_cat_key_zstr(cmd, zstr); + zend_tmp_string_release(tmp); + + return ret; +} + +zend_bool redis_cmd_cat_key_zstr(RedisCmd *cmd, zend_string *key) { + if (UNEXPECTED(cmd->redis_sock == NULL)) { + redis_cmd_cat_zstr(cmd, key); + return 1; + } + + key = redis_key_prefix_zstr(cmd->redis_sock, key); + + if (!update_slot(cmd, ZSTR_VAL(key), ZSTR_LEN(key))) { + zend_string_release(key); + return 0; + } + + redis_cmd_cat_zstr(cmd, key); + zend_string_release(key); + + return 1; +} + +zend_bool redis_cmd_cat_key_long(RedisCmd *cmd, zend_long lval) { + char buf[21], *key; + int keyfree; + size_t len; + + key = zend_print_long_to_buf(buf + sizeof(buf) - 1, lval); + len = buf + sizeof(buf) - 1 - key; + + if (UNEXPECTED(cmd->redis_sock == NULL)) { + redis_cmd_cat_str(cmd, key, len); + return 1; + } + + keyfree = redis_key_prefix(cmd->redis_sock, &key, &len); + + if (!update_slot(cmd, key, len)) { + if (keyfree) efree(key); + return 0; + } + + redis_cmd_cat_str(cmd, key, len); + if (keyfree) efree(key); + + return 1; +} + +zend_bool redis_cmd_cat_key_arrkey(RedisCmd *cmd, zend_string *str, zend_ulong idx) { + char buf[21], *key; + int keyfree; + size_t len; + + if (str == NULL) { + key = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, idx); + len = buf + sizeof(buf) - 1 - key; + } else { + key = ZSTR_VAL(str); + len = ZSTR_LEN(str); + } + + if (UNEXPECTED(cmd->redis_sock == NULL)) { + redis_cmd_cat_str(cmd, key, len); + return 1; + } + + keyfree = redis_key_prefix(cmd->redis_sock, &key, &len); + if (!update_slot(cmd, key, len)) { + if (keyfree) efree(key); + return 0; + } + + redis_cmd_cat_str(cmd, key, len); + if (keyfree) efree(key); + + return 1; +} + +void redis_cmd_cat_zstr(RedisCmd *cmd, zend_string *str) { + redis_cmd_cat_str(cmd, ZSTR_VAL(str), ZSTR_LEN(str)); +} + +void redis_cmd_cat_long(RedisCmd *cmd, zend_long lval) { + char buf[21], *res; + int len; + + res = zend_print_long_to_buf(buf + sizeof(buf) - 1, lval); + len = buf + sizeof(buf) - 1 - res; + + redis_cmd_cat_str(cmd, res, len); +} + +void redis_cmd_cat_ulong(RedisCmd *cmd, zend_ulong lval) { + char buf[21], *res; + int len; + + res = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, lval); + len = buf + sizeof(buf) - 1 - res; + + redis_cmd_cat_str(cmd, res, len); +} + +void redis_cmd_cat_u64(RedisCmd *cmd, uint64_t u64) { + char buf[21], *res; + int len; + + res = print_u64_to_buf(buf + sizeof(buf) - 1, u64); + len = buf + sizeof(buf) - 1 - res; + + redis_cmd_cat_str(cmd, res, len); +} + +void redis_cmd_cat_u32(RedisCmd *cmd, uint32_t u32) { + return redis_cmd_cat_u64(cmd, u32); +} + +void redis_cmd_cat_double(RedisCmd *cmd, double dval) { +#if PHP_VERSION_ID >= 80200 + char buf[ZEND_DOUBLE_MAX_LENGTH], *nul; + + zend_gcvt(dval, 17, '.', 'e', buf); + + nul = memchr(buf, 0, sizeof(buf)); + ZEND_ASSERT(nul != NULL); + + redis_cmd_cat_str(cmd, buf, nul - buf); + +#else + char buf[64], *p; + int len; + + len = snprintf(buf, sizeof(buf), "%.17g", dval); + + /* Legacy behavior locale fixup */ + if ((p = strchr(buf, ',')) != NULL) + *p = '.'; + + redis_cmd_cat_str(cmd, buf, len); +#endif +} + +void redis_cmd_cat_zval_zstr(RedisCmd *cmd, zval *zv) { + zend_string *zstr, *tmp; + + switch (Z_TYPE_P(zv)) { + case IS_FALSE: + redis_cmd_cat_str(cmd, ZEND_STRL("0")); + return; + case IS_TRUE: + redis_cmd_cat_str(cmd, ZEND_STRL("1")); + return; + case IS_LONG: + redis_cmd_cat_long(cmd, Z_LVAL_P(zv)); + return; + case IS_DOUBLE: + redis_cmd_cat_double(cmd, Z_DVAL_P(zv)); + return; + default: + zstr = zval_get_tmp_string(zv, &tmp); + redis_cmd_cat_zstr(cmd, zstr); + zend_tmp_string_release(tmp); + } +} + +void redis_cmd_cat_zval(RedisCmd *cmd, zval *zv) { + int valfree; + size_t len; + char *str; + + if (UNEXPECTED(cmd->redis_sock == NULL)) { + redis_cmd_cat_zval_zstr(cmd, zv); + return; + } + + valfree = redis_pack(cmd->redis_sock, zv, &str, &len); + redis_cmd_cat_str(cmd, str, len); + + if (valfree) + efree(str); +} + +/* A printf like method to construct a Redis RESP command. It has been extended + * to take a few different format specifiers that are convenient to phpredis. + * + * s - C string followed by length as a + * S - Pointer to a zend_string + * k - Same as 's' but the value will be prefixed if phpredis is set up do do + * that and the working slot will be set if it has been passed. + * K - Same as 'S' but the value will be prefixed if phpredis is set up do do + * v - A z_val which will be serialized if phpredis is configured to serialize. + * f - A double value + * F - Alias to 'f' + * i - An integer + * d - Alias to 'i' + * l - A long + * L - Alias to 'l' + */ +RedisCmd *redis_cmd_fmt(RedisSock *redis_sock, char *kw, char *fmt, ...) { + RedisCmd *ret; + va_list ap; + size_t len; + char *str; + + va_start(ap, fmt); + + ret = redis_cmd_create(redis_sock, kw, strlen(kw)); + + while (*fmt) { + switch (*fmt) { + case 's': + str = va_arg(ap, char*); + len = va_arg(ap, size_t); + redis_cmd_cat_str(ret, str, len); + break; + case 'S': + redis_cmd_cat_zstr(ret, va_arg(ap, zend_string*)); + break; + case 'k': + str = va_arg(ap, char*); + len = va_arg(ap, size_t); + redis_cmd_cat_key_str(ret, str, len); + break; + case 'K': + redis_cmd_cat_key_zstr(ret, va_arg(ap, zend_string*)); + break; + case 'v': + redis_cmd_cat_zval(ret, va_arg(ap, zval*)); + break; + case 'f': + case 'F': + redis_cmd_cat_double(ret, va_arg(ap, double)); + break; + case 'i': + case 'd': + redis_cmd_cat_long(ret, va_arg(ap, int)); + break; + case 'l': + case 'L': + redis_cmd_cat_long(ret, va_arg(ap, zend_long)); + break; + } + + fmt++; + } + + va_end(ap); + + return ret; +} + +void redis_cmd_set_ctx_ex(RedisCmd *cmd, void *ctx, RedisCmdCtxDtor *dtor) { + cmd->ctx.ptr = ctx; + cmd->ctx.dtor = dtor; +} + +const char *redis_cmd_str(RedisCmd *cmd) { + redis_cmd_finalize(cmd); + + return cmd->head; +} + +size_t redis_cmd_len(RedisCmd *cmd) { + redis_cmd_finalize(cmd); + + return cmd->s.len - (cmd->head - cmd->s.c); +} + +void redis_cmd_randslot(RedisCmd *cmd) { + if (cmd->redis_sock && cmd->redis_sock->type == REDIS_SOCK_CLUSTER) + cmd->slot = rand() % REDIS_CLUSTER_MOD; +} + +void resp_str_cat_str(smart_str *s, const char *str, zend_ulong len) { + smart_str_appendc(s, '$'); + smart_str_append_unsigned(s, len); + smart_str_appendl_ex(s, ZEND_STRL("\r\n"), 0); + smart_str_appendl(s, str, len); + smart_str_appendl_ex(s, ZEND_STRL("\r\n"), 0); +} + +void resp_str_cat_cmd(smart_str *s, zend_ulong argc, const char *kw, size_t len) { + smart_str_appendc(s, '*'); + smart_str_append_unsigned(s, 1 + argc); + smart_str_appendl_ex(s, ZEND_STRL("\r\n"), 0); + resp_str_cat_str(s, kw, len); +} + +void resp_str_cat_zstr(smart_str *s, zend_string *str) { + resp_str_cat_str(s, ZSTR_VAL(str), ZSTR_LEN(str)); +} diff --git a/redis_cmd.h b/redis_cmd.h new file mode 100644 index 00000000..643f091d --- /dev/null +++ b/redis_cmd.h @@ -0,0 +1,111 @@ +#ifndef REDIS_CMD_H +#define REDIS_CMD_H + +#include "php.h" +#include + +#include "common.h" + +typedef struct RedisCmd { + RedisSock *redis_sock; + short slot; + smart_string s; + RedisCmdCtx ctx; + uint32_t argc; + char *head; +} RedisCmd; + +#define redis_cmd_create_literal(redis_sock, kw) \ + redis_cmd_create(redis_sock, kw, sizeof(kw) - 1) + +#define redis_cmd_cat_literal(cmd, lit) \ + redis_cmd_cat_str(cmd, lit, sizeof(lit) - 1) + +#define redis_cmd_cat_literal_if(cmd, opt, lit) do { \ + if (opt) \ + redis_cmd_cat_str(cmd, lit, sizeof(lit) - 1); \ +} while (0) + +#define redis_cmd_cat_zstr_if(cmd, opt, zstr) do { \ + if (opt) \ + redis_cmd_cat_zstr(cmd, zstr); \ +} while (0) + +RedisCmd *redis_cmd_create(RedisSock *redis_sock, const char *kw, size_t kwlen); +void redis_cmd_reset(RedisCmd *cmd, const char *kw, size_t kwlen); +void redis_cmd_free_ex(RedisCmd *cmd, int free_ctx); +void redis_cmd_free_ctx(RedisCmd *cmd); + +static inline RedisCmdCtx redis_cmd_pop_ctx(RedisCmd *cmd) { + RedisCmdCtx ctx = cmd->ctx; + + memset(&cmd->ctx, 0, sizeof(RedisCmdCtx)); + + return ctx; +} + +static inline void redis_cmd_free(RedisCmd *cmd) { + redis_cmd_free_ex(cmd, 0); +} + +static inline RedisCmd * +redis_cmd_create_zstr(RedisSock *redis_sock, zend_string *kw) { + return redis_cmd_create(redis_sock, ZSTR_VAL(kw), ZSTR_LEN(kw)); +} + +void redis_cmd_cat_zstr(RedisCmd *cmd, zend_string *str); +void redis_cmd_cat_str(RedisCmd *cmd, const char *str, size_t len); +void redis_cmd_cat_long(RedisCmd *cmd, zend_long lval); +void redis_cmd_cat_ulong(RedisCmd *cmd, zend_ulong ulval); +void redis_cmd_cat_u32(RedisCmd *cmd, uint32_t u32); +void redis_cmd_cat_u64(RedisCmd *cmd, uint64_t u64); +void redis_cmd_cat_double(RedisCmd *cmd, double dval); + +zend_bool redis_cmd_cat_key_zval(RedisCmd *cmd, zval *zv); +zend_bool redis_cmd_cat_key_zstr(RedisCmd *cmd, zend_string *key); +zend_bool redis_cmd_cat_key_long(RedisCmd *cmd, zend_long key); +zend_bool redis_cmd_cat_key_str(RedisCmd *cmd, const char *key, size_t len); +zend_bool redis_cmd_cat_key_arrkey(RedisCmd *cmd, zend_string *str, zend_ulong idx); + +void redis_cmd_cat_zval(RedisCmd *cmd, zval *zv); +void redis_cmd_cat_zval_zstr(RedisCmd *cmd, zval *zv); + +void redis_cmd_cat_arrkey(RedisCmd *cmd, zend_string *str, zend_ulong idx); + +void redis_cmd_set_ctx_ex(RedisCmd *cmd, void *ptr, RedisCmdCtxDtor *dtor); + +static zend_always_inline void redis_cmd_set_ctx(RedisCmd *cmd, void *ptr) { + redis_cmd_set_ctx_ex(cmd, ptr, NULL); +} + +static zend_always_inline void +redis_cmd_set_ctx_u64(RedisCmd *cmd, uint64_t u64) { + redis_cmd_set_ctx(cmd, (void*)(uintptr_t)u64); +} + +void redis_cmd_randslot(RedisCmd *cmd); + +static inline void redis_cmd_ctx_efree(void *ptr) { + if (ptr) efree(ptr); +} + +RedisCmd *redis_cmd_fmt(RedisSock *redis_sock, char *kw, char *fmt, ...); + +#define redis_cmd_fmt_literal(redis_sock, kw, fmt, ...) \ + redis_cmd_fmt(redis_sock, kw, fmt, ##__VA_ARGS__) + +const char *redis_cmd_str(RedisCmd *cmd); +size_t redis_cmd_len(RedisCmd *cmd); + +/* Specialized thin wrapper around smart_str for the times we want to construct + * one buffer with 1-N commands */ +void resp_str_cat_str(smart_str *s, const char *str, zend_ulong len); +void resp_str_cat_cmd(smart_str *s, zend_ulong argc, const char *kw, size_t len); +void resp_str_cat_zstr(smart_str *s, zend_string *str); + +#define resp_str_cat_cmd_literal(s_, kw_, argc_) \ + resp_str_cat_cmd(s_, argc_, kw_, sizeof(kw_) - 1) +#define resp_str_cat_literal(s_, lit_) \ + resp_str_cat_str(s_, lit_, sizeof(lit_) - 1) + +#endif // REDIS_CMD_H diff --git a/redis_commands.c b/redis_commands.c index 8c851864..560855a8 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -64,6 +64,33 @@ typedef struct geoOptions { zend_string *key; } geoOptions; +typedef enum geosearchShape { + GEOSEARCH_SHAPE_RADIUS, + GEOSEARCH_SHAPE_BOX, + GEOSEARCH_SHAPE_POLYGON +} geosearchShape; + +typedef enum geosearchPosition { + GEOSEARCH_POSITION_NONE, + GEOSEARCH_POSITION_MEMBER, + GEOSEARCH_POSITION_LONLAT +} geosearchPosition; + +typedef struct geoSearchOptions { + geosearchShape shape_type; + geosearchPosition position_type; + union { + double radius; + HashTable *dimensions; + } shape; + union { + zend_string *member; + HashTable *coordinates; + } position; + int shape_argc; + int position_argc; +} geoSearchOptions; + typedef struct redisLcsOptions { zend_bool len; zend_bool idx; @@ -139,84 +166,129 @@ typedef struct redisZcmdOptions { } limit; } redisZcmdOptions; -/* Local passthrough macro for command construction. Given that these methods - * are generic (so they work whether the caller is Redis or RedisCluster) we - * will always have redis_sock, slot*, and */ -#define REDIS_CMD_SPPRINTF(ret, kw, fmt, ...) \ - redis_spprintf(redis_sock, slot, ret, kw, fmt, ##__VA_ARGS__) +static zend_always_inline void redis_crosslot_warning(void) { + php_error_docref(NULL, E_WARNING, + "Warning, not all keys hash to the same slot!"); +} + +#define redis_cmd_try_cat_key(cmd, fn, ...) \ + do { \ + if (!fn((cmd), __VA_ARGS__)) { \ + redis_crosslot_warning(); \ + redis_cmd_free_ex(cmd, 1); \ + return NULL; \ + } \ + } while (0) + +#define redis_cmd_try_cat_key_zstr(cmd, zstr) \ + redis_cmd_try_cat_key(cmd, redis_cmd_cat_key_zstr, zstr) +#define redis_cmd_try_cat_key_zval(cmd, zval) \ + redis_cmd_try_cat_key(cmd, redis_cmd_cat_key_zval, zval) +#define redis_cmd_try_cat_key_str(cmd, str, len) \ + redis_cmd_try_cat_key(cmd, redis_cmd_cat_key_str, str, len) +#define redis_cmd_try_cat_key_long(cmd, lval) \ + redis_cmd_try_cat_key(cmd, redis_cmd_cat_key_long, lval) + +static zend_always_inline void +redis_cmd_cat_channel_zstr(RedisCmd *cmd, zend_string *channel) { + zend_string *prefixed; + + if (UNEXPECTED(cmd->redis_sock == NULL)) { + redis_cmd_cat_zstr(cmd, channel); + return; + } + + prefixed = redis_key_prefix_zstr(cmd->redis_sock, channel); + + if (cmd->redis_sock->type == REDIS_SOCK_CLUSTER) { + cmd->slot = cluster_hash_key(ZSTR_VAL(prefixed), ZSTR_LEN(prefixed)); + } + + redis_cmd_cat_zstr(cmd, prefixed); + zend_string_release(prefixed); +} + +static zend_always_inline void +redis_cmd_cat_channel_zval(RedisCmd *cmd, zval *zv) { + zend_string *zstr, *tmp; + + zstr = zval_get_tmp_string(zv, &tmp); + redis_cmd_cat_channel_zstr(cmd, zstr); + zend_tmp_string_release(tmp); +} /* Generic commands based on method signature and what kind of things we're * processing. Lots of Redis commands take something like key, value, or * key, value long. Each unique signature like this is written only once */ /* A command that takes no arguments */ -int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, ""); - return SUCCESS; + RedisCmd *cmd; + + if (zend_parse_parameters_none() == FAILURE) { + return NULL; + } + + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + + redis_cmd_randslot(cmd); + + return cmd; } /* Helper to construct a raw command. Given that the cluster and non cluster * versions are different (RedisCluster needs an additional argument to direct * the command) we take the start of our array and count */ -int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len) -{ - smart_string cmdstr = {0}; +RedisCmd *redis_build_raw_cmd(zval *z_args, int argc) { + RedisCmd *cmd; int i; /* Make sure our first argument is a string */ if (Z_TYPE(z_args[0]) != IS_STRING) { php_error_docref(NULL, E_WARNING, "When sending a 'raw' command, the first argument must be a string!"); - return FAILURE; + return NULL; } - /* Initialize our command string */ - redis_cmd_init_sstr(&cmdstr, argc-1, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + cmd = redis_cmd_create(NULL, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); for (i = 1; i < argc; i++) { switch (Z_TYPE(z_args[i])) { case IS_STRING: - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[i]), - Z_STRLEN(z_args[i])); + redis_cmd_cat_zstr(cmd, Z_STR(z_args[i])); break; case IS_LONG: - redis_cmd_append_sstr_long(&cmdstr,Z_LVAL(z_args[i])); + redis_cmd_cat_long(cmd, Z_LVAL(z_args[i])); break; case IS_DOUBLE: - redis_cmd_append_sstr_dbl(&cmdstr,Z_DVAL(z_args[i])); + redis_cmd_cat_double(cmd, Z_DVAL(z_args[i])); break; default: php_error_docref(NULL, E_WARNING, "Raw command arguments must be scalar values!"); - efree(cmdstr.c); - return FAILURE; + redis_cmd_free(cmd); + return NULL; } } - /* Push command and length to caller */ - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -smart_string * -redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) -{ +RedisCmd *redis_build_script_cmd(int argc, zval *z_args) { + RedisCmd *cmd; int i; - zend_string *zstr; if (Z_TYPE(z_args[0]) != IS_STRING) { return NULL; } + + cmd = redis_cmd_create_literal(NULL, "SCRIPT"); + // Branch based on the directive if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "kill")) { - // Simple SCRIPT_KILL command - REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); - redis_cmd_append_sstr(cmd, ZEND_STRL("KILL")); + redis_cmd_cat_literal(cmd, "KILL"); } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "flush")) { // Simple SCRIPT FLUSH [ASYNC | SYNC] if (argc > 1 && ( @@ -225,218 +297,193 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) !zend_string_equals_literal_ci(Z_STR(z_args[1]), "async") ) )) { - return NULL; + goto error; } - REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); - redis_cmd_append_sstr(cmd, ZEND_STRL("FLUSH")); + + redis_cmd_cat_literal(cmd, "FLUSH"); if (argc > 1) { - redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); + redis_cmd_cat_zval_zstr(cmd, &z_args[1]); } } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "load")) { // Make sure we have a second argument, and it's not empty. If it is // empty, we can just return an empty array (which is what Redis does) if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || Z_STRLEN(z_args[1]) < 1) { - return NULL; + goto error; } - // Format our SCRIPT LOAD command - REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); - redis_cmd_append_sstr(cmd, ZEND_STRL("LOAD")); - redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); + + redis_cmd_cat_literal(cmd, "LOAD"); + redis_cmd_cat_zval_zstr(cmd, &z_args[1]); } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "exists")) { // Make sure we have a second argument if (argc < 2) { - return NULL; + goto error; } - /* Construct our SCRIPT EXISTS command */ - REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); - redis_cmd_append_sstr(cmd, ZEND_STRL("EXISTS")); + + redis_cmd_cat_literal(cmd, "EXISTS"); for (i = 1; i < argc; ++i) { - zstr = zval_get_string(&z_args[i]); - redis_cmd_append_sstr(cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); + redis_cmd_cat_zval_zstr(cmd, &z_args[i]); } } else { - /* Unknown directive */ - return NULL; + goto error; } + return cmd; + +error: + redis_cmd_free(cmd); + return NULL; } /* Command that takes one optional string */ -int redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - char *arg = NULL; - size_t arglen; + zend_string *arg = NULL; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &arg, &arglen) == FAILURE) { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(arg) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (arg != NULL) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", arg, arglen); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, ""); - } + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + if (arg != NULL) + redis_cmd_cat_zstr(cmd, arg); - return SUCCESS; + return cmd; } /* Generic command where we just take a string and do nothing to it*/ -int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - char *arg; - size_t arg_len; + zend_string *arg; + RedisCmd *cmd; - // Parse args - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) - ==FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(arg) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - // Build the command without molesting the string - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "s", arg, arg_len); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_zstr(cmd, arg); - return SUCCESS; + return cmd; } /* Key, long, zval (serialized) */ -int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - char *key = NULL; - size_t key_len; + zend_string *key = NULL; zend_long expire; zval *z_val; ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_STRING(key, key_len) + Z_PARAM_STR(key) Z_PARAM_LONG(expire) Z_PARAM_ZVAL(z_val) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "klv", key, key_len, expire, z_val); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "Klv", key, expire, z_val); } /* Generic key, long, string (unserialized) */ -int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - char *key, *val; - size_t key_len, val_len; + zend_string *key, *val; zend_long lval; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &key, &key_len, - &lval, &val, &val_len) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(key) + Z_PARAM_LONG(lval) + Z_PARAM_STR(val) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kds", key, key_len, (int)lval, val, val_len); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - return SUCCESS; + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, lval); + redis_cmd_cat_zstr(cmd, val); + + return cmd; } /* Generic command construction when we just take a key and value */ -int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - char *key; - size_t key_len; - zval *z_val; + zend_string *key; + zval *zv; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &key_len, - &z_val) == FAILURE) - { - return FAILURE; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &key, &zv) == FAILURE) { + return NULL; } - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kv", key, key_len, z_val); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "Kv", key, zv); } /* Generic command that takes a key and an unserialized value */ -int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - char *key, *val; - size_t key_len, val_len; + zend_string *key, *val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, - &val, &val_len) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &key, &val) == FAILURE) { - return FAILURE; + return NULL; } - // Construct command - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ks", key, key_len, val, val_len); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "KS", key, val); } /* Key, string, string without serialization (ZCOUNT, ZREMRANGEBYSCORE) */ -int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - char *k, *v1, *v2; - size_t klen, v1len, v2len; + zend_string *key, *str1, *str2; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &k, &klen, - &v1, &v1len, &v2, &v2len) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(key) + Z_PARAM_STR(str1) + Z_PARAM_STR(str2) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", k, klen, v1, v1len, v2, v2len); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - // Success! - return SUCCESS; + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, str1); + redis_cmd_cat_zstr(cmd, str2); + + return cmd; } /* Generic command that takes two keys */ -int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { zend_string *key1 = NULL, *key2 = NULL; - smart_string cmdstr = {0}; - short slot2; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key1) Z_PARAM_STR(key2) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - redis_cmd_init_sstr(&cmdstr, 2, kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, key1, redis_sock, slot); - redis_cmd_append_sstr_key_zstr(&cmdstr, key2, redis_sock, slot ? &slot2 : NULL); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_key_zstr(cmd, key1); - if (slot && *slot != slot2) { - php_error_docref(0, E_WARNING, "Keys don't hash to the same slot"); - smart_string_free(&cmdstr); - return FAILURE; - } + redis_cmd_try_cat_key_zstr(cmd, key2); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* Generic command construction where we take a key and a long */ -int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { zend_string *key = NULL; zend_long lval = 0; @@ -444,82 +491,67 @@ int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_LONG(lval) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kl", ZSTR_VAL(key), ZSTR_LEN(key), lval); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "Kl", key, lval); } /* long, long */ -int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { zend_long l1 = 0, l2 = 0; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(l1) Z_PARAM_LONG(l2) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ll", l1, l2); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "ll", l1, l2); } /* key, long, long */ -int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - char *key; - size_t key_len; zend_long val1, val2; + zend_string *key; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &key, &key_len, - &val1, &val2) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sll", &key, &val1, &val2) + == FAILURE) { - return FAILURE; + return NULL; } - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kll", key, key_len, val1, val2); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "Kll", key, val1, val2); } /* Generic command where we take a single key */ -int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - char *key; - size_t key_len; + zend_string *key; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(key, key_len); - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + Z_PARAM_STR(key); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "K", key); } -int -redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - int argc; - smart_string cmdstr = {0}; zend_bool abort = 0, force = 0; zend_long timeout = 0, port = 0; zend_string *zkey, *host = NULL; zval *z_to = NULL, *z_ele; + RedisCmd *cmd; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!bl", &z_to, &abort, &timeout) == FAILURE) { - return FAILURE; + return NULL; } if (z_to != NULL) { @@ -538,121 +570,102 @@ redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (!host || !port) { php_error_docref(NULL, E_WARNING, "host and port must be provided!"); if (host) zend_string_release(host); - return FAILURE; + return NULL; } } - argc = (host && port ? 3 + force : 0) + abort + (timeout > 0 ? 2 : 0); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "FAILOVER"); + cmd = redis_cmd_create_literal(redis_sock, "FAILOVER"); if (host && port) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TO"); - redis_cmd_append_sstr_zstr(&cmdstr, host); - redis_cmd_append_sstr_int(&cmdstr, port); + redis_cmd_cat_literal(cmd, "TO"); + redis_cmd_cat_zstr(cmd, host); + redis_cmd_cat_long(cmd, port); if (force) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FORCE"); + redis_cmd_cat_literal(cmd, "FORCE"); } zend_string_release(host); } if (abort) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ABORT"); + redis_cmd_cat_literal(cmd, "ABORT"); } if (timeout > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TIMEOUT"); - redis_cmd_append_sstr_long(&cmdstr, timeout); + redis_cmd_cat_literal(cmd, "TIMEOUT"); + redis_cmd_cat_long(cmd, timeout); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - smart_string cmdstr = {0}; zend_bool sync = 0; zend_bool is_null = 1; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_BOOL_OR_NULL(sync, is_null) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - redis_cmd_init_sstr(&cmdstr, !is_null, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); if (!is_null) { ZEND_ASSERT(sync == 0 || sync == 1); if (sync == 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASYNC"); + redis_cmd_cat_literal(cmd, "ASYNC"); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SYNC"); + redis_cmd_cat_literal(cmd, "SYNC"); } } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* Generic command where we take a key and a double */ -int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - char *key; - size_t key_len; + zend_string *key; double val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &key, &key_len, - &val) == FAILURE) - { - return FAILURE; - } + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sd", &key, &val) == FAILURE) + return NULL; - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kf", key, key_len, val); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "Kf", key, val); } /* Generic to construct SCAN and variant commands */ -int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, - uint64_t it, char *pat, int pat_len, long count) +RedisCmd * +redis_fmt_scan_cmd(REDIS_SCAN_TYPE type, char *key, int key_len, + uint64_t it, char *pat, int pat_len, long count) { static char *kw[] = {"SCAN","SSCAN","HSCAN","ZSCAN"}; - int argc; - smart_string cmdstr = {0}; + RedisCmd *cmd; - // Figure out our argument count - argc = 1 + (type!=TYPE_SCAN) + (pat_len>0?2:0) + (count>0?2:0); - - redis_cmd_init_sstr(&cmdstr, argc, kw[type], strlen(kw[type])); + cmd = redis_cmd_create(NULL, kw[type], strlen(kw[type])); // Append our key if it's not a regular SCAN command if (type != TYPE_SCAN) { - redis_cmd_append_sstr(&cmdstr, key, key_len); + redis_cmd_cat_str(cmd, key, key_len); } // Append cursor - redis_cmd_append_sstr_u64(&cmdstr, it); + redis_cmd_cat_u64(cmd, it); // Append count if we've got one if (count) { - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT")); - redis_cmd_append_sstr_long(&cmdstr, count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); } // Append pattern if we've got one if (pat_len) { - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("MATCH")); - redis_cmd_append_sstr(&cmdstr,pat,pat_len); + redis_cmd_cat_literal(cmd, "MATCH"); + redis_cmd_cat_str(cmd, pat, pat_len); } - // Push command to the caller, return length - *cmd = cmdstr.c; - return cmdstr.len; + return cmd; } void redis_get_zcmd_options(redisZcmdOptions *dst, zval *src, int flags) { @@ -800,21 +813,23 @@ static int validate_zlex_arg(const char *str, size_t len) { (len == 1 && (*str == '+' || *str == '-')); } -static int validate_zlex_arg_zval(zval *z) { - return Z_TYPE_P(z) == IS_STRING && validate_zlex_arg(Z_STRVAL_P(z), Z_STRLEN_P(z)); +static zend_always_inline int validate_zlex_arg_zstr(zend_string *z) { + return validate_zlex_arg(ZSTR_VAL(z), ZSTR_LEN(z)); } -int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +static int validate_zlex_arg_zval(zval *z) { + return Z_TYPE_P(z) == IS_STRING && validate_zlex_arg_zstr(Z_STR_P(z)); +} + +RedisCmd *redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { zval *zoptions = NULL, *zstart = NULL, *zend = NULL; zend_string *dst = NULL, *src = NULL; zend_long start = 0, end = 0; - smart_string cmdstr = {0}; redisZcmdOptions opt; int min_argc, flags; - short slot2; + RedisCmd *cmd; flags = redis_get_zcmd_flags(kw); @@ -833,7 +848,7 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(zoptions) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); redis_get_zcmd_options(&opt, zoptions, flags); @@ -841,112 +856,101 @@ int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_ASSERT(!(flags & REDIS_ZCMD_INT_RANGE)); if (!validate_zlex_arg_zval(zstart) || !validate_zlex_arg_zval(zend)) { php_error_docref(NULL, E_WARNING, "Legographical args must start with '[' or '(' or be '+' or '-'"); - return FAILURE; + return NULL; } } - redis_cmd_init_sstr(&cmdstr, min_argc + !!opt.bylex + !!opt.byscore + - !!opt.rev + !!opt.withscores + - (opt.limit.enabled ? 3 : 0), kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - if (flags & REDIS_ZCMD_HAS_DST_KEY) - redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); - redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, &slot2); + if ((flags & REDIS_ZCMD_HAS_DST_KEY)) + redis_cmd_try_cat_key_zstr(cmd, dst); - /* Protect the user from crossslot errors */ - if ((flags & REDIS_ZCMD_HAS_DST_KEY) && slot && *slot != slot2) { - php_error_docref(NULL, E_WARNING, "destination and source keys must map to the same slot"); - efree(cmdstr.c); - return FAILURE; - } + redis_cmd_try_cat_key_zstr(cmd, src); if (flags & REDIS_ZCMD_INT_RANGE) { - redis_cmd_append_sstr_long(&cmdstr, start); - redis_cmd_append_sstr_long(&cmdstr, end); + redis_cmd_cat_long(cmd, start); + redis_cmd_cat_long(cmd, end); } else { - redis_cmd_append_sstr_zval(&cmdstr, zstart, NULL); - redis_cmd_append_sstr_zval(&cmdstr, zend, NULL); + redis_cmd_cat_zval_zstr(cmd, zstart); + redis_cmd_cat_zval_zstr(cmd, zend); } - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.byscore, "BYSCORE"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.bylex, "BYLEX"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.rev, "REV"); + redis_cmd_cat_literal_if(cmd, opt.byscore, "BYSCORE"); + redis_cmd_cat_literal_if(cmd, opt.bylex, "BYLEX"); + redis_cmd_cat_literal_if(cmd, opt.rev, "REV"); if (opt.limit.enabled) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT"); - redis_cmd_append_sstr_long(&cmdstr, opt.limit.offset); - redis_cmd_append_sstr_long(&cmdstr, opt.limit.count); + redis_cmd_cat_literal(cmd, "LIMIT"); + redis_cmd_cat_long(cmd, opt.limit.offset); + redis_cmd_cat_long(cmd, opt.limit.count); } - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withscores, "WITHSCORES"); + redis_cmd_cat_literal_if(cmd, opt.withscores, "WITHSCORES"); - if (slot) *slot = slot2; - *ctx = opt.withscores ? PHPREDIS_CTX_PTR : NULL; - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + redis_cmd_set_ctx(cmd, opt.withscores ? PHPREDIS_CTX_PTR : NULL); - return SUCCESS; + return cmd; } -static int redis_build_config_get_cmd(smart_string *dst, zval *val) { +static RedisCmd *redis_build_config_get_cmd(RedisSock *redis_sock, zval *val) { zend_string *zstr; - int ncfg; + RedisCmd *cmd; zval *zv; if (val == NULL || (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_ARRAY)) { php_error_docref(NULL, E_WARNING, "Must pass a string or array of values to CONFIG GET"); - return FAILURE; + return NULL; } else if (Z_TYPE_P(val) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(val)) == 0) { php_error_docref(NULL, E_WARNING, "Cannot pass an empty array to CONFIG GET"); - return FAILURE; + return NULL; } - ncfg = Z_TYPE_P(val) == IS_STRING ? 1 : zend_hash_num_elements(Z_ARRVAL_P(val)); - - REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + ncfg, "CONFIG"); - REDIS_CMD_APPEND_SSTR_STATIC(dst, "GET"); + cmd = redis_cmd_create_literal(redis_sock, "CONFIG"); + redis_cmd_cat_literal(cmd, "GET"); if (Z_TYPE_P(val) == IS_STRING) { - redis_cmd_append_sstr_zstr(dst, Z_STR_P(val)); + redis_cmd_cat_zstr(cmd, Z_STR_P(val)); } else { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), zv) { ZVAL_DEREF(zv); zstr = zval_get_string(zv); - redis_cmd_append_sstr_zstr(dst, zstr); + redis_cmd_cat_zstr(cmd, zstr); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); } - return SUCCESS; + return cmd; } -static int redis_build_config_set_cmd(smart_string *dst, zval *key, zend_string *val) { +static RedisCmd *redis_build_config_set_cmd(RedisSock *redis_sock, zval *key, + zend_string *val) { zend_string *zkey, *zstr; + RedisCmd *cmd; zval *zv; /* Legacy case: CONFIG SET */ if (key != NULL && val != NULL) { - REDIS_CMD_INIT_SSTR_STATIC(dst, 3, "CONFIG"); - REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET"); + cmd = redis_cmd_create_literal(redis_sock, "CONFIG"); + redis_cmd_cat_literal(cmd, "SET"); zstr = zval_get_string(key); - redis_cmd_append_sstr_zstr(dst, zstr); + redis_cmd_cat_zstr(cmd, zstr); zend_string_release(zstr); - redis_cmd_append_sstr_zstr(dst, val); + redis_cmd_cat_zstr(cmd, val); - return SUCCESS; + return cmd; } /* Now we must have an array with at least one element */ if (key == NULL || Z_TYPE_P(key) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(key)) == 0) { php_error_docref(NULL, E_WARNING, "Must either pass two strings to CONFIG SET or a non-empty array of values"); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(dst, 1 + (2 * zend_hash_num_elements(Z_ARRVAL_P(key))), "CONFIG"); - REDIS_CMD_APPEND_SSTR_STATIC(dst, "SET"); + cmd = redis_cmd_create_literal(redis_sock, "CONFIG"); + redis_cmd_cat_literal(cmd, "SET"); ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(key), zkey, zv) { if (zkey == NULL) @@ -954,29 +958,27 @@ static int redis_build_config_set_cmd(smart_string *dst, zval *key, zend_string ZVAL_DEREF(zv); - redis_cmd_append_sstr_zstr(dst, zkey); + redis_cmd_cat_zstr(cmd, zkey); zstr = zval_get_string(zv); - redis_cmd_append_sstr_zstr(dst, zstr); + redis_cmd_cat_zstr(cmd, zstr); zend_string_release(zstr); } ZEND_HASH_FOREACH_END(); - return SUCCESS; + return cmd; fail: php_error_docref(NULL, E_WARNING, "Must pass an associate array of config keys and values"); - efree(dst->c); - memset(dst, 0, sizeof(*dst)); - return FAILURE; + redis_cmd_free(cmd); + return NULL; } -int -redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *op = NULL, *arg = NULL; - smart_string cmdstr = {0}; - int res = FAILURE; + RedisCmd *cmd = NULL; + void *ctx = NULL; zval *key = NULL; ZEND_PARSE_PARAMETERS_START(1, 3) @@ -984,67 +986,67 @@ redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(key) Z_PARAM_STR_OR_NULL(arg) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_string_equals_literal_ci(op, "RESETSTAT") || zend_string_equals_literal_ci(op, "REWRITE")) { - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CONFIG"); - redis_cmd_append_sstr_zstr(&cmdstr, op); - *ctx = redis_boolean_response; - res = SUCCESS; + cmd = redis_cmd_create_literal(redis_sock, "CONFIG"); + redis_cmd_cat_zstr(cmd, op); + ctx = redis_boolean_response; } else if (zend_string_equals_literal_ci(op, "GET")) { - res = redis_build_config_get_cmd(&cmdstr, key); - *ctx = redis_mbulk_reply_zipped_raw; + cmd = redis_build_config_get_cmd(redis_sock, key); + ctx = redis_mbulk_reply_zipped_raw; } else if (zend_string_equals_literal_ci(op, "SET")) { - res = redis_build_config_set_cmd(&cmdstr, key, arg); - *ctx = redis_boolean_response; + cmd = redis_build_config_set_cmd(redis_sock, key, arg); + ctx = redis_boolean_response; } else { php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op)); - return FAILURE; + return NULL; } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return res; + if (cmd) + redis_cmd_set_ctx(cmd, ctx); + + return cmd; } -int -redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; zend_string *op = NULL, *arg; zval *argv = NULL; + RedisCmd *cmd; + void *ctx = NULL; int i, argc = 0; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_VARIADIC('*', argv, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); for (i = 0; i < argc; ++i) { if (Z_TYPE(argv[i]) != IS_STRING) { php_error_docref(NULL, E_WARNING, "invalid argument"); - return FAILURE; + return NULL; } } if (zend_string_equals_literal_ci(op, "DELETE")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "argument required"); - return FAILURE; + return NULL; } } else if (zend_string_equals_literal_ci(op, "DUMP")) { - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "FLUSH")) { if (argc > 0 && !zend_string_equals_literal_ci(Z_STR(argv[0]), "SYNC") && !zend_string_equals_literal_ci(Z_STR(argv[0]), "ASYNC") ) { php_error_docref(NULL, E_WARNING, "invalid argument"); - return FAILURE; + return NULL; } } else if (zend_string_equals_literal_ci(op, "KILL")) { // noop @@ -1053,22 +1055,22 @@ redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (zend_string_equals_literal_ci(Z_STR(argv[0]), "LIBRARYNAME")) { if (argc < 2) { php_error_docref(NULL, E_WARNING, "argument required"); - return FAILURE; + return NULL; } } else if (!zend_string_equals_literal_ci(Z_STR(argv[0]), "WITHCODE")) { php_error_docref(NULL, E_WARNING, "invalid argument"); - return FAILURE; + return NULL; } } - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "LOAD")) { if (argc < 1 || ( zend_string_equals_literal_ci(Z_STR(argv[0]), "REPLACE") && argc < 2 )) { php_error_docref(NULL, E_WARNING, "argument required"); - return FAILURE; + return NULL; } - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "RESTORE")) { if (argc < 1 || ( argc > 1 && @@ -1077,37 +1079,35 @@ redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, !zend_string_equals_literal_ci(Z_STR(argv[1]), "REPLACE") )) { php_error_docref(NULL, E_WARNING, "invalid argument"); - return FAILURE; + return NULL; } } else if (zend_string_equals_literal_ci(op, "STATS")) { - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else { php_error_docref(NULL, E_WARNING, "Unknown operation '%s'", ZSTR_VAL(op)); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + argc, "FUNCTION"); - redis_cmd_append_sstr_zstr(&cmdstr, op); + cmd = redis_cmd_create_literal(redis_sock, "FUNCTION"); + redis_cmd_cat_zstr(cmd, op); for (i = 0; i < argc; i++) { arg = zval_get_string(&argv[i]); - redis_cmd_append_sstr_zstr(&cmdstr, arg); + redis_cmd_cat_zstr(cmd, arg); zend_string_release(arg); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + redis_cmd_set_ctx(cmd, ctx); - return SUCCESS; + return cmd; } -int -redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw) { HashTable *keys = NULL, *args = NULL; - smart_string cmdstr = {0}; zend_string *fn = NULL; + RedisCmd *cmd; zval *zv; ZEND_PARSE_PARAMETERS_START(1, 3) @@ -1115,159 +1115,138 @@ redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT(keys) Z_PARAM_ARRAY_HT(args) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) + - (args ? zend_hash_num_elements(args) : 0), kw, strlen(kw)); - redis_cmd_append_sstr_zstr(&cmdstr, fn); - redis_cmd_append_sstr_long(&cmdstr, keys ? zend_hash_num_elements(keys) : 0); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_zstr(cmd, fn); + redis_cmd_cat_long(cmd, keys ? zend_hash_num_elements(keys) : 0); if (keys != NULL) { ZEND_HASH_FOREACH_VAL(keys, zv) { - redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); + redis_cmd_try_cat_key_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); } if (args != NULL) { ZEND_HASH_FOREACH_VAL(args, zv) { - redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); + redis_cmd_cat_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int -redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - char *key; - int count = 0; - size_t key_len; - smart_string cmdstr = {0}; zend_bool withscores = 0; - zval *z_opts = NULL, *z_ele; - zend_string *zkey; + zend_string *key, *zstr; + HashTable *opts = NULL; + RedisCmd *cmd; + int count = 0; + zval *z_ele; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", - &key, &key_len, &z_opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT(opts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (z_opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { - if (zkey != NULL) { + if (opts != NULL) { + ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zstr, z_ele) { + if (zstr != NULL) { ZVAL_DEREF(z_ele); - if (zend_string_equals_literal_ci(zkey, "count")) { + if (zend_string_equals_literal_ci(zstr, "count")) { count = zval_get_long(z_ele); - } else if (zend_string_equals_literal_ci(zkey, "withscores")) { + } else if (zend_string_equals_literal_ci(zstr, "withscores")) { withscores = zend_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withscores, "ZRANDMEMBER"); - redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "ZRANDMEMBER"); + redis_cmd_cat_key_zstr(cmd, key); if (count != 0) { - redis_cmd_append_sstr_long(&cmdstr, count); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_long(cmd, count); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } if (withscores) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); - *ctx = PHPREDIS_CTX_PTR + 1; + redis_cmd_cat_literal(cmd, "WITHSCORES"); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR + 1); } - - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } -int -redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zval *z_keys, *z_opts = NULL, *z_key; redisZcmdOptions opts = {0}; - smart_string cmdstr = {0}; + RedisCmd *cmd; int numkeys, flags; - short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &z_keys, &z_opts) == FAILURE) { - return FAILURE; + return NULL; } if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) { - return FAILURE; + return NULL; } flags = redis_get_zcmd_flags("ZDIFF"); redis_get_zcmd_options(&opts, z_opts, flags); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + numkeys + opts.withscores, "ZDIFF"); - redis_cmd_append_sstr_long(&cmdstr, numkeys); + cmd = redis_cmd_create_literal(redis_sock, "ZDIFF"); + redis_cmd_cat_long(cmd, numkeys); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_key) { ZVAL_DEREF(z_key); - redis_cmd_append_sstr_key_zval(&cmdstr, z_key, redis_sock, slot); - - if (slot && s2 && s2 != *slot) { - php_error_docref(NULL, E_WARNING, "Not all keys map to the same slot!"); - efree(cmdstr.c); - return FAILURE; - } - - if (slot) s2 = *slot; + redis_cmd_try_cat_key_zval(cmd, z_key); } ZEND_HASH_FOREACH_END(); if (opts.withscores) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_literal(cmd, "WITHSCORES"); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } -static int redis_cmd_append_sstr_score(smart_string *dst, zval *score) { +static int redis_cmd_cat_score(RedisCmd *cmd, zval *score) { zend_uchar type; zend_long lval; - size_t cmdlen; + uint32_t argc; double dval; /* Get current command length */ - cmdlen = dst->len; + argc = cmd->argc; if (Z_TYPE_P(score) == IS_LONG) { - redis_cmd_append_sstr_zend_long(dst, Z_LVAL_P(score)); + redis_cmd_cat_long(cmd, Z_LVAL_P(score)); } else if (Z_TYPE_P(score) == IS_DOUBLE) { - redis_cmd_append_sstr_dbl(dst, Z_DVAL_P(score)); + redis_cmd_cat_double(cmd, Z_DVAL_P(score)); } else if (Z_TYPE_P(score) == IS_STRING) { type = is_numeric_string(Z_STRVAL_P(score), Z_STRLEN_P(score), &lval, &dval, 0); if (type == IS_LONG) { - redis_cmd_append_sstr_long(dst, lval); + redis_cmd_cat_long(cmd, lval); } else if (type == IS_DOUBLE) { - redis_cmd_append_sstr_dbl(dst, dval); + redis_cmd_cat_double(cmd, dval); } else if (zend_string_equals_literal_ci(Z_STR_P(score), "-inf") || zend_string_equals_literal_ci(Z_STR_P(score), "+inf") || zend_string_equals_literal_ci(Z_STR_P(score), "inf")) { - redis_cmd_append_sstr_zstr(dst, Z_STR_P(score)); + redis_cmd_cat_zstr(cmd, Z_STR_P(score)); } } /* Success if we appended something */ - if (dst->len > cmdlen) + if (cmd->argc > argc) return SUCCESS; /* Nothing appended, failure */ @@ -1275,221 +1254,174 @@ static int redis_cmd_append_sstr_score(smart_string *dst, zval *score) { return FAILURE; } -int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - smart_string cmdstr = {0}; zend_long limit = -1; HashTable *keys; - zend_string *key; + RedisCmd *cmd; zval *zv; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ARRAY_HT(keys) Z_PARAM_OPTIONAL Z_PARAM_LONG(limit) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(keys) == 0) { php_error_docref(NULL, E_WARNING, "Must pass at least one key"); - return FAILURE; + return NULL; } else if (ZEND_NUM_ARGS() == 2 && limit < 0) { php_error_docref(NULL, E_WARNING, "LIMIT cannot be negative"); - return FAILURE; + return NULL; } - redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(keys) + (limit > 0 ? 2 : 0), kw, strlen(kw)); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(keys)); - - if (slot) *slot = -1; + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_long(cmd, zend_hash_num_elements(keys)); ZEND_HASH_FOREACH_VAL(keys, zv) { - key = redis_key_prefix_zval(redis_sock, zv); - - if (slot) { - if (*slot == -1) { - *slot = cluster_hash_key_zstr(key); - } else if (*slot != cluster_hash_key_zstr(key)) { - php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot"); - efree(cmdstr.c); - zend_string_release(key); - return FAILURE; - } - } - - redis_cmd_append_sstr_zstr(&cmdstr, key); - zend_string_release(key); + redis_cmd_try_cat_key_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); if (limit > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT"); - redis_cmd_append_sstr_long(&cmdstr, limit); + redis_cmd_cat_literal(cmd, "LIMIT"); + redis_cmd_cat_long(cmd, limit); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } -int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - smart_string cmdstr = {0}; zend_string *host = NULL; zend_long port = 6379; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL Z_PARAM_STR(host) Z_PARAM_LONG(port) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (port < 0 || port > UINT16_MAX) { php_error_docref(NULL, E_WARNING, "Invalid port %ld", (long)port); - return FAILURE; + return NULL; } - redis_cmd_init_sstr(&cmdstr, 2, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); if (ZEND_NUM_ARGS() == 2) { - redis_cmd_append_sstr_zstr(&cmdstr, host); - redis_cmd_append_sstr_long(&cmdstr, port); + redis_cmd_cat_zstr(cmd, host); + redis_cmd_cat_long(cmd, port); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ONE"); + redis_cmd_cat_literal(cmd, "NO"); + redis_cmd_cat_literal(cmd, "ONE"); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int +RedisCmd * redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) + char *kw) { zval *z_keys, *z_weights = NULL, *z_opts = NULL, *z_ele; redisZcmdOptions opts = {0}; - smart_string cmdstr = {0}; + RedisCmd *cmd; int numkeys, flags; - short s2 = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a!a", &z_keys, &z_weights, &z_opts) == FAILURE) { - return FAILURE; + return NULL; } if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) { - return FAILURE; + return NULL; } if (z_weights && zend_hash_num_elements(Z_ARRVAL_P(z_weights)) != numkeys) { php_error_docref(NULL, E_WARNING, "WEIGHTS and keys array should be the same size!"); - return FAILURE; + return NULL; } flags = redis_get_zcmd_flags(kw); redis_get_zcmd_options(&opts, z_opts, flags); - redis_cmd_init_sstr(&cmdstr, 1 + numkeys + (z_weights ? 1 + numkeys : 0) + (opts.aggregate ? 2 : 0) + opts.withscores, kw, strlen(kw)); - redis_cmd_append_sstr_long(&cmdstr, numkeys); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_long(cmd, numkeys); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) { ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_key_zval(&cmdstr, z_ele, redis_sock, slot); - if (slot) { - if (s2 && s2 != *slot) { - php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot"); - efree(cmdstr.c); - return FAILURE; - } - s2 = *slot; - } + redis_cmd_try_cat_key_zval(cmd, z_ele); } ZEND_HASH_FOREACH_END(); if (z_weights) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS"); + redis_cmd_cat_literal(cmd, "WEIGHTS"); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_weights), z_ele) { ZVAL_DEREF(z_ele); - if (redis_cmd_append_sstr_score(&cmdstr, z_ele) == FAILURE) { - efree(cmdstr.c); - return FAILURE; + if (redis_cmd_cat_score(cmd, z_ele) == FAILURE) { + redis_cmd_free(cmd); + return NULL; } } ZEND_HASH_FOREACH_END(); } if (opts.aggregate) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AGGREGATE"); - redis_cmd_append_sstr_zstr(&cmdstr, opts.aggregate); + redis_cmd_cat_literal(cmd, "AGGREGATE"); + redis_cmd_cat_zstr(cmd, opts.aggregate); } if (opts.withscores) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_literal(cmd, "WITHSCORES"); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } -int -redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; zend_string *dst = NULL; HashTable *keys = NULL; zend_ulong nkeys; - short s2 = 0; + RedisCmd *cmd; zval *zkey; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(dst) Z_PARAM_ARRAY_HT(keys) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); nkeys = zend_hash_num_elements(keys); if (nkeys == 0) - return FAILURE; + return NULL; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + nkeys, "ZDIFFSTORE"); - redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); - redis_cmd_append_sstr_long(&cmdstr, nkeys); + cmd = redis_cmd_create_literal(redis_sock, "ZDIFFSTORE"); + redis_cmd_cat_key_zstr(cmd, dst); + redis_cmd_cat_long(cmd, nkeys); ZEND_HASH_FOREACH_VAL(keys, zkey) { ZVAL_DEREF(zkey); - redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot ? &s2 : NULL); - if (slot && *slot != s2) { - php_error_docref(NULL, E_WARNING, "All keys must hash to the same slot"); - efree(cmdstr.c); - return FAILURE; - } + redis_cmd_try_cat_key_zval(cmd, zkey); } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* ZUNIONSTORE, ZINTERSTORE */ -int +RedisCmd * redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) + char *kw) { HashTable *keys = NULL, *weights = NULL; - smart_string cmdstr = {0}; zend_string *dst = NULL; zend_string *agg = NULL; zend_ulong nkeys; zval *zv = NULL; - short s2 = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 4) Z_PARAM_STR(dst) @@ -1497,15 +1429,15 @@ redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(weights) Z_PARAM_STR_OR_NULL(agg) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); nkeys = zend_hash_num_elements(keys); if (nkeys == 0) - return FAILURE; + return NULL; if (weights != NULL && zend_hash_num_elements(weights) != nkeys) { php_error_docref(NULL, E_WARNING, "WEIGHTS and keys array must be the same size!"); - return FAILURE; + return NULL; } // AGGREGATE option @@ -1514,124 +1446,114 @@ redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, !zend_string_equals_literal_ci(agg, "MAX"))) { php_error_docref(NULL, E_WARNING, "AGGREGATE option must be 'SUM', 'MIN', or 'MAX'"); - return FAILURE; + return NULL; } - redis_cmd_init_sstr(&cmdstr, 2 + nkeys + (weights ? 1 + nkeys : 0) + (agg ? 2 : 0), kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); - redis_cmd_append_sstr_int(&cmdstr, nkeys); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_key_zstr(cmd, dst); + redis_cmd_cat_long(cmd, nkeys); ZEND_HASH_FOREACH_VAL(keys, zv) { ZVAL_DEREF(zv); - redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot ? &s2 : NULL); - if (slot && s2 != *slot) { - php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot!"); - efree(cmdstr.c); - return FAILURE; - } + redis_cmd_try_cat_key_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); if (weights) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS"); + redis_cmd_cat_literal(cmd, "WEIGHTS"); ZEND_HASH_FOREACH_VAL(weights, zv) { ZVAL_DEREF(zv); - if (redis_cmd_append_sstr_score(&cmdstr, zv) == FAILURE) { - efree(cmdstr.c); - return FAILURE; + if (redis_cmd_cat_score(cmd, zv) == FAILURE) { + redis_cmd_free(cmd); + return NULL; } } ZEND_HASH_FOREACH_END(); } if (agg) { - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("AGGREGATE")); - redis_cmd_append_sstr_zstr(&cmdstr, agg); + redis_cmd_cat_literal(cmd, "AGGREGATE"); + redis_cmd_cat_zstr(cmd, agg); } - // Push out values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { HashTable *channels = NULL; - smart_string cmdstr = {0}; zend_string *op, *pattern = NULL; zval *arg = NULL, *z_chan; + RedisCmd *cmd; + void *ctx = NULL; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(arg) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_string_equals_literal_ci(op, "NUMPAT")) { - *ctx = NULL; + ctx = NULL; } else if (zend_string_equals_literal_ci(op, "CHANNELS") || zend_string_equals_literal_ci(op, "SHARDCHANNELS") ) { if (arg != NULL) { if (Z_TYPE_P(arg) != IS_STRING) { php_error_docref(NULL, E_WARNING, "Invalid pattern value"); - return FAILURE; + return NULL; } pattern = zval_get_string(arg); } - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "NUMSUB") || zend_string_equals_literal_ci(op, "SHARDNUMSUB") ) { if (arg != NULL) { if (Z_TYPE_P(arg) != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "Invalid channels value"); - return FAILURE; + return NULL; } channels = Z_ARRVAL_P(arg); } - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else { php_error_docref(NULL, E_WARNING, "Unknown PUBSUB operation '%s'", ZSTR_VAL(op)); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + !!pattern + (channels ? zend_hash_num_elements(channels) : 0), "PUBSUB"); - redis_cmd_append_sstr_zstr(&cmdstr, op); + cmd = redis_cmd_create_literal(redis_sock, "PUBSUB"); + redis_cmd_cat_zstr(cmd, op); if (pattern != NULL) { - redis_cmd_append_sstr_zstr(&cmdstr, pattern); + redis_cmd_cat_zstr(cmd, pattern); zend_string_release(pattern); } else if (channels != NULL) { ZEND_HASH_FOREACH_VAL(channels, z_chan) { - redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot); + /* Prefix but don't cross slot protect */ + redis_cmd_cat_channel_zval(cmd, z_chan); } ZEND_HASH_FOREACH_END(); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + redis_cmd_set_ctx(cmd, ctx); - return SUCCESS; + return cmd; } /* SUBSCRIBE/PSUBSCRIBE/SSUBSCRIBE */ -int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd * +redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { zval *z_arr, *z_chan; HashTable *ht_chan; - smart_string cmdstr = {0}; subscribeContext *sctx = ecalloc(1, sizeof(*sctx)); unsigned short shardslot = REDIS_CLUSTER_SLOTS; - short s2; + RedisCmd *cmd; if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &z_arr, &sctx->cb.fci, &sctx->cb.fci_cache) == FAILURE) { efree(sctx); - return FAILURE; + return NULL; } ht_chan = Z_ARRVAL_P(z_arr); @@ -1640,7 +1562,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (sctx->argc == 0) { efree(sctx); - return FAILURE; + return NULL; } if (strcasecmp(kw, "ssubscribe") == 0) { @@ -1648,320 +1570,271 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if ((z_chan = zend_hash_get_current_data(ht_chan)) == NULL) { php_error_docref(NULL, E_WARNING, "Internal Zend HashTable error"); efree(sctx); - return FAILURE; + return NULL; } shardslot = cluster_hash_key_zval(z_chan); } // Start command construction - redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); // Iterate over channels ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { - redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot ? &s2 : NULL); - - if (slot && (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot)) { + if (!redis_cmd_cat_key_zval(cmd, z_chan) || + (shardslot != REDIS_CLUSTER_SLOTS && cmd->slot != shardslot)) + { php_error_docref(NULL, E_WARNING, "All shard channels needs to belong to a single slot"); - smart_string_free(&cmdstr); + redis_cmd_free(cmd); efree(sctx); - return FAILURE; + return NULL; } } ZEND_HASH_FOREACH_END(); - // Push values out - *cmd_len = cmdstr.len; - *cmd = cmdstr.c; - *ctx = sctx; + redis_cmd_set_ctx_ex(cmd, sctx, redis_cmd_ctx_efree); if (shardslot != REDIS_CLUSTER_SLOTS) { - if (slot) *slot = shardslot; + cmd->slot = shardslot; } else { // Pick a slot at random - CMD_RAND_SLOT(slot); + redis_cmd_randslot(cmd); } - return SUCCESS; + return cmd; } /* UNSUBSCRIBE/PUNSUBSCRIBE/SUNSUBSCRIBE */ -int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - smart_string cmdstr = {0}; subscribeContext *sctx; HashTable *channels; zval *channel; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(channels) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(channels) == 0) - return FAILURE; + return NULL; sctx = ecalloc(1, sizeof(*sctx)); sctx->kw = kw; sctx->argc = zend_hash_num_elements(channels); - redis_cmd_init_sstr(&cmdstr, sctx->argc, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_set_ctx_ex(cmd, sctx, redis_cmd_ctx_efree); ZEND_HASH_FOREACH_VAL(channels, channel) { - redis_cmd_append_sstr_key_zval(&cmdstr, channel, redis_sock, slot); + /* Prefix but don't cross slot protect */ + redis_cmd_cat_channel_zval(cmd, channel); } ZEND_HASH_FOREACH_END(); - *cmd_len = cmdstr.len; - *cmd = cmdstr.c; - *ctx = sctx; - - return SUCCESS; + return cmd; } /* ZRANGEBYLEX/ZREVRANGEBYLEX */ -int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - char *key, *min, *max; - size_t key_len, min_len, max_len; + zend_string *key, *min, *max; int argc = ZEND_NUM_ARGS(); zend_long offset, count; + RedisCmd *cmd; /* We need either 3 or 5 arguments for this to be valid */ if (argc != 3 && argc != 5) { php_error_docref(0, E_WARNING, "Must pass either 3 or 5 arguments"); - return FAILURE; + return NULL; } - if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, - &max, &max_len, &offset, &count) == FAILURE) + if (zend_parse_parameters(argc, "SSS|ll", &key, &min, &max, &offset, + &count) == FAILURE) { - return FAILURE; + return NULL; } /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) { + if (!validate_zlex_arg_zstr(min) || !validate_zlex_arg_zstr(max)) { php_error_docref(NULL, E_WARNING, "Min/Max args can be '-' or '+', or start with '[' or '('"); - return FAILURE; + return NULL; } - /* Construct command */ - if (argc == 3) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, - max, max_len); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssll", key, key_len, min, min_len, - max, max_len, "LIMIT", 5, offset, count); + cmd = redis_cmd_fmt(redis_sock, kw, "KSS", key, min, max); + + if (argc == 5) { + redis_cmd_cat_literal(cmd, "LIMIT"); + redis_cmd_cat_long(cmd, offset); + redis_cmd_cat_long(cmd, count); } - return SUCCESS; + return cmd; } /* ZLEXCOUNT/ZREMRANGEBYLEX */ -int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - char *key, *min, *max; - size_t key_len, min_len, max_len; + zend_string *key, *min, *max; /* Parse args */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, - &min, &min_len, &max, &max_len) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS", &key, &min, &max) + == FAILURE) { - return FAILURE; + return NULL; } /* Quick sanity check on min/max */ - if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) { + if (!validate_zlex_arg_zstr(min) || !validate_zlex_arg_zstr(max)) { php_error_docref(NULL, E_WARNING, "Min/Max args can be '-' or '+', or start with '[' or '('"); - return FAILURE; + return NULL; } /* Construct command */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, min, min_len, - max, max_len); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, kw, "KSS", key, min, max); } /* EVAL and EVALSHA */ -int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - int argc = 0; zval *zv; HashTable *ht = NULL; zend_long num_keys = 0; - smart_string cmdstr = {0}; - zend_string *arg, *lua, *tmp; - short prevslot = -1; + zend_string *lua; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_STR(lua) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(ht) Z_PARAM_LONG(num_keys) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (ht == NULL) ht = (HashTable*)&zend_empty_array; - argc = zend_hash_num_elements(ht); - /* EVAL[SHA] {script || sha1} {num keys} */ - redis_cmd_init_sstr(&cmdstr, 2 + argc, kw, strlen(kw)); - redis_cmd_append_sstr_zstr(&cmdstr, lua); - redis_cmd_append_sstr_long(&cmdstr, num_keys); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_zstr(cmd, lua); + redis_cmd_cat_long(cmd, num_keys); - /* Pick a random slot up front. Any provided key(s) will override this */ - CMD_RAND_SLOT(slot); + /* Pick a random slot if there are no keys */ + if (num_keys == 0) + redis_cmd_randslot(cmd); - // Iterate over our args if we have any ZEND_HASH_FOREACH_VAL(ht, zv) { - arg = zval_get_tmp_string(zv, &tmp); - - /* If we're still on a key, prefix it check slot */ if (num_keys-- > 0) { - redis_cmd_append_sstr_key_zstr(&cmdstr, arg, redis_sock, slot); - - /* If we have been passed a slot, all keys must match */ - if (slot) { - if (prevslot != -1 && prevslot != *slot) { - zend_tmp_string_release(tmp); - php_error_docref(0, E_WARNING, - "All keys do not map to the same slot"); - return FAILURE; - } - prevslot = *slot; - } + redis_cmd_try_cat_key_zval(cmd, zv); } else { - redis_cmd_append_sstr_zstr(&cmdstr, arg); + redis_cmd_cat_zval_zstr(cmd, zv); } - - zend_tmp_string_release(tmp); } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* Commands that take a key followed by a variable list of serializable * values (RPUSH, LPUSH, SADD, SREM, etc...) */ -int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { zval *args = NULL; zend_string *key = NULL; - smart_string cmdstr = {0}; + RedisCmd *cmd; size_t i; int argc = 0; ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_STR(key) Z_PARAM_VARIADIC('*', args, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* Initialize our command */ - redis_cmd_init_sstr(&cmdstr, argc + 1, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); /* Append key */ - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, key); /* Add members */ for (i = 0; i < argc; i++) { - redis_cmd_append_sstr_zval(&cmdstr, &args[i], redis_sock); + redis_cmd_cat_zval(cmd, &args[i]); } - // Push out values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - // Success! - return SUCCESS; + return cmd; } /* Commands that take a key and then an array of values */ -static int gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, zend_bool pack_values, char **cmd, int *cmd_len, - short *slot, void **ctx) +static RedisCmd *gen_key_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw, + zend_bool pack_values) { - smart_string cmdstr = {0}; HashTable *values = NULL; zend_string *key = NULL; + RedisCmd *cmd; zval *zv; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(values) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(values) == 0) - return FAILURE; + return NULL; - redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(values), kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_key_zstr(cmd, key); ZEND_HASH_FOREACH_VAL(values, zv) { - redis_cmd_append_sstr_zval(&cmdstr, zv, pack_values ? redis_sock : NULL); + if (pack_values) { + redis_cmd_cat_zval(cmd, zv); + } else { + redis_cmd_cat_zval_zstr(cmd, zv); + } } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - 1, cmd, cmd_len, slot, ctx); + return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, 1); } -int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, char *kw) { - return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - 0, cmd, cmd_len, slot, ctx); + return gen_key_arr_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, 0); } /* Generic function that takes one or more non-serialized arguments */ -static int +RedisCmd * gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - uint32_t min_argc, char *kw, char **cmd, int *cmd_len, - short *slot, void **ctx) + uint32_t min_argc, char *kw) { - smart_string cmdstr = {0}; zval *argv = NULL; + RedisCmd *cmd; int argc = 0; uint32_t i; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); for (i = 0; i < argc; i++) { - redis_cmd_append_sstr_zval(&cmdstr, &argv[i], NULL); + redis_cmd_cat_zval_zstr(cmd, &argv[i]); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -static void -redis_cmd_append_sstr_mset_kvals(smart_string *cmdstr, RedisSock *redis_sock, - HashTable *kvals, short *slot) +static zend_bool +redis_cmd_cat_mset_kvals(RedisCmd *cmd, HashTable *kvals) { zend_string *key; zend_ulong idx; @@ -1970,57 +1843,57 @@ redis_cmd_append_sstr_mset_kvals(smart_string *cmdstr, RedisSock *redis_sock, ZEND_HASH_FOREACH_KEY_VAL(kvals, idx, key, zv) { ZVAL_DEREF(zv); if (key) { - redis_cmd_append_sstr_key_zstr(cmdstr, key, redis_sock, slot); + if (!redis_cmd_cat_key_zstr(cmd, key)) + return 0; } else { - redis_cmd_append_sstr_key_long(cmdstr, idx, redis_sock, slot); + if (!redis_cmd_cat_key_long(cmd, idx)) + return 0; } - redis_cmd_append_sstr_zval(cmdstr, zv, redis_sock); + redis_cmd_cat_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); + + return 1; } -int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - smart_string cmdstr = {0}; HashTable *kvals = NULL; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(kvals) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(kvals) == 0) - return FAILURE; + return NULL; - redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(kvals) * 2, kw, - strlen(kw)); - redis_cmd_append_sstr_mset_kvals(&cmdstr, redis_sock, kvals, slot); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + redis_cmd_try_cat_key(cmd, redis_cmd_cat_mset_kvals, kvals); - return SUCCESS; + return cmd; } /* Generic function that takes a variable number of keys, with an optional * timeout value. This can handle various SUNION/SUNIONSTORE/BRPOP type * commands. */ -static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, int kw_len, zend_bool has_timeout, - char **cmd, int *cmd_len, short *slot) +static RedisCmd * +gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + int kw_len, zend_bool has_timeout) + { zval *argv = NULL, ztimeout = {0}, *zv; - smart_string cmdstr = {0}; uint32_t min_argc; - short kslot = -1; int single_array; + RedisCmd *cmd; int argc = 0; min_argc = has_timeout ? 2 : 1; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); single_array = argc == min_argc && Z_TYPE(argv[0]) == IS_ARRAY; @@ -2032,7 +1905,7 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (Z_TYPE(ztimeout) != IS_LONG && Z_TYPE(ztimeout) != IS_DOUBLE) { php_error_docref(NULL, E_WARNING, "Timeout must be a long or double"); - return FAILURE; + return NULL; } } @@ -2041,63 +1914,41 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Need at least one argument */ argc = zend_hash_num_elements(Z_ARRVAL(argv[0])); if (argc == 0) - return FAILURE; + return NULL; if (has_timeout) argc++; } // Begin construction of our command - redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); + cmd = redis_cmd_create(redis_sock, kw, kw_len); if (single_array) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL(argv[0]), zv) { - redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); - if (slot) { - if (kslot != -1 && *slot != kslot) - goto cross_slot; - kslot = *slot; - } + redis_cmd_try_cat_key_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); } else { - uint32_t i; - for(i = 0; i < argc - !!has_timeout; i++) { - redis_cmd_append_sstr_key_zval(&cmdstr, &argv[i], redis_sock, slot); - if (slot) { - if (kslot != -1 && *slot != kslot) - goto cross_slot; - kslot = *slot; - } + for (uint32_t i = 0; i < argc - !!has_timeout; i++) { + redis_cmd_try_cat_key_zval(cmd, &argv[i]); } } if (Z_TYPE(ztimeout) == IS_DOUBLE) { - redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL(ztimeout)); + redis_cmd_cat_double(cmd, Z_DVAL(ztimeout)); } else if (Z_TYPE(ztimeout) == IS_LONG) { - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(ztimeout)); + redis_cmd_cat_long(cmd, Z_LVAL(ztimeout)); } - // Push out parameters - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; - -cross_slot: - efree(cmdstr.c); - php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); - return FAILURE; + return cmd; } -int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - int argc, blocking, is_zmpop; - smart_string cmdstr = {0}; +RedisCmd * +redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw) { zend_string *from = NULL; HashTable *keys = NULL; + int blocking, is_zmpop; double timeout = 0.0; zend_long count = 1; - short slot2 = -1; + RedisCmd *cmd; zval *zv; /* Sanity check on our keyword */ @@ -2114,95 +1965,71 @@ int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw Z_PARAM_STR(from); Z_PARAM_OPTIONAL Z_PARAM_LONG(count); - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(keys) == 0) { php_error_docref(NULL, E_WARNING, "Must pass at least one key"); - return FAILURE; + return NULL; } else if (count < 1) { php_error_docref(NULL, E_WARNING, "Count must be > 0"); - return FAILURE; + return NULL; } else if (!is_zmpop && !(zend_string_equals_literal_ci(from, "LEFT") || zend_string_equals_literal_ci(from, "RIGHT"))) { php_error_docref(NULL, E_WARNING, "from must be either 'LEFT' or 'RIGHT'"); - return FAILURE; + return NULL; } else if (is_zmpop && !(zend_string_equals_literal_ci(from, "MIN") || zend_string_equals_literal_ci(from, "MAX"))) { php_error_docref(NULL, E_WARNING, "from must be either 'MIN' or 'MAX'"); - return FAILURE; + return NULL; } - argc = 2 + !!blocking + zend_hash_num_elements(keys) + (count != 1 ? 2 : 0); - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - if (blocking) redis_cmd_append_sstr_dbl(&cmdstr, timeout); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(keys)); - - if (slot) *slot = -1; + if (blocking) + redis_cmd_cat_double(cmd, timeout); + redis_cmd_cat_long(cmd, zend_hash_num_elements(keys)); ZEND_HASH_FOREACH_VAL(keys, zv) { - redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); - if (slot) { - if (slot2 != -1 && *slot != slot2) { - php_error_docref(NULL, E_WARNING, "All keys don't hash to the same slot"); - efree(cmdstr.c); - return FAILURE; - } - slot2 = *slot; - } + redis_cmd_try_cat_key_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); - redis_cmd_append_sstr_zstr(&cmdstr, from); + redis_cmd_cat_zstr(cmd, from); if (count != 1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); } - *ctx = is_zmpop ? PHPREDIS_CTX_PTR : NULL; - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + redis_cmd_set_ctx(cmd, is_zmpop ? PHPREDIS_CTX_PTR : NULL); - return SUCCESS; + return cmd; } -int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { return gen_vararg_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 0, - "INFO", cmd, cmd_len, slot, ctx); + "INFO"); } -int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - int argc = 0; - smart_string cmdstr = {0}; +RedisCmd *redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zval *argv = NULL; + int argc = 0; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_VARIADIC('*', argv, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (redis_build_script_cmd(&cmdstr, argc, argv) == NULL) { - return FAILURE; - } - - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return redis_build_script_cmd(argc, argv); } /* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */ -int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd * +redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, - strlen(kw), 1, cmd, cmd_len, slot); + strlen(kw), 1); } /* @@ -2210,139 +2037,128 @@ int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * have specific processing (argument validation, etc) that make them unique */ -int -redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw) { - char *key; - size_t key_len; - smart_string cmdstr = {0}; + zend_string *key; zend_long count = 0; + RedisCmd *cmd; // Make sure the function is being called correctly - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", - &key, &key_len, &count) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", + &key, &count) == FAILURE) { - return FAILURE; + return NULL; } - redis_cmd_init_sstr(&cmdstr, 1 + (count > 0), kw, strlen(kw)); - redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_key_zstr(cmd, key); if (count > 0) { - redis_cmd_append_sstr_long(&cmdstr, (long)count); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_long(cmd, count); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int -redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd * +redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *op, *zstr; zval *z_args = NULL; + RedisCmd *cmd; + void *ctx = NULL; int argc = 0, i; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_VARIADIC('*', z_args, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_string_equals_literal_ci(op, "CAT") || zend_string_equals_literal_ci(op, "LIST") || zend_string_equals_literal_ci(op, "USERS") ) { - *ctx = NULL; + ctx = NULL; } else if (zend_string_equals_literal_ci(op, "LOAD") || zend_string_equals_literal_ci(op, "SAVE") ) { - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "GENPASS") || zend_string_equals_literal_ci(op, "WHOAMI") ) { - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "SETUSER")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "ACL SETUSER requires at least one argument"); - return FAILURE; + return NULL; } - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "DELUSER")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "ACL DELUSER requires at least one argument"); - return FAILURE; + return NULL; } - *ctx = PHPREDIS_CTX_PTR + 2; + ctx = PHPREDIS_CTX_PTR + 2; } else if (zend_string_equals_literal_ci(op, "GETUSER")) { if (argc < 1) { php_error_docref(NULL, E_WARNING, "ACL GETUSER requires at least one argument"); - return FAILURE; + return NULL; } - *ctx = PHPREDIS_CTX_PTR + 3; + ctx = PHPREDIS_CTX_PTR + 3; } else if (zend_string_equals_literal_ci(op, "DRYRUN")) { if (argc < 2) { php_error_docref(NULL, E_WARNING, "ACL DRYRUN requires at least two arguments"); - return FAILURE; + return NULL; } - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "LOG")) { if (argc > 0 && Z_TYPE(z_args[0]) == IS_STRING && ZVAL_STRICMP_STATIC(&z_args[0], "RESET")) { - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else { - *ctx = PHPREDIS_CTX_PTR + 4; + ctx = PHPREDIS_CTX_PTR + 4; } } else { php_error_docref(NULL, E_WARNING, "Unknown ACL operation '%s'", ZSTR_VAL(op)); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + argc, "ACL"); - redis_cmd_append_sstr_zstr(&cmdstr, op); + cmd = redis_cmd_create_literal(redis_sock, "ACL"); + redis_cmd_cat_zstr(cmd, op); for (i = 0; i < argc; ++i) { zstr = zval_get_string(&z_args[i]); - redis_cmd_append_sstr_zstr(&cmdstr, zstr); + redis_cmd_cat_zstr(cmd, zstr); zend_string_release(zstr); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + redis_cmd_set_ctx(cmd, ctx); - return SUCCESS; + return cmd; } -int redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long numlocal, numreplicas, timeout; - smart_string cmdstr = {0}; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_LONG(numlocal) Z_PARAM_LONG(numreplicas) Z_PARAM_LONG(timeout) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (numlocal < 0 || numreplicas < 0 || timeout < 0) { php_error_docref(NULL, E_WARNING, "No arguments can be negative"); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "WAITAOF"); - redis_cmd_append_sstr_long(&cmdstr, numlocal); - redis_cmd_append_sstr_long(&cmdstr, numreplicas); - redis_cmd_append_sstr_long(&cmdstr, timeout); + cmd = redis_cmd_create_literal(redis_sock, "WAITAOF"); + redis_cmd_cat_long(cmd, numlocal); + redis_cmd_cat_long(cmd, numreplicas); + redis_cmd_cat_long(cmd, timeout); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* Attempt to pull a long expiry from a zval. We're more restrictave than zval_get_long @@ -2465,30 +2281,29 @@ fill_set_options_zval(redisSetOptions *dst, zval *zv, zend_bool legacy_set) { } static void -redis_cmd_append_sstr_expiry(smart_string *cmdstr, redisExpiryOptions *e) +redis_cmd_cat_expiry(RedisCmd *cmd, redisExpiryOptions *e) { if (e->type == REDIS_EXPIRY_NONE && !e->keepttl) return; if (e->keepttl) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "KEEPTTL"); + redis_cmd_cat_literal(cmd, "KEEPTTL"); return; } else if (e->type == REDIS_EXPIRY_EX) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "EX"); + redis_cmd_cat_literal(cmd, "EX"); } else if (e->type == REDIS_EXPIRY_PX) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PX"); + redis_cmd_cat_literal(cmd, "PX"); } else if (e->type == REDIS_EXPIRY_EXAT) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "EXAT"); + redis_cmd_cat_literal(cmd, "EXAT"); } else if (e->type == REDIS_EXPIRY_PXAT) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PXAT"); + redis_cmd_cat_literal(cmd, "PXAT"); } - redis_cmd_append_sstr_zend_long(cmdstr, e->ttl); + redis_cmd_cat_long(cmd, e->ttl); } static void -redis_cmd_append_eq_clause(smart_string *cmdstr, RedisSock *redis_sock, - redisEqType type, zval *zv) +redis_cmd_cat_eq_clause(RedisCmd *cmd, redisEqType type, zval *zv) { zend_bool pack; @@ -2496,188 +2311,156 @@ redis_cmd_append_eq_clause(smart_string *cmdstr, RedisSock *redis_sock, return; if (type == REDIS_IFEQ) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFEQ"); + redis_cmd_cat_literal(cmd, "IFEQ"); } else if (type == REDIS_IFNE) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFNE"); + redis_cmd_cat_literal(cmd, "IFNE"); } else if (type == REDIS_IFDEQ) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFDEQ"); + redis_cmd_cat_literal(cmd, "IFDEQ"); } else if (type == REDIS_IFDNE) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFDNE"); + redis_cmd_cat_literal(cmd, "IFDNE"); } pack = type == REDIS_IFEQ || type == REDIS_IFNE; - redis_cmd_append_sstr_zval(cmdstr, zv, pack ? redis_sock : NULL); + if (pack) { + redis_cmd_cat_zval(cmd, zv); + } else { + redis_cmd_cat_zval_zstr(cmd, zv); + } } static void -redis_cmd_append_sstr_set_type(smart_string *cmdstr, redisSetType type) { +redis_cmd_cat_set_type(RedisCmd *cmd, redisSetType type) { if (type == REDIS_SET_NX) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "NX"); + redis_cmd_cat_literal(cmd, "NX"); } else if (type == REDIS_SET_XX) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "XX"); + redis_cmd_cat_literal(cmd, "XX"); } } /* MSETEX */ -int redis_msetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_msetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { redisSetOptions opt = {0}; - smart_string cmdstr = {0}; zval *zexp = NULL; HashTable *kvals; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ARRAY_HT(kvals) Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(zexp) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(kvals) == 0) { php_error_docref(NULL, E_WARNING, "No key/value pairs provided"); - return FAILURE; + return NULL; } if (fill_set_options_zval(&opt, zexp, 0) != SUCCESS) - return FAILURE; + return NULL; - argc = 1 + (2 * zend_hash_num_elements(kvals)) + !!opt.type; - if (opt.expiry.keepttl) { - argc += 1; - } else if (opt.expiry.type != REDIS_EXPIRY_NONE) { - argc += 2; - } + cmd = redis_cmd_create_literal(redis_sock, "MSETEX"); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "MSETEX"); + redis_cmd_cat_u64(cmd, zend_hash_num_elements(kvals)); + redis_cmd_try_cat_key(cmd, redis_cmd_cat_mset_kvals, kvals); + redis_cmd_cat_set_type(cmd, opt.type); + redis_cmd_cat_expiry(cmd, &opt.expiry); - redis_cmd_append_sstr_u64(&cmdstr, zend_hash_num_elements(kvals)); - redis_cmd_append_sstr_mset_kvals(&cmdstr, redis_sock, kvals, slot); - redis_cmd_append_sstr_set_type(&cmdstr, opt.type); - redis_cmd_append_sstr_expiry(&cmdstr, &opt.expiry); - - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* SET */ -int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zval *z_value = NULL, *z_opts = NULL; - smart_string cmdstr = {0}; redisSetOptions opt = {0}; zend_string *key; - int argc = 2; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(key) Z_PARAM_ZVAL(z_value) Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(z_opts) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (fill_set_options_zval(&opt, z_opts, 1) != SUCCESS) - return FAILURE; + return NULL; /* You can't use IFEQ with NX or XX */ if (opt.type && opt.eq.type != REDIS_IF_NONE) { php_error_docref(NULL, E_WARNING, "IF clauses can't be combined with NX/XX"); - return FAILURE; + return NULL; } /* Backward compatibility: If we are passed no options except an EXPIRE ttl, we * actually execute the SETEX command */ if (opt.expiry.ttl > 0 && opt.expiry.type == REDIS_EXPIRY_NONE && !opt.type) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "Klv", key, opt.expiry.ttl, - z_value); - return SUCCESS; - } - - /* Add additional argc depending on options */ - argc += (opt.eq.type != REDIS_IF_NONE ? 2 : 0) + !!opt.type + !!opt.get; - if (opt.expiry.keepttl) { - argc += 1; - } else if (opt.expiry.type != REDIS_EXPIRY_NONE) { - argc += 2; + return redis_cmd_fmt(redis_sock, "SETEX", "Klv", key, opt.expiry.ttl, + z_value); } /* Initial SET */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "SET"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zval(&cmdstr, z_value, redis_sock); + cmd = redis_cmd_create_literal(redis_sock, "SET"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zval(cmd, z_value); if (opt.eq.type != REDIS_IF_NONE) { - redis_cmd_append_eq_clause(&cmdstr, redis_sock, opt.eq.type, opt.eq.zval); + redis_cmd_cat_eq_clause(cmd, opt.eq.type, opt.eq.zval); } else if (opt.type != REDIS_SET_NONE) { - redis_cmd_append_sstr_set_type(&cmdstr, opt.type); + redis_cmd_cat_set_type(cmd, opt.type); } if (opt.get) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GET"); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_literal(cmd, "GET"); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } - redis_cmd_append_sstr_expiry(&cmdstr, &opt.expiry); + redis_cmd_cat_expiry(cmd, &opt.expiry); - /* Push command and length to the caller */ - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* MGET */ -int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; HashTable *keys = NULL; + RedisCmd *cmd; zval *zkey; - /* RedisCluster has a custom MGET implementation */ - ZEND_ASSERT(slot == NULL); - ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(keys) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(keys) == 0) - return FAILURE; + return NULL; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, zend_hash_num_elements(keys), "MGET"); + cmd = redis_cmd_create_literal(redis_sock, "MGET"); ZEND_HASH_FOREACH_VAL(keys, zkey) { ZVAL_DEREF(zkey); - redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot); + redis_cmd_try_cat_key_zval(cmd, zkey); } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int -redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; - char *key, *exp_type = NULL; + char *exp_type = NULL; zval *z_opts = NULL, *z_ele; + zend_string *zkey, *key; zend_long expire = -1; zend_bool persist = 0; - zend_string *zkey; - size_t key_len; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", - &key, &key_len, &z_opts) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|a", &key, &z_opts) + == FAILURE) { - return FAILURE; + return NULL; } if (z_opts != NULL) { @@ -2707,61 +2490,57 @@ redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (exp_type != NULL && expire < 1) { php_error_docref(NULL, E_WARNING, "EXPIRE can't be < 1"); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (exp_type ? 2 : persist), "GETEX"); - redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "GETEX"); + redis_cmd_cat_key_zstr(cmd, key); if (exp_type != NULL) { - redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type)); - redis_cmd_append_sstr_long(&cmdstr, expire); + redis_cmd_cat_str(cmd, exp_type, strlen(exp_type)); + redis_cmd_cat_long(cmd, expire); } else if (persist) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "PERSIST"); + redis_cmd_cat_literal(cmd, "PERSIST"); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* BRPOPLPUSH */ -int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock) { zend_string *src = NULL, *dst = NULL; double timeout = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, 3) Z_PARAM_STR(src) Z_PARAM_STR(dst) Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - - src = redis_key_prefix_zstr(redis_sock, src); - dst = redis_key_prefix_zstr(redis_sock, dst); - - if (slot && (*slot = cluster_hash_key_zstr(src)) != cluster_hash_key_zstr(dst)) { - php_error_docref(NULL, E_WARNING, "Keys must hash to the same slot"); - zend_string_release(src); - zend_string_release(dst); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* Consistency with Redis. If timeout < 0 use RPOPLPUSH */ if (timeout < 0) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "RPOPLPUSH", "SS", src, dst); + cmd = redis_cmd_create_literal(redis_sock, "RPOPLPUSH"); } else if (fabs(timeout - (long)timeout) < .0001) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "SSd", src, dst, (long)timeout); + cmd = redis_cmd_create_literal(redis_sock, "BRPOPLPUSH"); } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BRPOPLPUSH", "SSf", src, dst, timeout); + cmd = redis_cmd_create_literal(redis_sock, "BRPOPLPUSH"); } - zend_string_release(src); - zend_string_release(dst); + redis_cmd_cat_key_zstr(cmd, src); + redis_cmd_try_cat_key_zstr(cmd, dst); - return SUCCESS; + if (timeout >= 0) { + if (fabs(timeout - (long)timeout) < .0001) { + redis_cmd_cat_long(cmd, (long)timeout); + } else { + redis_cmd_cat_double(cmd, timeout); + } + } + + return cmd; } /* To maintain backward compatibility with earlier versions of phpredis, we @@ -2771,55 +2550,46 @@ int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, #define TYPE_DECR 1 /* Handle INCR(BY) and DECR(BY) depending on optional increment value */ -static int +static RedisCmd * redis_atomic_increment(INTERNAL_FUNCTION_PARAMETERS, int type, - RedisSock *redis_sock, char **cmd, int *cmd_len, - short *slot, void **ctx) + RedisSock *redis_sock) { - char *key; - size_t key_len; zend_long val = 1; + zend_string *key; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, - &val) == FAILURE) - { - return FAILURE; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &key, &val) == FAILURE) { + return NULL; } /* If our value is 1 we use INCR/DECR. For other values, treat the call as * an INCRBY or DECRBY call */ if (type == TYPE_INCR) { if (val == 1) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "INCR", "k", key, key_len); + return redis_cmd_fmt(redis_sock, "INCR", "K", key); } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "INCRBY", "kl", key, key_len, val); + return redis_cmd_fmt(redis_sock, "INCRBY", "Kl", key, val); } } else { if (val == 1) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "DECR", "k", key, key_len); + return redis_cmd_fmt(redis_sock, "DECR", "K", key); } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "DECRBY", "kl", key, key_len, val); + return redis_cmd_fmt(redis_sock, "DECRBY", "Kl", key, val); } } - - /* Success */ - return SUCCESS; } /* INCR */ -int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { return redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, - TYPE_INCR, redis_sock, cmd, cmd_len, slot, ctx); + TYPE_INCR, redis_sock); } /* DECR */ -int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { return redis_atomic_increment(INTERNAL_FUNCTION_PARAM_PASSTHRU, - TYPE_DECR, redis_sock, cmd, cmd_len, slot, ctx); + TYPE_DECR, redis_sock); } typedef enum xdelExMode { @@ -2829,20 +2599,19 @@ typedef enum xdelExMode { REDIS_XDELEX_ACKED, } xdelExMode; -int redis_delex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_delex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { redisEqType type = REDIS_IF_NONE; - smart_string cmdstr = {0}; zend_string *key, *ztype; HashTable *ht = NULL; zval *zv = NULL; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(key) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(ht) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (ht != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(ht, ztype, zv) { @@ -2858,58 +2627,43 @@ int redis_delex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, type != REDIS_IF_NONE ? 3 : 1, "DELEX"); + cmd = redis_cmd_create_literal(redis_sock, "DELEX"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_eq_clause(&cmdstr, redis_sock, type, zv); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_eq_clause(cmd, type, zv); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* HINCRBY */ -int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - char *key, *mem; - size_t key_len, mem_len; + zend_string *key, *mem; zend_long byval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key, &key_len, - &mem, &mem_len, &byval) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl", &key, &mem, &byval) + == FAILURE) { - return FAILURE; + return NULL; } - // Construct command - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HINCRBY", "ksl", key, key_len, mem, mem_len, byval); - - // Success - return SUCCESS; + return redis_cmd_fmt(redis_sock, "HINCRBY", "KSl", key, mem, byval); } /* HINCRBYFLOAT */ -int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock) { - char *key, *mem; - size_t key_len, mem_len; - double byval; + zend_string *key, *mem; + double dval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssd", &key, &key_len, - &mem, &mem_len, &byval) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSd", &key, &mem, &dval) + == FAILURE) { - return FAILURE; + return NULL; } - // Construct command - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HINCRBYFLOAT", "ksf", key, key_len, mem, - mem_len, byval); - - // Success - return SUCCESS; + return redis_cmd_fmt(redis_sock, "HINCRBYFLOAT", "KSf", key, mem, dval); } static inline zval *coerce_hash_field(zval *zv, zval *aux) { @@ -2976,113 +2730,104 @@ static zend_bool hmget_filter(zval *zv) { (Z_TYPE_P(zv) == IS_LONG); } -static void -redis_cmd_append_sstr_hash_fields(smart_string *cmdstr, HashTable *ht) { +static void redis_cmd_cat_hash_fields(RedisCmd *cmd, HashTable *ht) { zend_string *key; zend_ulong idx; ZEND_HASH_FOREACH_KEY(ht, idx, key) { if (key) { - redis_cmd_append_sstr_zstr(cmdstr, key); + redis_cmd_cat_zstr(cmd, key); } else { - redis_cmd_append_sstr_long(cmdstr, idx); + redis_cmd_cat_long(cmd, idx); } } ZEND_HASH_FOREACH_END(); - } -/* HMGET */ -int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +void redis_cmd_ctx_hash_dtor(void *ptr) { + HashTable *ht = ptr; + + if (GC_REFCOUNT(ht) == 1) { + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); + } else { + GC_DELREF(ht); + } +} + +RedisCmd *redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { HashTable *fields = NULL, *htctx = NULL; - smart_string cmdstr = {0}; zend_string *key = NULL; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(fields) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(fields) == 0) - return FAILURE; + return NULL; htctx = build_hash_context_ht(fields, hmget_filter); if (htctx == NULL) { php_error_docref(NULL, E_WARNING, "No valid fields provided"); - return FAILURE; + return NULL; } - argc = 1 + zend_hash_num_elements(htctx); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "HMGET"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "HMGET"); + redis_cmd_cat_key_zstr(cmd, key); - redis_cmd_append_sstr_hash_fields(&cmdstr, htctx); + redis_cmd_cat_hash_fields(cmd, htctx); - // Push out command, length, and key context - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - *ctx = htctx; + redis_cmd_set_ctx_ex(cmd, htctx, redis_cmd_ctx_hash_dtor); - return SUCCESS; + return cmd; } /* HMSET */ -int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; zend_string *key = NULL; HashTable *ht = NULL; uint32_t fields; + RedisCmd *cmd; zend_ulong idx; zval *zv; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(ht) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); fields = zend_hash_num_elements(ht); if (fields == 0) - return FAILURE; + return NULL; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (2 * fields), "HMSET"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "HMSET"); + redis_cmd_cat_key_zstr(cmd, key); ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zv) { if (key) { - redis_cmd_append_sstr_zstr(&cmdstr, key); + redis_cmd_cat_zstr(cmd, key); } else { - redis_cmd_append_sstr_long(&cmdstr, idx); + redis_cmd_cat_long(cmd, idx); } - redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); + redis_cmd_cat_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); - *cmd_len = cmdstr.len; - *cmd = cmdstr.c; - - return SUCCESS; + return cmd; } /* HSTRLEN */ -int -redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - char *key, *field; - size_t key_len, field_len; + zend_string *key, *field; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, - &field, &field_len) == FAILURE - ) { - return FAILURE; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &key, &field) == FAILURE) { + return NULL; } - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HSTRLEN", "ks", key, key_len, field, field_len); - - return SUCCESS; + return redis_cmd_fmt(redis_sock, "HSTRLEN", "KS", key, field); } static void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) { @@ -3126,73 +2871,51 @@ static void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) { } /* LCS */ -int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *key1 = NULL, *key2 = NULL; - smart_string cmdstr = {0}; HashTable *ht = NULL; redisLcsOptions opt; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(key1) Z_PARAM_STR(key2) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(ht) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - - key1 = redis_key_prefix_zstr(redis_sock, key1); - key2 = redis_key_prefix_zstr(redis_sock, key2); - - if (slot) { - *slot = cluster_hash_key_zstr(key1); - if (*slot != cluster_hash_key_zstr(key2)) { - php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); - zend_string_release(key1); - zend_string_release(key2); - return FAILURE; - } - } + ZEND_PARSE_PARAMETERS_END_EX(return NULL); redis_get_lcs_options(&opt, ht); - argc = 2 + !!opt.idx + !!opt.len + !!opt.withmatchlen + (opt.minmatchlen ? 2 : 0); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LCS"); + cmd = redis_cmd_create_literal(redis_sock, "LCS"); - redis_cmd_append_sstr_zstr(&cmdstr, key1); - redis_cmd_append_sstr_zstr(&cmdstr, key2); + redis_cmd_cat_key_zstr(cmd, key1); + redis_cmd_try_cat_key_zstr(cmd, key2); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.idx, "IDX"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.len, "LEN"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withmatchlen, "WITHMATCHLEN"); + redis_cmd_cat_literal_if(cmd, opt.idx, "IDX"); + redis_cmd_cat_literal_if(cmd, opt.len, "LEN"); + redis_cmd_cat_literal_if(cmd, opt.withmatchlen, "WITHMATCHLEN"); if (opt.minmatchlen) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINMATCHLEN"); - redis_cmd_append_sstr_long(&cmdstr, opt.minmatchlen); + redis_cmd_cat_literal(cmd, "MINMATCHLEN"); + redis_cmd_cat_long(cmd, opt.minmatchlen); } - zend_string_release(key1); - zend_string_release(key2); - - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } -int redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode; - smart_string cmdstr = {0}; zend_string *op = NULL; zend_long arg = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_LONG(arg) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_string_equals_literal_ci(op, "GET")) { mode = SLOWLOG_GET; @@ -3202,19 +2925,16 @@ int redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, mode = SLOWLOG_RESET; } else { php_error_docref(NULL, E_WARNING, "Unknown SLOWLOG operation '%s'", ZSTR_VAL(op)); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2), "SLOWLOG"); - redis_cmd_append_sstr_zstr(&cmdstr, op); + cmd = redis_cmd_create_literal(redis_sock, "SLOWLOG"); + redis_cmd_cat_zstr(cmd, op); if (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) - redis_cmd_append_sstr_long(&cmdstr, arg); + redis_cmd_cat_long(cmd, arg); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } void redis_get_restore_options(redisRestoreOptions *dst, HashTable *ht) { @@ -3266,15 +2986,13 @@ void redis_get_restore_options(redisRestoreOptions *dst, HashTable *ht) { } /* RESTORE */ -int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *key, *value = NULL; - smart_string cmdstr = {0}; HashTable *options = NULL; redisRestoreOptions opt; zend_long timeout = 0; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, 4) { Z_PARAM_STR(key) @@ -3282,44 +3000,39 @@ int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR(value) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(options) - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); redis_get_restore_options(&opt, options); - argc = 3 + (opt.idletime>-1?2:0) + (opt.freq>-1?2:0) + !!opt.absttl + !!opt.replace; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "RESTORE"); + cmd = redis_cmd_create_literal(redis_sock, "RESTORE"); - redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); - redis_cmd_append_sstr_long(&cmdstr, timeout); - redis_cmd_append_sstr_zstr(&cmdstr, value); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, timeout); + redis_cmd_cat_zstr(cmd, value); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.replace, "REPLACE"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.absttl, "ABSTTL"); + redis_cmd_cat_literal_if(cmd, opt.replace, "REPLACE"); + redis_cmd_cat_literal_if(cmd, opt.absttl, "ABSTTL"); if (opt.idletime > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IDLETIME"); - redis_cmd_append_sstr_long(&cmdstr, opt.idletime); + redis_cmd_cat_literal(cmd, "IDLETIME"); + redis_cmd_cat_long(cmd, opt.idletime); } if (opt.freq > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FREQ"); - redis_cmd_append_sstr_long(&cmdstr, opt.freq); + redis_cmd_cat_literal(cmd, "FREQ"); + redis_cmd_cat_long(cmd, opt.freq); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* BITPOS key bit [start [end [BYTE | BIT]]] */ -int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long start = 0, end = -1; zend_bool bit = 0, bybit = 0; - smart_string cmdstr = {0}; zend_string *key = NULL; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 5) Z_PARAM_STR(key) @@ -3328,278 +3041,223 @@ int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_LONG(start) Z_PARAM_LONG(end) Z_PARAM_BOOL(bybit) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (ZEND_NUM_ARGS() > 2 ? 2 : 0) + !!bybit, "BITPOS"); + cmd = redis_cmd_create_literal(redis_sock, "BITPOS"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_long(&cmdstr, bit); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, bit); /* Start and length if we were passed either */ if (ZEND_NUM_ARGS() > 2) { - redis_cmd_append_sstr_long(&cmdstr, start); - redis_cmd_append_sstr_long(&cmdstr, end); + redis_cmd_cat_long(cmd, start); + redis_cmd_cat_long(cmd, end); } /* Finally, BIT or BYTE if we were passed that argument */ - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, !!bybit, "BIT"); + redis_cmd_cat_literal_if(cmd, !!bybit, "BIT"); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* BITOP */ -int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - zval *z_args; - int i, argc = ZEND_NUM_ARGS(); - smart_string cmdstr = {0}; - short s2; +RedisCmd *redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *op, *dstkey, *srckey; + RedisCmd *cmd; + zval *args; + int argc; - // Allocate space for args, parse them as an array - z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || - argc < 3 || Z_TYPE(z_args[0]) != IS_STRING) - { - efree(z_args); - return FAILURE; + ZEND_PARSE_PARAMETERS_START(3, -1) + Z_PARAM_STR(op) + Z_PARAM_STR(dstkey) + Z_PARAM_STR(srckey) + Z_PARAM_VARIADIC('*', args, argc) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); + + cmd = redis_cmd_create_literal(redis_sock, "BITOP"); + + redis_cmd_cat_zstr(cmd, op); + redis_cmd_cat_key_zstr(cmd, dstkey); + redis_cmd_try_cat_key_zstr(cmd, srckey); + + for (int i = 0; i < argc; i++) { + redis_cmd_try_cat_key_zval(cmd, &args[i]); } - // If we were passed a slot pointer, init to a sentinel value - if (slot) *slot = -1; - - // Initialize command construction, add our operation argument - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("BITOP")); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); - - // Now iterate over our keys argument - for (i = 1; i < argc; i++) { - // Append the key - redis_cmd_append_sstr_key_zval(&cmdstr, &z_args[i], redis_sock, slot ? &s2 : NULL); - - // Verify slot if this is a Cluster request - if (slot) { - if (*slot != -1 && s2 != *slot) { - php_error_docref(NULL, E_WARNING, "Warning, not all keys hash to the same slot!"); - efree(z_args); - efree(cmdstr.c); - return FAILURE; - } - *slot = s2; - } - } - - // Free our argument array - efree(z_args); - - // Push out variables - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* BITCOUNT */ -int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key; - size_t key_len; +RedisCmd *redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long start = 0, end = -1; zend_bool isbit = 0; + zend_string *key; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llb", &key, &key_len, - &start, &end, &isbit) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(start) + Z_PARAM_LONG(end) + Z_PARAM_BOOL(isbit) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (isbit) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdds", key, key_len, - (int)start, (int)end, "BIT", 3); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "BITCOUNT", "kdd", key, key_len, - (int)start, (int)end); - } + cmd = redis_cmd_create_literal(redis_sock, "BITCOUNT"); - return SUCCESS; + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, start); + redis_cmd_cat_long(cmd, end); + + redis_cmd_cat_literal_if(cmd, isbit, "BIT"); + + return cmd; } /* PFADD and PFMERGE are the same except that in one case we serialize, * and in the other case we key prefix */ -static int redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, int kw_len, int is_keys, char **cmd, - int *cmd_len, short *slot) +static RedisCmd *redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, int kw_len, int is_keys) { - smart_string cmdstr = {0}; zend_string *key = NULL; HashTable *ht = NULL; + RedisCmd *cmd; zval *z_ele; - int argc=1; - short s2; // Parse arguments ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(ht) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - - argc += zend_hash_num_elements(ht); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); // We need at least two arguments - if (argc < 2) { - return FAILURE; + if (zend_hash_num_elements(ht) + 1 < 2) { + return NULL; } - redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create(redis_sock, kw, kw_len); + + redis_cmd_cat_key_zstr(cmd, key); // Append our array of keys or serialized values */ ZEND_HASH_FOREACH_VAL(ht, z_ele) { if (is_keys) { - redis_cmd_append_sstr_key_zval(&cmdstr, z_ele, redis_sock, slot ? &s2 : NULL); - if (slot && *slot != s2) { - php_error_docref(0, E_WARNING, "All keys must hash to the same slot!"); - return FAILURE; - } + redis_cmd_try_cat_key_zval(cmd, z_ele); } else { - redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); + redis_cmd_cat_zval(cmd, z_ele); } } ZEND_HASH_FOREACH_END(); - // Push output arguments - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* PFADD */ -int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - ZEND_STRL("PFADD"), 0, cmd, cmd_len, slot); + ZEND_STRL("PFADD"), 0); } /* PFMERGE */ -int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { return redis_gen_pf_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - ZEND_STRL("PFMERGE"), 1, cmd, cmd_len, slot); + ZEND_STRL("PFMERGE"), 1); } /* PFCOUNT */ -int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd *redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zval *zarg = NULL, *zv; - short slot2 = -1; uint32_t keys; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ZVAL(zarg) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); + + cmd = redis_cmd_create_literal(redis_sock, "PFCOUNT"); if (Z_TYPE_P(zarg) == IS_STRING) { - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "PFCOUNT"); - redis_cmd_append_sstr_key_zstr(&cmdstr, Z_STR_P(zarg), redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, Z_STR_P(zarg)); } else if (Z_TYPE_P(zarg) == IS_ARRAY) { keys = zend_hash_num_elements(Z_ARRVAL_P(zarg)); - if (keys == 0) - return FAILURE; - - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, keys, "PFCOUNT"); + if (keys == 0) { + redis_cmd_free(cmd); + return NULL; + } ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zarg), zv) { - redis_cmd_append_sstr_key_zval(&cmdstr, zv, redis_sock, slot); - if (slot) { - if (slot2 != -1 && slot2 != *slot) - goto cross_slot; - slot2 = *slot; - } + redis_cmd_try_cat_key_zval(cmd, zv); } ZEND_HASH_FOREACH_END(); } else { php_error_docref(NULL, E_WARNING, "Argument must be either an array or a string"); - return FAILURE; + redis_cmd_free(cmd); + return NULL; } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; - -cross_slot: - php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); - efree(cmdstr.c); - return FAILURE; + return cmd; } -int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *user = NULL, *pass = NULL; + RedisCmd *cmd; zval *ztest; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z!", &ztest) == FAILURE || redis_extract_auth_info(ztest, &user, &pass) == FAILURE) { - return FAILURE; + return NULL; } + cmd = redis_cmd_create_literal(redis_sock, "AUTH"); + /* Construct either AUTH or AUTH */ - if (user && pass) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "SS", user, pass); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "AUTH", "S", pass); - } + if (user) + redis_cmd_cat_zstr(cmd, user); + if (pass) + redis_cmd_cat_zstr(cmd, pass); redis_sock_set_auth(redis_sock, user, pass); - if (user) zend_string_release(user); - if (pass) zend_string_release(pass); + if (user) + zend_string_release(user); + if (pass) + zend_string_release(pass); - return SUCCESS; + return cmd; } /* SETBIT */ -int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key; - size_t key_len; +RedisCmd *redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long offset; + zend_string *key; zend_bool val; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slb", &key, &key_len, - &offset, &val) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(key) + Z_PARAM_LONG(offset) + Z_PARAM_BOOL(val) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); // Validate our offset if (offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) { php_error_docref(0, E_WARNING, "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); - return FAILURE; + return NULL; } - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETBIT", "kld", key, key_len, offset, (int)val); + cmd = redis_cmd_create_literal(redis_sock, "SETBIT"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, offset); + redis_cmd_cat_long(cmd, val); - return SUCCESS; + return cmd; } -int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { zend_string *src = NULL, *dst = NULL, *from = NULL, *to = NULL; - smart_string cmdstr = {0}; double timeout = 0.0; - short slot2 = 0; + RedisCmd *cmd; int blocking; blocking = toupper(*kw) == 'B'; @@ -3612,109 +3270,102 @@ int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (blocking) { Z_PARAM_DOUBLE(timeout) } - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (!zend_string_equals_literal_ci(from, "LEFT") && !zend_string_equals_literal_ci(from, "RIGHT")) { php_error_docref(NULL, E_WARNING, "Wherefrom argument must be 'LEFT' or 'RIGHT'"); - return FAILURE; + return NULL; } else if (!zend_string_equals_literal_ci(to, "LEFT") && !zend_string_equals_literal_ci(to, "RIGHT")) { php_error_docref(NULL, E_WARNING, "Whereto argument must be 'LEFT' or 'RIGHT'"); - return FAILURE; + return NULL; } - redis_cmd_init_sstr(&cmdstr, 4 + !!blocking, kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); - redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_key_zstr(cmd, src); + redis_cmd_try_cat_key_zstr(cmd, dst); - /* Protect the user from CROSSLOT errors */ - if (slot && slot2 != *slot) { - php_error_docref(NULL, E_WARNING, "Both keys must hash to the same slot!"); - efree(cmdstr.c); - return FAILURE; - } + redis_cmd_cat_zstr(cmd, from); + redis_cmd_cat_zstr(cmd, to); + if (blocking) redis_cmd_cat_double(cmd, timeout); - redis_cmd_append_sstr_zstr(&cmdstr, from); - redis_cmd_append_sstr_zstr(&cmdstr, to); - if (blocking) redis_cmd_append_sstr_dbl(&cmdstr, timeout); - - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* LINSERT */ -int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key, *pos; - size_t key_len, pos_len; +RedisCmd * +redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *key, *pos; zval *z_val, *z_pivot; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszz", &key, &key_len, - &pos, &pos_len, &z_pivot, &z_val) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(4, 4) + Z_PARAM_STR(key) + Z_PARAM_STR(pos) + Z_PARAM_ZVAL(z_pivot) + Z_PARAM_ZVAL(z_val) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); // Validate position - if (strcasecmp(pos, "after") && strcasecmp(pos, "before")) { + if (!zend_string_equals_literal_ci(pos, "BEFORE") && + !zend_string_equals_literal_ci(pos, "AFTER")) + { + /* Valid */ php_error_docref(NULL, E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); - return FAILURE; + return NULL; } - /* Construct command */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LINSERT", "ksvv", key, key_len, pos, - pos_len, z_pivot, z_val); + cmd = redis_cmd_create_literal(redis_sock, "LINSERT"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, pos); + redis_cmd_cat_zval(cmd, z_pivot); + redis_cmd_cat_zval(cmd, z_val); - // Success - return SUCCESS; + return cmd; } /* LREM */ -int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key; - size_t key_len; +RedisCmd *redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long count = 0; + zend_string *key; + RedisCmd *cmd; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l", &key, &key_len, - &z_val, &count) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(z_val) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(count) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - /* Construct command */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "LREM", "kdv", key, key_len, count, z_val); + cmd = redis_cmd_create_literal(redis_sock, "LREM"); - // Success! - return SUCCESS; + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, count); + redis_cmd_cat_zval(cmd, z_val); + + return cmd; } -int -redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key; - int argc = 2; - size_t key_len; - smart_string cmdstr = {0}; +RedisCmd * +redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *key; zend_bool withrank = 0; zend_long rank = 0, count = -1, maxlen = -1; zend_string *zkey; - zval *z_val, *z_ele, *z_opts = NULL; + zval *z_val, *z_ele; + HashTable *ht_opts = NULL; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|a", - &key, &key_len, &z_val, &z_opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(z_val) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(ht_opts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (z_opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { + if (ht_opts != NULL) { + ZEND_HASH_FOREACH_STR_KEY_VAL(ht_opts, zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "count")) { @@ -3729,168 +3380,139 @@ redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); } - argc += (withrank ? 2 : 0) + (count >= 0 ? 2 : 0) + (maxlen >= 0 ? 2 : 0); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "LPOS"); + cmd = redis_cmd_create_literal(redis_sock, "LPOS"); - redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); - redis_cmd_append_sstr_zval(&cmdstr, z_val, redis_sock); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zval(cmd, z_val); if (withrank) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "RANK"); - redis_cmd_append_sstr_long(&cmdstr, rank); + redis_cmd_cat_literal(cmd, "RANK"); + redis_cmd_cat_long(cmd, rank); } if (count >= 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, count); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } if (maxlen >= 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN"); - redis_cmd_append_sstr_long(&cmdstr, maxlen); + redis_cmd_cat_literal(cmd, "MAXLEN"); + redis_cmd_cat_long(cmd, maxlen); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *src = NULL, *dst = NULL; - smart_string cmdstr = {0}; zval *zv = NULL; - short slot2; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, 3) { Z_PARAM_STR(src) Z_PARAM_STR(dst) Z_PARAM_ZVAL(zv) - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "SMOVE"); - redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); - redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); - redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); + cmd = redis_cmd_create_literal(redis_sock, "SMOVE"); - if (slot && *slot != slot2) { - php_error_docref(0, E_WARNING, "Source and destination keys don't hash to the same slot!"); - efree(cmdstr.c); - return FAILURE; - } + redis_cmd_cat_key_zstr(cmd, src); + redis_cmd_try_cat_key_zstr(cmd, dst); + redis_cmd_cat_zval(cmd, zv); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* HSET */ -int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { int i, argc; - smart_string cmdstr = {0}; zend_string *key, *zkey; zval *args, *z_ele; zend_ulong idx; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_STR(key) Z_PARAM_VARIADIC('*', args, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (argc == 1) { if (Z_TYPE_P(args) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(args)) == 0) { - return FAILURE; + return NULL; } - /* Initialize our command */ - redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL_P(args)) * 2, ZEND_STRL("HSET")); - - /* Append key */ - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "HSET"); + redis_cmd_cat_key_zstr(cmd, key); ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(args), idx, zkey, z_ele) { ZVAL_DEREF(z_ele); if (zkey == NULL) { - redis_cmd_append_sstr_long(&cmdstr, idx); + redis_cmd_cat_long(cmd, idx); } else { - redis_cmd_append_sstr_zstr(&cmdstr, zkey); + redis_cmd_cat_zstr(cmd, zkey); } - redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); + redis_cmd_cat_zval(cmd, z_ele); } ZEND_HASH_FOREACH_END(); } else { if (argc % 2 != 0) { - return FAILURE; + return NULL; } - /* Initialize our command */ - redis_cmd_init_sstr(&cmdstr, argc + 1, ZEND_STRL("HSET")); + cmd = redis_cmd_create_literal(redis_sock, "HSET"); - /* Append key */ - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, key); for (i = 0; i < argc; ++i) { if (i % 2) { - redis_cmd_append_sstr_zval(&cmdstr, &args[i], redis_sock); + redis_cmd_cat_zval(cmd, &args[i]); } else { - redis_cmd_append_sstr_zval(&cmdstr, &args[i], NULL); + redis_cmd_cat_zval_zstr(cmd, &args[i]); } } } - // Push out values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* HSETNX */ -int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key, *mem; - size_t key_len, mem_len; +RedisCmd *redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *key, *mem; + RedisCmd *cmd; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, - &mem, &mem_len, &z_val) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(key) + Z_PARAM_STR(mem) + Z_PARAM_ZVAL(z_val) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - /* Construct command */ - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "HSETNX", "ksv", key, key_len, mem, mem_len, z_val); + cmd = redis_cmd_create_literal(redis_sock, "HSETNX"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, mem); + redis_cmd_cat_zval(cmd, z_val); - // Success - return SUCCESS; + return cmd; } -int -redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key; - int count = 0; - size_t key_len; - smart_string cmdstr = {0}; +RedisCmd * +redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + HashTable *ht_opts = NULL; zend_bool withvalues = 0; - zval *z_opts = NULL, *z_ele; zend_string *zkey; + zend_string *key; + RedisCmd *cmd; + int count = 0; + zval *z_ele; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", - &key, &key_len, &z_opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(ht_opts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (z_opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_opts), zkey, z_ele) { + if (ht_opts != NULL) { + ZEND_HASH_FOREACH_STR_KEY_VAL(ht_opts, zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "count")) { @@ -3910,369 +3532,303 @@ redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (count == 0 && withvalues) count = 1; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withvalues, "HRANDFIELD"); - redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "HRANDFIELD"); + + redis_cmd_cat_key_zstr(cmd, key); if (count != 0) { - redis_cmd_append_sstr_long(&cmdstr, count); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_long(cmd, count); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } if (withvalues) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHVALUES"); - *ctx = PHPREDIS_CTX_PTR + 1; + redis_cmd_cat_literal(cmd, "WITHVALUES"); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR + 1); } - - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } -int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd * +redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long db = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(db) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (db < 0 || db > INT_MAX) - return FAILURE; + return NULL; - *ctx = (void*)(uintptr_t)db; - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SELECT", "d", db); + cmd = redis_cmd_create_literal(redis_sock, "SELECT"); + redis_cmd_cat_long(cmd, db); + redis_cmd_set_ctx(cmd, (void*)(uintptr_t)db); - return SUCCESS; + return cmd; } /* SRANDMEMBER */ -int redis_randmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd * +redis_randmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { uint32_t argc = ZEND_NUM_ARGS(); - smart_string cmdstr = {0}; zend_string *key = NULL; zend_long count = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(key) Z_PARAM_OPTIONAL Z_PARAM_LONG(count) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - redis_cmd_init_sstr(&cmdstr, 1 + (argc == 2), kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + + redis_cmd_cat_key_zstr(cmd, key); if (argc == 2) - redis_cmd_append_sstr_long(&cmdstr, count); + redis_cmd_cat_long(cmd, count); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - *ctx = argc == 2 ? PHPREDIS_CTX_PTR : NULL; + if (argc == 2) + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); - return SUCCESS; + return cmd; } /* ZINCRBY */ -int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key; - size_t key_len; +RedisCmd * +redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *key; + RedisCmd *cmd; double incrby; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sdz", &key, &key_len, - &incrby, &z_val) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(key) + Z_PARAM_DOUBLE(incrby) + Z_PARAM_ZVAL(z_val) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "ZINCRBY", "kfv", key, key_len, incrby, z_val); + cmd = redis_cmd_create_literal(redis_sock, "ZINCRBY"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_double(cmd, incrby); + redis_cmd_cat_zval(cmd, z_val); - return SUCCESS; + return cmd; } /* SORT */ -int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - zval *z_opts=NULL, *z_ele, z_argv; - char *key; - HashTable *ht_opts; - smart_string cmdstr = {0}; - size_t key_len; - int key_free; + HashTable *opts = NULL; + zend_string *key; + RedisCmd *cmd; + zval *zv; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, - &z_opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT(opts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); // If we don't have an options array, the command is quite simple - if (!z_opts || zend_hash_num_elements(Z_ARRVAL_P(z_opts)) == 0) { - // Construct command - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len); - - return SUCCESS; + if (!opts || zend_hash_num_elements(opts) == 0) { + return redis_cmd_fmt(redis_sock, kw, "K", key); } - // Create our hash table to hold our sort arguments - array_init(&z_argv); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); // SORT - key_free = redis_key_prefix(redis_sock, &key, &key_len); - add_next_index_stringl(&z_argv, key, key_len); - if (key_free) efree(key); - - // Set slot - CMD_SET_SLOT(slot,key,key_len); - - // Grab the hash table - ht_opts = Z_ARRVAL_P(z_opts); + redis_cmd_cat_key_zstr(cmd, key); // Handle BY pattern - if (((z_ele = zend_hash_str_find(ht_opts, "by", sizeof("by") - 1)) != NULL || - (z_ele = zend_hash_str_find(ht_opts, "BY", sizeof("BY") - 1)) != NULL - ) && Z_TYPE_P(z_ele) == IS_STRING - ) { + if (((zv = zend_hash_str_find(opts, ZEND_STRL("by"))) != NULL || + (zv = zend_hash_str_find(opts, ZEND_STRL("BY"))) != NULL + ) && Z_TYPE_P(zv) == IS_STRING) + { // "BY" option is disabled in cluster - if (slot) { + if (redis_sock->type == REDIS_SOCK_CLUSTER) { php_error_docref(NULL, E_WARNING, "SORT BY option is not allowed in Redis Cluster"); - zval_ptr_dtor_nogc(&z_argv); - return FAILURE; + redis_cmd_free(cmd); + return NULL; } // ... BY - add_next_index_stringl(&z_argv, "BY", sizeof("BY") - 1); - add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + redis_cmd_cat_literal(cmd, "BY"); + redis_cmd_cat_zstr(cmd, Z_STR_P(zv)); } // Handle ASC/DESC option - if (((z_ele = zend_hash_str_find(ht_opts, "sort", sizeof("sort") - 1)) != NULL || - (z_ele = zend_hash_str_find(ht_opts, "SORT", sizeof("SORT") - 1)) != NULL - ) && Z_TYPE_P(z_ele) == IS_STRING - ) { + if (((zv = zend_hash_str_find(opts, ZEND_STRL("sort"))) != NULL || + (zv = zend_hash_str_find(opts, ZEND_STRL("SORT"))) != NULL) && + Z_TYPE_P(zv) == IS_STRING) + { // 'asc'|'desc' - add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + redis_cmd_cat_zstr(cmd, Z_STR_P(zv)); } // STORE option - if (((z_ele = zend_hash_str_find(ht_opts, "store", sizeof("store") - 1)) != NULL || - (z_ele = zend_hash_str_find(ht_opts, "STORE", sizeof("STORE") - 1)) != NULL - ) && Z_TYPE_P(z_ele) == IS_STRING - ) { - // Slot verification - int cross_slot = slot && *slot != cluster_hash_key( - Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); - - if (cross_slot) { - php_error_docref(0, E_WARNING, - "Error, SORT key and STORE key have different slots!"); - zval_ptr_dtor_nogc(&z_argv); - return FAILURE; - } - - // STORE - add_next_index_stringl(&z_argv, "STORE", sizeof("STORE") - 1); - add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); - - // We are using STORE - *ctx = PHPREDIS_CTX_PTR; + if (((zv = zend_hash_str_find(opts, ZEND_STRL("store"))) != NULL || + (zv = zend_hash_str_find(opts, ZEND_STRL("STORE"))) != NULL) && + Z_TYPE_P(zv) == IS_STRING) + { + redis_cmd_cat_literal(cmd, "STORE"); + redis_cmd_try_cat_key_zval(cmd, zv); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } // GET option - if (((z_ele = zend_hash_str_find(ht_opts, "get", sizeof("get") - 1)) != NULL || - (z_ele = zend_hash_str_find(ht_opts, "GET", sizeof("GET") - 1)) != NULL - ) && (Z_TYPE_P(z_ele) == IS_STRING || Z_TYPE_P(z_ele) == IS_ARRAY) - ) { + if (((zv = zend_hash_str_find(opts, ZEND_STRL("get"))) != NULL || + (zv = zend_hash_str_find(opts, ZEND_STRL("GET"))) != NULL) && + (Z_TYPE_P(zv) == IS_STRING || Z_TYPE_P(zv) == IS_ARRAY)) + { // Disabled in cluster - if (slot) { + if (redis_sock->type == REDIS_SOCK_CLUSTER) { php_error_docref(NULL, E_WARNING, "GET option for SORT disabled in Redis Cluster"); - zval_ptr_dtor_nogc(&z_argv); - return FAILURE; + redis_cmd_free(cmd); + return NULL; } // If it's a string just add it - if (Z_TYPE_P(z_ele) == IS_STRING) { - add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); - add_next_index_stringl(&z_argv, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + if (Z_TYPE_P(zv) == IS_STRING) { + redis_cmd_cat_literal(cmd, "GET"); + redis_cmd_cat_zstr(cmd, Z_STR_P(zv)); } else { - int added = 0; - zval *z_key; + zend_bool added = 0; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_ele), z_key) { - // If we can't get the data, or it's not a string, skip - if (z_key == NULL || Z_TYPE_P(z_key) != IS_STRING) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), zv) { + if (Z_TYPE_P(zv) != IS_STRING) continue; - } - /* Add get per thing we're getting */ - add_next_index_stringl(&z_argv, "GET", sizeof("GET") - 1); - // Add this key to our argv array - add_next_index_stringl(&z_argv, Z_STRVAL_P(z_key), Z_STRLEN_P(z_key)); - added++; + redis_cmd_cat_literal(cmd, "GET"); + redis_cmd_cat_zval_zstr(cmd, zv); + added = 1; } ZEND_HASH_FOREACH_END(); - // Make sure we were able to add at least one if (added == 0) { php_error_docref(NULL, E_WARNING, "Array of GET values requested, but none are valid"); - zval_ptr_dtor_nogc(&z_argv); - return FAILURE; + redis_cmd_free(cmd); + return NULL; } } } // ALPHA - if (((z_ele = zend_hash_str_find(ht_opts, "alpha", sizeof("alpha") - 1)) != NULL || - (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL) && - zend_is_true(z_ele) - ) { - add_next_index_stringl(&z_argv, "ALPHA", sizeof("ALPHA") - 1); + if (((zv = zend_hash_str_find(opts, ZEND_STRL("alpha"))) != NULL || + (zv = zend_hash_str_find(opts, ZEND_STRL("ALPHA"))) != NULL) && + zend_is_true(zv)) + { + redis_cmd_cat_literal(cmd, "ALPHA"); } // LIMIT - if (((z_ele = zend_hash_str_find(ht_opts, "limit", sizeof("limit") - 1)) != NULL || - (z_ele = zend_hash_str_find(ht_opts, "LIMIT", sizeof("LIMIT") - 1)) != NULL - ) && Z_TYPE_P(z_ele) == IS_ARRAY - ) { - HashTable *ht_off = Z_ARRVAL_P(z_ele); - zval *z_off, *z_cnt; + if (((zv = zend_hash_str_find(opts, ZEND_STRL("limit"))) != NULL || + (zv = zend_hash_str_find(opts, ZEND_STRL("LIMIT"))) != NULL) && + Z_TYPE_P(zv) == IS_ARRAY) + { + HashTable *ht_off = Z_ARRVAL_P(zv); + zval *zoff, *zcnt; - if ((z_off = zend_hash_index_find(ht_off, 0)) != NULL && - (z_cnt = zend_hash_index_find(ht_off, 1)) != NULL + if ((zoff = zend_hash_index_find(ht_off, 0)) != NULL && + (zcnt = zend_hash_index_find(ht_off, 1)) != NULL ) { - if ((Z_TYPE_P(z_off) != IS_STRING && Z_TYPE_P(z_off) != IS_LONG) || - (Z_TYPE_P(z_cnt) != IS_STRING && Z_TYPE_P(z_cnt) != IS_LONG) + if ((Z_TYPE_P(zoff) != IS_STRING && Z_TYPE_P(zoff) != IS_LONG) || + (Z_TYPE_P(zcnt) != IS_STRING && Z_TYPE_P(zcnt) != IS_LONG) ) { php_error_docref(NULL, E_WARNING, "LIMIT options on SORT command must be longs or strings"); - zval_ptr_dtor_nogc(&z_argv); - return FAILURE; + redis_cmd_free(cmd); + return NULL; } // Add LIMIT argument - add_next_index_stringl(&z_argv, "LIMIT", sizeof("LIMIT") - 1); + redis_cmd_cat_literal(cmd, "LIMIT"); long low, high; - if (Z_TYPE_P(z_off) == IS_STRING) { - low = atol(Z_STRVAL_P(z_off)); + if (Z_TYPE_P(zoff) == IS_STRING) { + low = atol(Z_STRVAL_P(zoff)); } else { - low = Z_LVAL_P(z_off); + low = Z_LVAL_P(zoff); } - if (Z_TYPE_P(z_cnt) == IS_STRING) { - high = atol(Z_STRVAL_P(z_cnt)); + if (Z_TYPE_P(zcnt) == IS_STRING) { + high = atol(Z_STRVAL_P(zcnt)); } else { - high = Z_LVAL_P(z_cnt); + high = Z_LVAL_P(zcnt); } // Add our two LIMIT arguments - add_next_index_long(&z_argv, low); - add_next_index_long(&z_argv, high); + redis_cmd_cat_long(cmd, low); + redis_cmd_cat_long(cmd, high); } } - // Start constructing our command - HashTable *ht_argv = Z_ARRVAL_P(&z_argv); - redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(ht_argv), kw, strlen(kw)); - - // Iterate through our arguments - ZEND_HASH_FOREACH_VAL(ht_argv, z_ele) { - // Args are strings or longs - if (Z_TYPE_P(z_ele) == IS_STRING) { - redis_cmd_append_sstr(&cmdstr,Z_STRVAL_P(z_ele), - Z_STRLEN_P(z_ele)); - } else { - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele)); - } - } ZEND_HASH_FOREACH_END(); - - /* Clean up our arguments array. Note we don't have to free any prefixed - * key as that we didn't duplicate the pointer if we prefixed */ - zval_ptr_dtor_nogc(&z_argv); - - // Push our length and command - *cmd_len = cmdstr.len; - *cmd = cmdstr.c; - - // Success! - return SUCCESS; + return cmd; } /* HDEL */ -int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd *redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *key = NULL; - int i; + RedisCmd *cmd; int argc = 0; zval *args; + int i; ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_STR(key) Z_PARAM_VARIADIC('*', args, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - // Start command construction - redis_cmd_init_sstr(&cmdstr, argc + 1, ZEND_STRL("HDEL")); + cmd = redis_cmd_create_literal(redis_sock, "HDEL"); - // Append key - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, key); - // Iterate through the members we're removing for (i = 0; i < argc; i++) { - redis_cmd_append_sstr_zval(&cmdstr, &args[i], NULL); + redis_cmd_cat_zval_zstr(cmd, &args[i]); } - // Push out values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - // Success! - return SUCCESS; + return cmd; } -/* ZADD */ -int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *zstr, *key = NULL, *exp_type = NULL, *range_type = NULL; zend_bool ch = 0, incr = 0; - smart_string cmdstr = {0}; zval *argv = NULL, *z_opt; int argc = 0, pos = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, -1) Z_PARAM_STR(key) Z_PARAM_VARIADIC('*', argv, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); // Need key, [NX|XX] [LT|GT] [CH] [INCR] score, value, [score, value...] */ if (argc % 2 != 0) { if (Z_TYPE(argv[0]) != IS_ARRAY) { - return FAILURE; + return NULL; } ZEND_HASH_FOREACH_VAL(Z_ARRVAL(argv[0]), z_opt) { if (Z_TYPE_P(z_opt) == IS_STRING) { zstr = Z_STR_P(z_opt); - if (zend_string_equals_literal_ci(zstr, "NX") || zend_string_equals_literal_ci(zstr, "XX")) { + if (zend_string_equals_literal_ci(zstr, "NX") || + zend_string_equals_literal_ci(zstr, "XX")) + { exp_type = Z_STR_P(z_opt); - } else if (zend_string_equals_literal_ci(zstr, "LT") || zend_string_equals_literal_ci(zstr, "GT")) { + } else if (zend_string_equals_literal_ci(zstr, "LT") || + zend_string_equals_literal_ci(zstr, "GT")) + { range_type = Z_STR_P(z_opt); } else if (zend_string_equals_literal_ci(zstr, "CH")) { ch = 1; } else if (zend_string_equals_literal_ci(zstr, "INCR")) { if (argc != 3) { // Only one score-element pair can be specified in this mode. - return FAILURE; + return NULL; } incr = 1; } @@ -4282,106 +3838,99 @@ int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, pos++; } - // Start command construction - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (argc - pos) + !!exp_type + !!range_type + !!ch + !!incr, "ZADD"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "ZADD"); - if (exp_type) redis_cmd_append_sstr_zstr(&cmdstr, exp_type); - if (range_type) redis_cmd_append_sstr_zstr(&cmdstr, range_type); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, ch, "CH"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, incr, "INCR"); + redis_cmd_cat_key_zstr(cmd, key); + + if (exp_type) + redis_cmd_cat_zstr(cmd, exp_type); + if (range_type) + redis_cmd_cat_zstr(cmd, range_type); + + redis_cmd_cat_literal_if(cmd, ch, "CH"); + redis_cmd_cat_literal_if(cmd, incr, "INCR"); // Now the rest of our arguments while (pos < argc) { // Append score and member - if (redis_cmd_append_sstr_score(&cmdstr, &argv[pos]) == FAILURE) { - smart_string_free(&cmdstr); - return FAILURE; + if (redis_cmd_cat_score(cmd, &argv[pos]) == FAILURE) { + redis_cmd_free(cmd); + return NULL; } - redis_cmd_append_sstr_zval(&cmdstr, &argv[pos+1], redis_sock); + redis_cmd_cat_zval(cmd, &argv[pos+1]); pos += 2; } - // Push output values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - *ctx = incr ? PHPREDIS_CTX_PTR : NULL; + if (incr) + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); - return SUCCESS; + return cmd; } -/* OBJECT */ -int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *subcmd = NULL, *key = NULL; - smart_string cmdstr = {0}; + void *ctx = NULL; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(subcmd) Z_PARAM_STR(key) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_string_equals_literal_ci(subcmd, "REFCOUNT") || zend_string_equals_literal_ci(subcmd, "IDLETIME")) { - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(subcmd, "ENCODING")) { - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else { php_error_docref(NULL, E_WARNING, "Invalid subcommand sent to OBJECT"); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "OBJECT"); - redis_cmd_append_sstr_zstr(&cmdstr, subcmd); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "OBJECT"); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + redis_cmd_cat_zstr(cmd, subcmd); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_set_ctx(cmd, ctx); + + return cmd; } -int -redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - zval *z_args, *z_ele; - smart_string cmdstr = {0}; +RedisCmd * +redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *mode = NULL; + zval *args, *z_ele; zend_bool ch = 0; - zend_string *zstr; - char *mode = NULL; + RedisCmd *cmd; int argc, i; // We at least need a key and three values if ((argc = ZEND_NUM_ARGS()) < 4 || (argc % 3 != 1 && argc % 3 != 2)) { zend_wrong_param_count(); - return FAILURE; + return NULL; } - // Make sure we at least have a key, and we can get other args - z_args = ecalloc(argc, sizeof(*z_args)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(4, -1) + Z_PARAM_VARIADIC('*', args, argc) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (argc % 3 == 2) { argc--; - if (Z_TYPE(z_args[argc]) != IS_ARRAY) { + if (Z_TYPE(args[argc]) != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "Invalid options value"); - efree(z_args); - return FAILURE; + return NULL; } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL(z_args[argc]), z_ele) { + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[argc]), z_ele) { ZVAL_DEREF(z_ele); if (Z_TYPE_P(z_ele) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "NX") || zend_string_equals_literal_ci(Z_STR_P(z_ele), "XX")) { - mode = Z_STRVAL_P(z_ele); + mode = Z_STR_P(z_ele); } else if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "CH")) { ch = 1; } @@ -4389,71 +3938,158 @@ redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); } - /* Initialize our command */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc + (mode != NULL) + ch, "GEOADD"); + cmd = redis_cmd_create_literal(redis_sock, "GEOADD"); - /* Append key */ - zstr = zval_get_string(&z_args[0]); - redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); - zend_string_release(zstr); + redis_cmd_cat_key_zval(cmd, &args[0]); - /* Append options */ - if (mode != NULL) { - redis_cmd_append_sstr(&cmdstr, mode, strlen(mode)); - } - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, ch, "CH"); + if (mode != NULL) + redis_cmd_cat_zstr(cmd, mode); + + redis_cmd_cat_literal_if(cmd, ch, "CH"); /* Append members */ for (i = 1; i < argc; ++i) { - redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock); + redis_cmd_cat_zval(cmd, &args[i]); } - // Cleanup arg array - efree(z_args); - - // Push out values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* GEODIST */ -int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *key, *source, *dest, *unit = NULL; - size_t keylen, sourcelen, destlen, unitlen; +RedisCmd * +redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *key, *source, *dest, *unit = NULL; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|s", &key, &keylen, - &source, &sourcelen, &dest, &destlen, &unit, - &unitlen) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STR(key) + Z_PARAM_STR(source) + Z_PARAM_STR(dest) + Z_PARAM_OPTIONAL + Z_PARAM_STR(unit) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - /* Construct command */ - if (unit != NULL) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "GEODIST", "ksss", key, keylen, source, - sourcelen, dest, destlen, unit, unitlen); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "GEODIST", "kss", key, keylen, source, - sourcelen, dest, destlen); - } + cmd = redis_cmd_create_literal(redis_sock, "GEODIST"); - return SUCCESS; + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, source); + redis_cmd_cat_zstr(cmd, dest); + + if (unit) + redis_cmd_cat_zstr(cmd, unit); + + return cmd; } geoStoreType get_georadius_store_type(zend_string *key) { - if (ZSTR_LEN(key) == 5 && !strcasecmp(ZSTR_VAL(key), "store")) { + if (zend_string_equals_literal_ci(key, "store")) { return STORE_COORD; - } else if (ZSTR_LEN(key) == 9 && !strcasecmp(ZSTR_VAL(key), "storedist")) { + } else if (zend_string_equals_literal_ci(key, "storedist")) { return STORE_DIST; } return STORE_NONE; } +static int validate_geosearch_shape_position(zval *position, zval *shape, + geoSearchOptions *opts) +{ + uint32_t nelems; + + memset(opts, 0, sizeof(*opts)); + + if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { + opts->shape_type = GEOSEARCH_SHAPE_RADIUS; + opts->shape.radius = zval_get_double(shape); + opts->shape_argc = 2; + } else if (Z_TYPE_P(shape) == IS_ARRAY) { + nelems = zend_hash_num_elements(Z_ARRVAL_P(shape)); + opts->shape.dimensions = Z_ARRVAL_P(shape); + + if (nelems == 2) { + opts->shape_type = GEOSEARCH_SHAPE_BOX; + opts->shape_argc = 3; + } else if (nelems >= 6 && nelems % 2 == 0) { + opts->shape_type = GEOSEARCH_SHAPE_POLYGON; + opts->position_type = GEOSEARCH_POSITION_NONE; + opts->shape_argc = 2 + nelems; + return 1; + } else { + php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); + return 0; + } + } else { + php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); + return 0; + } + + if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { + opts->position_type = GEOSEARCH_POSITION_MEMBER; + opts->position.member = Z_STR_P(position); + opts->position_argc = 2; + } else if (Z_TYPE_P(position) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) + { + opts->position_type = GEOSEARCH_POSITION_LONLAT; + opts->position.coordinates = Z_ARRVAL_P(position); + opts->position_argc = 3; + } else { + php_error_docref(NULL, E_WARNING, "Invalid position"); + return 0; + } + + return 1; +} + +static void +append_geosearch_position(RedisCmd *cmd, geoSearchOptions *opts) { + zval *z_ele; + + switch (opts->position_type) { + case GEOSEARCH_POSITION_MEMBER: + redis_cmd_cat_literal(cmd, "FROMMEMBER"); + redis_cmd_cat_zstr(cmd, opts->position.member); + break; + case GEOSEARCH_POSITION_LONLAT: + redis_cmd_cat_literal(cmd, "FROMLONLAT"); + ZEND_HASH_FOREACH_VAL(opts->position.coordinates, z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_cat_double(cmd, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + break; + case GEOSEARCH_POSITION_NONE: + break; + } +} + +static void +append_geosearch_shape(RedisCmd *cmd, geoSearchOptions *opts) { + zval *z_ele; + + switch (opts->shape_type) { + case GEOSEARCH_SHAPE_RADIUS: + redis_cmd_cat_literal(cmd, "BYRADIUS"); + redis_cmd_cat_double(cmd, opts->shape.radius); + break; + case GEOSEARCH_SHAPE_BOX: + redis_cmd_cat_literal(cmd, "BYBOX"); + ZEND_HASH_FOREACH_VAL(opts->shape.dimensions, z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_cat_double(cmd, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + break; + case GEOSEARCH_SHAPE_POLYGON: + redis_cmd_cat_literal(cmd, "BYPOLYGON"); + redis_cmd_cat_long(cmd, + zend_hash_num_elements(opts->shape.dimensions) / 2); + ZEND_HASH_FOREACH_VAL(opts->shape.dimensions, z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_cat_double(cmd, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + break; + } +} + /* Helper function to get COUNT and possible ANY flag which is passable to * both GEORADIUS and GEOSEARCH */ static int get_georadius_count_options(zval *optval, geoOptions *opts) { @@ -4545,56 +4181,52 @@ static int get_georadius_opts(HashTable *ht, geoOptions *opts) { } /* Helper to append options to a GEORADIUS or GEORADIUSBYMEMBER command */ -void append_georadius_opts(RedisSock *redis_sock, smart_string *str, short *slot, - geoOptions *opt) +zend_bool append_georadius_opts(RedisCmd *cmd, geoOptions *opt) { - if (opt->withcoord) - REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHCOORD"); - if (opt->withdist) - REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHDIST"); - if (opt->withhash) - REDIS_CMD_APPEND_SSTR_STATIC(str, "WITHHASH"); + zend_bool res = 1; + + redis_cmd_cat_literal_if(cmd, opt->withcoord, "WITHCOORD"); + redis_cmd_cat_literal_if(cmd, opt->withdist, "WITHDIST"); + redis_cmd_cat_literal_if(cmd, opt->withhash, "WITHHASH"); /* Append sort if it's not GEO_NONE */ if (opt->sort == SORT_ASC) { - REDIS_CMD_APPEND_SSTR_STATIC(str, "ASC"); + redis_cmd_cat_literal(cmd, "ASC"); } else if (opt->sort == SORT_DESC) { - REDIS_CMD_APPEND_SSTR_STATIC(str, "DESC"); + redis_cmd_cat_literal(cmd, "DESC"); } /* Append our count if we've got one */ if (opt->count) { - REDIS_CMD_APPEND_SSTR_STATIC(str, "COUNT"); - redis_cmd_append_sstr_long(str, opt->count); - if (opt->any) { - REDIS_CMD_APPEND_SSTR_STATIC(str, "ANY"); - } + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, opt->count); + redis_cmd_cat_literal_if(cmd, opt->any, "ANY"); } /* Append store options if we've got them */ if (opt->store != STORE_NONE && opt->key != NULL) { if (opt->store == STORE_COORD) { - REDIS_CMD_APPEND_SSTR_STATIC(str, "STORE"); + redis_cmd_cat_literal(cmd, "STORE"); } else { - REDIS_CMD_APPEND_SSTR_STATIC(str, "STOREDIST"); + redis_cmd_cat_literal(cmd, "STOREDIST"); } - redis_cmd_append_sstr_key_zstr(str, opt->key, redis_sock, slot); + res = redis_cmd_cat_key_zstr(cmd, opt->key); } + + return res; } /* GEORADIUS / GEORADIUS_RO */ -int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd * +redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { zend_string *key = NULL, *unit = NULL; double lng = 0, lat = 0, radius = 0; - smart_string cmdstr = {0}; HashTable *opts = NULL; geoOptions gopts = {0}; - short store_slot = -1; - uint32_t argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(5, 6) Z_PARAM_STR(key) @@ -4604,177 +4236,115 @@ int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR(unit) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(opts) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* Parse any GEORADIUS options we have */ if (opts != NULL && get_georadius_opts(opts, &gopts) != SUCCESS) - return FAILURE; + return NULL; - /* Increment argc depending on options */ - argc = 5 + gopts.withcoord + gopts.withdist + gopts.withhash + - (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) + - (gopts.store != STORE_NONE ? 2 : 0); - - /* Begin construction of our command */ - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_key_zstr(cmd, key); /* Append required arguments */ - redis_cmd_append_sstr_dbl(&cmdstr, lng); - redis_cmd_append_sstr_dbl(&cmdstr, lat); - redis_cmd_append_sstr_dbl(&cmdstr, radius); - redis_cmd_append_sstr_zstr(&cmdstr, unit); + redis_cmd_cat_double(cmd, lng); + redis_cmd_cat_double(cmd, lat); + redis_cmd_cat_double(cmd, radius); + redis_cmd_cat_zstr(cmd, unit); /* Append optional arguments */ - append_georadius_opts(redis_sock, &cmdstr, slot ? &store_slot : NULL, &gopts); - - /* Free key if it was prefixed */ - if (gopts.key) zend_string_release(gopts.key); - - /* Protect the user from CROSSSLOT if we're in cluster */ - if (slot && gopts.store != STORE_NONE && *slot != store_slot) { - php_error_docref(NULL, E_WARNING, - "Key and STORE[DIST] key must hash to the same slot"); - efree(cmdstr.c); - return FAILURE; + if (!append_georadius_opts(cmd, &gopts)) { + redis_crosslot_warning(); + redis_cmd_free(cmd); + cmd = NULL; } - /* Set slot, command and len, and return */ - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + /* Free key if it was prefixed */ + if (gopts.key) + zend_string_release(gopts.key); - return SUCCESS; + return cmd; } /* GEORADIUSBYMEMBER/GEORADIUSBYMEMBER_RO * key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] */ -int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd * +redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { - char *key, *mem, *unit; - size_t keylen, memlen, unitlen; - short store_slot = 0; - int keyfree, argc = 4; - double radius; + zend_string *key, *mem, *unit; geoOptions gopts = {0}; - zval *opts = NULL; - smart_string cmdstr = {0}; + HashTable *opts = NULL; + double radius; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssds|a", &key, &keylen, - &mem, &memlen, &radius, &unit, &unitlen, &opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(4, 5) + Z_PARAM_STR(key) + Z_PARAM_STR(mem) + Z_PARAM_DOUBLE(radius) + Z_PARAM_STR(unit) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(opts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (opts != NULL) { /* Attempt to parse our options array */ - if (get_georadius_opts(Z_ARRVAL_P(opts), &gopts) == FAILURE) { - return FAILURE; + if (get_georadius_opts(opts, &gopts) == FAILURE) { + return NULL; } } - /* Increment argc based on options */ - argc += gopts.withcoord + gopts.withdist + gopts.withhash + - (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0) + - (gopts.store != STORE_NONE ? 2 : 0); - - /* Begin command construction*/ - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - - /* Prefix our key if we're prefixing and set the slot */ - keyfree = redis_key_prefix(redis_sock, &key, &keylen); - CMD_SET_SLOT(slot, key, keylen); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); /* Append required arguments */ - redis_cmd_append_sstr(&cmdstr, key, keylen); - redis_cmd_append_sstr(&cmdstr, mem, memlen); - redis_cmd_append_sstr_long(&cmdstr, radius); - redis_cmd_append_sstr(&cmdstr, unit, unitlen); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, mem); + redis_cmd_cat_long(cmd, radius); + redis_cmd_cat_zstr(cmd, unit); /* Append options */ - append_georadius_opts(redis_sock, &cmdstr, slot ? &store_slot : NULL, &gopts); - - /* Free key if we prefixed */ - if (keyfree) efree(key); - if (gopts.key) zend_string_release(gopts.key); - - /* Protect the user from CROSSSLOT if we're in cluster */ - if (slot && gopts.store != STORE_NONE && *slot != store_slot) { - php_error_docref(NULL, E_WARNING, - "Key and STORE[DIST] key must hash to the same slot"); - efree(cmdstr.c); - return FAILURE; + if (!append_georadius_opts(cmd, &gopts)) { + redis_crosslot_warning(); + redis_cmd_free(cmd); + cmd = NULL; } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + if (gopts.key) + zend_string_release(gopts.key); - return SUCCESS; + return cmd; } -int -redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - char *key, *unit; - int argc = 0; - size_t keylen, unitlen; - geoOptions gopts = {0}; - smart_string cmdstr = {0}; - zval *position, *shape, *opts = NULL, *z_ele; + zval *position, *shape, *z_ele; + geoSearchOptions sopts = {0}; zend_string *zkey, *zstr; - zend_bool bypolygon = 0; - HashTable *ht; + zend_string *key, *unit; + geoOptions gopts = {0}; + HashTable *opts = NULL; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a", &key, &keylen, - &position, &shape, &unit, &unitlen, - &opts) == FAILURE) - { - return FAILURE; + ZEND_PARSE_PARAMETERS_START(4, 5) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(position) + Z_PARAM_ZVAL(shape) + Z_PARAM_STR(unit) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(opts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); + + if (!validate_geosearch_shape_position(position, shape, &sopts)) { + return NULL; } - if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { - argc += 2; - } else if (Z_TYPE_P(shape) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(shape)) == 2) - { - // BYBOX - argc += 3; - } else if (Z_TYPE_P(shape) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(shape)) >= 6 && - zend_hash_num_elements(Z_ARRVAL_P(shape)) % 2 == 0) - { - // BYPOLYGON N verticies - argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(shape)); - bypolygon = 1; - } else { - php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); - return FAILURE; - } - - if (!bypolygon) { - if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { - argc += 2; - } else if (Z_TYPE_P(position) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) - { - argc += 3; - } else { - php_error_docref(NULL, E_WARNING, "Invalid position"); - return FAILURE; - } - } - - argc += bypolygon ? 1 : 2; - /* Attempt to parse our options array */ if (opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { + ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, z_ele) { ZVAL_DEREF(z_ele); if (zkey != NULL && zend_string_equals_literal_ci(zkey, "COUNT")) { if (get_georadius_count_options(z_ele, &gopts) == FAILURE) { - return FAILURE; + return NULL; } } else if (Z_TYPE_P(z_ele) == IS_STRING) { zstr = Z_STR_P(z_ele); @@ -4793,142 +4363,75 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); } - /* Increment argc based on options */ - argc += gopts.withcoord + gopts.withdist + gopts.withhash - + (gopts.sort != SORT_NONE) + (gopts.count ? 2 + gopts.any : 0); + cmd = redis_cmd_create_literal(redis_sock, "GEOSEARCH"); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCH"); - redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, key); - if (!bypolygon) { - if (Z_TYPE_P(position) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); - } ZEND_HASH_FOREACH_END(); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); - } + append_geosearch_position(cmd, &sopts); + append_geosearch_shape(cmd, &sopts); + + if (sopts.shape_type != GEOSEARCH_SHAPE_POLYGON) { + redis_cmd_cat_zstr(cmd, unit); } - if (Z_TYPE_P(shape) == IS_ARRAY) { - ht = Z_ARRVAL_P(shape); - if (bypolygon) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYPOLYGON"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ht) / 2); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); - } - - ZEND_HASH_FOREACH_VAL(ht, z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); - } ZEND_HASH_FOREACH_END(); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); - } - - if (!bypolygon) { - redis_cmd_append_sstr(&cmdstr, unit, unitlen); - } - - /* Append optional arguments */ - if (gopts.withcoord) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHCOORD"); - if (gopts.withdist) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHDIST"); - if (gopts.withhash) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHHASH"); + redis_cmd_cat_literal_if(cmd, gopts.withcoord, "WITHCOORD"); + redis_cmd_cat_literal_if(cmd, gopts.withdist, "WITHDIST"); + redis_cmd_cat_literal_if(cmd, gopts.withhash, "WITHHASH"); /* Append sort if it's not GEO_NONE */ if (gopts.sort == SORT_ASC) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC"); + redis_cmd_cat_literal(cmd, "ASC"); } else if (gopts.sort == SORT_DESC) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC"); + redis_cmd_cat_literal(cmd, "DESC"); } /* Append our count if we've got one */ if (gopts.count) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, gopts.count); - if (gopts.any) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ANY"); - } + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, gopts.count); + redis_cmd_cat_literal_if(cmd, gopts.any, "ANY"); } if (gopts.withcoord + gopts.withdist + gopts.withhash > 0) { - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int -redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - char *dest, *src, *unit; - size_t destlen, srclen, unitlen; +RedisCmd * +redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zval *position, *shape, *z_ele; + zend_string *dest, *src, *unit; + geoSearchOptions sopts = {0}; + HashTable *opts = NULL; geoOptions gopts = {0}; - smart_string cmdstr = {0}; - zval *position, *shape, *opts = NULL, *z_ele; - zend_bool bypolygon = 0; zend_string *zkey; - HashTable *ht; - short s2 = 0; - int argc = 0; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszzs|a", - &dest, &destlen, &src, &srclen, &position, &shape, - &unit, &unitlen, &opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(5, 6) + Z_PARAM_STR(dest) + Z_PARAM_STR(src) + Z_PARAM_ZVAL(position) + Z_PARAM_ZVAL(shape) + Z_PARAM_STR(unit) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(opts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { - argc += 2; - } else if (Z_TYPE_P(shape) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(shape)) == 2) - { - argc += 3; - } else if (Z_TYPE_P(shape) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(shape)) >= 6 && - zend_hash_num_elements(Z_ARRVAL_P(shape)) % 2 == 0) - { - argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(shape)); - bypolygon = 1; - } else { - php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); - return FAILURE; - } - - argc += bypolygon ? 2 : 3; - - if (!bypolygon) { - if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { - argc += 2; - } else if (Z_TYPE_P(position) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) - { - argc += 3; - } else { - php_error_docref(NULL, E_WARNING, "Invalid position"); - return FAILURE; - } + if (!validate_geosearch_shape_position(position, shape, &sopts)) { + return NULL; } /* Attempt to parse our options array */ if (opts != NULL) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { + ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, z_ele) { ZVAL_DEREF(z_ele); if (zkey != NULL) { if (zend_string_equals_literal_ci(zkey, "COUNT")) { if (Z_TYPE_P(z_ele) != IS_LONG || Z_LVAL_P(z_ele) <= 0) { php_error_docref(NULL, E_WARNING, "COUNT must be an integer > 0!"); - return FAILURE; + return NULL; } gopts.count = Z_LVAL_P(z_ele); } @@ -4945,76 +4448,34 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } - /* Increment argc based on options */ - argc += gopts.withcoord + gopts.withdist + gopts.withhash - + (gopts.sort != SORT_NONE) + (gopts.count ? 2 : 0) - + (gopts.store != STORE_NONE); + cmd = redis_cmd_create_literal(redis_sock, "GEOSEARCHSTORE"); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCHSTORE"); - redis_cmd_append_sstr_key(&cmdstr, dest, destlen, redis_sock, slot); - redis_cmd_append_sstr_key(&cmdstr, src, srclen, redis_sock, slot ? &s2 : NULL); + redis_cmd_cat_key_zstr(cmd, dest); + redis_cmd_try_cat_key_zstr(cmd, src); - if (slot && *slot != s2) { - php_error_docref(NULL, E_WARNING, "All keys must hash to the same slot"); - efree(cmdstr.c); - return FAILURE; - } + append_geosearch_position(cmd, &sopts); + append_geosearch_shape(cmd, &sopts); - if (!bypolygon) { - if (Z_TYPE_P(position) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); - } ZEND_HASH_FOREACH_END(); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); - } - } - - if (Z_TYPE_P(shape) == IS_ARRAY) { - ht = Z_ARRVAL_P(shape); - if (bypolygon) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYPOLYGON"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ht) / 2); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); - } - ZEND_HASH_FOREACH_VAL(ht, z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); - } ZEND_HASH_FOREACH_END(); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); - } - - if (!bypolygon) { - redis_cmd_append_sstr(&cmdstr, unit, unitlen); + if (sopts.shape_type != GEOSEARCH_SHAPE_POLYGON) { + redis_cmd_cat_zstr(cmd, unit); } /* Append sort if it's not GEO_NONE */ if (gopts.sort == SORT_ASC) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ASC"); + redis_cmd_cat_literal(cmd, "ASC"); } else if (gopts.sort == SORT_DESC) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DESC"); + redis_cmd_cat_literal(cmd, "DESC"); } /* Append our count if we've got one */ if (gopts.count) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, gopts.count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, gopts.count); } - if (gopts.store == STORE_DIST) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "STOREDIST"); - } + redis_cmd_cat_literal_if(cmd, gopts.store == STORE_DIST, "STOREDIST"); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* MIGRATE host port destination-db timeout [COPY] [REPLACE] @@ -5026,43 +4487,35 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Starting with Redis version 6.0.0: Added the AUTH2 option. */ -int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; - zend_string *key, *field, *tmp; +RedisCmd * +redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw) { HashTable *fields; - int argc; + zend_string *key; + RedisCmd *cmd; zval *zv; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(fields) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(fields) < 1) { php_error_docref(NULL, E_WARNING, "Must pass at least one field"); - return FAILURE; + return NULL; } - // 3 because FIELDS - argc = 3 + zend_hash_num_elements(fields); - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + redis_cmd_cat_key_zstr(cmd, key); + + redis_cmd_cat_literal(cmd, "FIELDS"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(fields)); ZEND_HASH_FOREACH_VAL(fields, zv) - field = zval_get_tmp_string(zv, &tmp); - redis_cmd_append_sstr_zstr(&cmdstr, field); - zend_tmp_string_release(tmp); + redis_cmd_cat_zval_zstr(cmd, zv); ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } typedef struct redisHGetExOptions { @@ -5114,57 +4567,50 @@ static int get_hgetex_expiry_opts(redisHGetExOptions *dst, zval *zv) { return SUCCESS; } -int redis_hgetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_hgetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { redisHGetExOptions opts = {0}; - smart_string cmdstr = {0}; HashTable *fields, *htctx; zval *zexpiry = NULL; zend_string *key; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(fields) Z_PARAM_OPTIONAL Z_PARAM_ZVAL_OR_NULL(zexpiry); - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(fields) == 0) { php_error_docref(NULL, E_WARNING, "Must pass at least one field"); - return FAILURE; + return NULL; } else if (get_hgetex_expiry_opts(&opts, zexpiry) == FAILURE) { - return FAILURE; + return NULL; } htctx = build_hash_context_ht(fields, hmget_filter); if (htctx == NULL) { php_error_docref(NULL, E_WARNING, "Failed to build context hash table"); - return FAILURE; + return NULL; } - argc = 3 + (opts.exp_type != NULL) + (opts.exp_arg >= 0) + - zend_hash_num_elements(fields); + cmd = redis_cmd_create_literal(redis_sock, "HGETEX"); + redis_cmd_cat_key_zstr(cmd, key); - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HGETEX")); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); if (opts.exp_type) { - redis_cmd_append_sstr_zstr(&cmdstr, opts.exp_type); + redis_cmd_cat_zstr(cmd, opts.exp_type); if (opts.exp_arg >= 0) - redis_cmd_append_sstr_long(&cmdstr, opts.exp_arg); + redis_cmd_cat_long(cmd, opts.exp_arg); } - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(htctx)); - redis_cmd_append_sstr_hash_fields(&cmdstr, htctx); + redis_cmd_cat_literal(cmd, "FIELDS"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(htctx)); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - *ctx = htctx; + redis_cmd_cat_hash_fields(cmd, htctx); + redis_cmd_set_ctx_ex(cmd, htctx, redis_cmd_ctx_hash_dtor); - return SUCCESS; + return cmd; } typedef struct redisHSetExOptions { @@ -5214,113 +4660,99 @@ void get_hsetex_expiry_options(redisHSetExOptions *dst, HashTable *src) { } ZEND_HASH_FOREACH_END(); } -int redis_hsetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - redisHSetExOptions opts = {0}; - smart_string cmdstr = {0}; +RedisCmd *redis_hsetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { HashTable *fields, *expiry = NULL; + redisHSetExOptions opts = {0}; zend_string *key; zend_ulong idx; + RedisCmd *cmd; zval *zv; - int argc; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(key) Z_PARAM_ARRAY_HT(fields) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(expiry); - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(fields) == 0) { php_error_docref(NULL, E_WARNING, "Must pass at least one field"); - return FAILURE; + return NULL; } get_hsetex_expiry_options(&opts, expiry); - argc = 3 + !!opts.set_mode + !!opts.exp_type + (opts.exp_arg >= 0) + - zend_hash_num_elements(fields) * 2; + cmd = redis_cmd_create_literal(redis_sock, "HSETEX"); + redis_cmd_cat_key_zstr(cmd, key); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "HSETEX"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); if (opts.set_mode) - redis_cmd_append_sstr_zstr(&cmdstr, opts.set_mode); + redis_cmd_cat_zstr(cmd, opts.set_mode); if (opts.exp_type) { - redis_cmd_append_sstr_zstr(&cmdstr, opts.exp_type); + redis_cmd_cat_zstr(cmd, opts.exp_type); if (opts.exp_arg >= 0) - redis_cmd_append_sstr_long(&cmdstr, opts.exp_arg); + redis_cmd_cat_long(cmd, opts.exp_arg); } - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + redis_cmd_cat_literal(cmd, "FIELDS"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(fields)); ZEND_HASH_FOREACH_KEY_VAL(fields, idx, key, zv) if (key) { - redis_cmd_append_sstr_zstr(&cmdstr, key); + redis_cmd_cat_zstr(cmd, key); } else { - redis_cmd_append_sstr_long(&cmdstr, idx); + redis_cmd_cat_long(cmd, idx); } - redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); + + redis_cmd_cat_zval(cmd, zv); ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int redis_hgetdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_hgetdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { HashTable *fields, *htctx; - smart_string cmdstr = {0}; zend_string *key; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key); Z_PARAM_ARRAY_HT(fields); - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(fields) == 0) { php_error_docref(NULL, E_WARNING, "Must pass at least one field"); - return FAILURE; + return NULL; } htctx = build_hash_context_ht(fields, hmget_filter); if (htctx == NULL) { php_error_docref(NULL, E_WARNING, "Failed to build context hash table"); - return FAILURE; + return NULL; } - argc = 3 + zend_hash_num_elements(htctx); + cmd = redis_cmd_create_literal(redis_sock, "HGETDEL"); + redis_cmd_cat_key_zstr(cmd, key); - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HGETDEL")); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_cat_literal(cmd, "FIELDS"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(htctx)); + redis_cmd_cat_hash_fields(cmd, htctx); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(htctx)); - redis_cmd_append_sstr_hash_fields(&cmdstr, htctx); + redis_cmd_set_ctx_ex(cmd, htctx, redis_cmd_ctx_hash_dtor); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - *ctx = htctx; - - return SUCCESS; + return cmd; } -int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd * +redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) + { - zend_string *key, *option = NULL, *tmp, *field; - smart_string cmdstr = {0}; + zend_string *key, *option = NULL; HashTable *fields; + RedisCmd *cmd; zend_long ttl; zval *zv; - int argc; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STR(key) @@ -5328,46 +4760,36 @@ int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ARRAY_HT(fields) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_NULL(option) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(fields) < 1) { php_error_docref(NULL, E_WARNING, "Must pass at least one field"); - return FAILURE; + return NULL; } - // 4 because FIELDS - argc = 4 + zend_hash_num_elements(fields) + (option ? 1 : 0); - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_long(&cmdstr, ttl); - if (option) redis_cmd_append_sstr_zstr(&cmdstr, option); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, ttl); + redis_cmd_cat_zstr_if(cmd, option, option); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + redis_cmd_cat_literal(cmd, "FIELDS"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(fields)); ZEND_HASH_FOREACH_VAL(fields, zv) - field = zval_get_tmp_string(zv, &tmp); - redis_cmd_append_sstr_zstr(&cmdstr, field); - zend_tmp_string_release(tmp); + redis_cmd_cat_zval_zstr(cmd, zv); ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* MIGRATE */ -int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - zend_string *host = NULL, *key = NULL, *user = NULL, *pass = NULL; +RedisCmd *redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *host = NULL, *user = NULL, *pass = NULL; zend_long destdb = 0, port = 0, timeout = 0; zval *zkeys = NULL, *zkey, *zauth = NULL; zend_bool copy = 0, replace = 0; - smart_string cmdstr = {0}; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(5, 8) Z_PARAM_STR(host) @@ -5379,93 +4801,90 @@ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_BOOL(copy) Z_PARAM_BOOL(replace) Z_PARAM_ZVAL_OR_NULL(zauth) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* Sanity check on our optional AUTH argument */ if (zauth && redis_extract_auth_info(zauth, &user, &pass) == FAILURE) { - php_error_docref(NULL, E_WARNING, "AUTH must be a string or an array with one or two strings"); + php_error_docref(NULL, E_WARNING, + "AUTH must be a string or an array with one or two strings"); user = pass = NULL; } /* Protect against being passed an array with zero elements */ - if (Z_TYPE_P(zkeys) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(zkeys)) == 0) { + if (Z_TYPE_P(zkeys) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(zkeys)) == 0) + { php_error_docref(NULL, E_WARNING, "Keys array cannot be empty"); - return FAILURE; + return NULL; } - /* host, port, key|"", dest-db, timeout, [copy, replace] [KEYS key1..keyN] */ - argc = 5 + copy + replace + (user||pass ? 1 : 0) + (user != NULL) + (pass != NULL); - if (Z_TYPE_P(zkeys) == IS_ARRAY) { - /* +1 for the "KEYS" argument itself */ - argc += 1 + zend_hash_num_elements(Z_ARRVAL_P(zkeys)); - } + cmd = redis_cmd_create_literal(redis_sock, "MIGRATE"); - /* Initialize MIGRATE command with host and port */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "MIGRATE"); - redis_cmd_append_sstr_zstr(&cmdstr, host); - redis_cmd_append_sstr_long(&cmdstr, port); + redis_cmd_cat_zstr(cmd, host); + redis_cmd_cat_long(cmd, port); /* If passed a keys array the keys come later, otherwise pass the key to * migrate here */ if (Z_TYPE_P(zkeys) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, ""); + redis_cmd_cat_literal(cmd, ""); } else { - key = redis_key_prefix_zval(redis_sock, zkeys); - redis_cmd_append_sstr_zstr(&cmdstr, key); - zend_string_release(key); + if (!redis_cmd_cat_key_zval(cmd, zkeys)) { + redis_crosslot_warning(); + redis_cmd_free(cmd); + if (user) zend_string_release(user); + if (pass) zend_string_release(pass); + return NULL; + } } - redis_cmd_append_sstr_long(&cmdstr, destdb); - redis_cmd_append_sstr_long(&cmdstr, timeout); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, copy, "COPY"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE"); + redis_cmd_cat_long(cmd, destdb); + redis_cmd_cat_long(cmd, timeout); + redis_cmd_cat_literal_if(cmd, copy, "COPY"); + redis_cmd_cat_literal_if(cmd, replace, "REPLACE"); if (user && pass) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AUTH2"); - redis_cmd_append_sstr_zstr(&cmdstr, user); - redis_cmd_append_sstr_zstr(&cmdstr, pass); + redis_cmd_cat_literal(cmd, "AUTH2"); + redis_cmd_cat_zstr(cmd, user); + redis_cmd_cat_zstr(cmd, pass); } else if (pass) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AUTH"); - redis_cmd_append_sstr_zstr(&cmdstr, pass); - } - - /* Append actual keys if we've got a keys array */ - if (Z_TYPE_P(zkeys) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "KEYS"); - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zkeys), zkey) { - key = redis_key_prefix_zval(redis_sock, zkey); - redis_cmd_append_sstr_zstr(&cmdstr, key); - zend_string_release(key); - } ZEND_HASH_FOREACH_END(); + redis_cmd_cat_literal(cmd, "AUTH"); + redis_cmd_cat_zstr(cmd, pass); } if (user) zend_string_release(user); if (pass) zend_string_release(pass); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + /* Append actual keys if we've got a keys array */ + if (Z_TYPE_P(zkeys) == IS_ARRAY) { + redis_cmd_cat_literal(cmd, "KEYS"); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zkeys), zkey) { + redis_cmd_try_cat_key_zval(cmd, zkey); + } ZEND_HASH_FOREACH_END(); + } + + return cmd; } /* A generic passthru function for variadic key commands that take one or more * keys. This is essentially all of them except ones that STORE data. */ -int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw) { return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, - kw, strlen(kw), 0, cmd, cmd_len, slot); + kw, strlen(kw), 0); } -static int -redis_build_client_list_command(smart_string *cmdstr, int argc, zval *z_args) +static RedisCmd * +redis_build_client_list_command(RedisSock *redis_sock, int argc, zval *z_args) { zend_string *zkey; zval *z_ele, *type = NULL, *id = NULL; + RedisCmd *cmd; if (argc > 0) { if (Z_TYPE(z_args[0]) != IS_ARRAY) { - return FAILURE; + return NULL; } ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[0]), zkey, z_ele) { if (zkey != NULL) { @@ -5477,7 +4896,7 @@ redis_build_client_list_command(smart_string *cmdstr, int argc, zval *z_args) !ZVAL_STRICMP_STATIC(z_ele, "replica") && !ZVAL_STRICMP_STATIC(z_ele, "pubsub") )) { - return FAILURE; + return NULL; } type = z_ele; } else if (zend_string_equals_literal_ci(zkey, "id")) { @@ -5485,51 +4904,50 @@ redis_build_client_list_command(smart_string *cmdstr, int argc, zval *z_args) Z_TYPE_P(z_ele) != IS_ARRAY || !zend_hash_num_elements(Z_ARRVAL_P(z_ele)) )) { - return FAILURE; + return NULL; } id = z_ele; } } } ZEND_HASH_FOREACH_END(); } - REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 1 + (type ? 2 : 0) + ( - id ? (Z_TYPE_P(id) == IS_ARRAY ? 1 + zend_hash_num_elements(Z_ARRVAL_P(id)) : 2) : 0 - ), "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "LIST"); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "LIST"); if (type != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TYPE"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(type), Z_STRLEN_P(type)); + redis_cmd_cat_literal(cmd, "TYPE"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(type), Z_STRLEN_P(type)); } if (id != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ID"); + redis_cmd_cat_literal(cmd, "ID"); if (Z_TYPE_P(id) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(id), z_ele) { if (Z_TYPE_P(z_ele) == IS_STRING) { - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + redis_cmd_cat_str(cmd, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { zkey = zval_get_string(z_ele); - redis_cmd_append_sstr(cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); + redis_cmd_cat_zstr(cmd, zkey); zend_string_release(zkey); } } ZEND_HASH_FOREACH_END(); } else { - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(id), Z_STRLEN_P(id)); + redis_cmd_cat_str(cmd, Z_STRVAL_P(id), Z_STRLEN_P(id)); } } - return SUCCESS; + return cmd; } -static int -redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args) +static RedisCmd * +redis_build_client_kill_command(RedisSock *redis_sock, int argc, zval *z_args) { zend_string *zkey; zval *z_ele, *id = NULL, *type = NULL, *address = NULL, *opts = NULL, *user = NULL, *addr = NULL, *laddr = NULL, *skipme = NULL; + RedisCmd *cmd; if (argc > 0) { if (argc > 1) { if (Z_TYPE(z_args[0]) != IS_STRING || Z_TYPE(z_args[1]) != IS_ARRAY) { - return FAILURE; + return NULL; } address = &z_args[0]; opts = &z_args[1]; @@ -5538,14 +4956,14 @@ redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args) } else if (Z_TYPE(z_args[0]) == IS_ARRAY) { opts = &z_args[0]; } else { - return FAILURE; + return NULL; } if (opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (Z_TYPE_P(z_ele) != IS_STRING) { - return FAILURE; + return NULL; } if (zend_string_equals_literal_ci(zkey, "id")) { id = z_ele; @@ -5556,7 +4974,7 @@ redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args) !ZVAL_STRICMP_STATIC(z_ele, "replica") && !ZVAL_STRICMP_STATIC(z_ele, "pubsub") ) { - return FAILURE; + return NULL; } type = z_ele; } else if (zend_string_equals_literal_ci(zkey, "user")) { @@ -5569,7 +4987,7 @@ redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args) if (!ZVAL_STRICMP_STATIC(z_ele, "yes") && !ZVAL_STRICMP_STATIC(z_ele, "no") ) { - return FAILURE; + return NULL; } skipme = z_ele; } @@ -5577,65 +4995,64 @@ redis_build_client_kill_command(smart_string *cmdstr, int argc, zval *z_args) } ZEND_HASH_FOREACH_END(); } } - REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 1 + (address != 0) + (id ? 2 : 0) - + (type ? 2 : 0) + (user ? 2 : 0) + (addr ? 2 : 0) + (laddr ? 2 : 0) - + (skipme ? 2 : 0), "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "KILL"); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "KILL"); if (address != NULL) { - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(address), Z_STRLEN_P(address)); + redis_cmd_cat_str(cmd, Z_STRVAL_P(address), Z_STRLEN_P(address)); } if (id != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ID"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(id), Z_STRLEN_P(id)); + redis_cmd_cat_literal(cmd, "ID"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(id), Z_STRLEN_P(id)); } if (type != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TYPE"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(type), Z_STRLEN_P(type)); + redis_cmd_cat_literal(cmd, "TYPE"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(type), Z_STRLEN_P(type)); } if (user != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "USER"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(user), Z_STRLEN_P(user)); + redis_cmd_cat_literal(cmd, "USER"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(user), Z_STRLEN_P(user)); } if (addr != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ADDR"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(addr), Z_STRLEN_P(addr)); + redis_cmd_cat_literal(cmd, "ADDR"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(addr), Z_STRLEN_P(addr)); } if (laddr != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "LADDR"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(laddr), Z_STRLEN_P(laddr)); + redis_cmd_cat_literal(cmd, "LADDR"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(laddr), Z_STRLEN_P(laddr)); } if (skipme != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "SKIPME"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(skipme), Z_STRLEN_P(skipme)); + redis_cmd_cat_literal(cmd, "SKIPME"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(skipme), Z_STRLEN_P(skipme)); } - return SUCCESS; + return cmd; } -static int -redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args) +static RedisCmd * +redis_build_client_tracking_command(RedisSock *redis_sock, int argc, zval *z_args) { zend_string *zkey; zval *z_ele, *redirect = NULL, *prefix = NULL; zend_bool bcast = 0, optin = 0, optout = 0, noloop = 0; + RedisCmd *cmd; if (argc < 1) { - return FAILURE; + return NULL; } if (argc > 1) { if (Z_TYPE(z_args[1]) != IS_ARRAY) { - return FAILURE; + return NULL; } ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) { if (zkey != NULL) { ZVAL_DEREF(z_ele); if (zend_string_equals_literal_ci(zkey, "redirect")) { if (Z_TYPE_P(z_ele) != IS_STRING) { - return FAILURE; + return NULL; } redirect = z_ele; } else if (zend_string_equals_literal_ci(zkey, "prefix")) { if (Z_TYPE_P(z_ele) != IS_STRING && Z_TYPE_P(z_ele) != IS_ARRAY) { - return FAILURE; + return NULL; } prefix = z_ele; } else if (zend_string_equals_literal_ci(zkey, "bcast")) { @@ -5650,126 +5067,113 @@ redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args } } ZEND_HASH_FOREACH_END(); } - REDIS_CMD_INIT_SSTR_STATIC(cmdstr, 2 + (redirect ? 2 : 0) - + (prefix ? 2 * zend_hash_num_elements(Z_ARRVAL_P(prefix)) : 0) - + bcast + optin + optout + noloop, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "TRACKING"); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "TRACKING"); if (Z_TYPE(z_args[0]) == IS_STRING && ( ZVAL_STRICMP_STATIC(&z_args[0], "on") || ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { - redis_cmd_append_sstr(cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } else if (zend_is_true(&z_args[0])) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ON"); + redis_cmd_cat_literal(cmd, "ON"); } else { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OFF"); + redis_cmd_cat_literal(cmd, "OFF"); } if (redirect != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "REDIRECT"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(redirect), Z_STRLEN_P(redirect)); + redis_cmd_cat_literal(cmd, "REDIRECT"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(redirect), Z_STRLEN_P(redirect)); } if (prefix != NULL) { if (Z_TYPE_P(prefix) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(prefix), z_ele) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PREFIX"); + redis_cmd_cat_literal(cmd, "PREFIX"); if (Z_TYPE_P(z_ele) == IS_STRING) { - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + redis_cmd_cat_str(cmd, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); } else { zkey = zval_get_string(z_ele); - redis_cmd_append_sstr(cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); + redis_cmd_cat_zstr(cmd, zkey); zend_string_release(zkey); } } ZEND_HASH_FOREACH_END(); } else { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PREFIX"); - redis_cmd_append_sstr(cmdstr, Z_STRVAL_P(prefix), Z_STRLEN_P(prefix)); + redis_cmd_cat_literal(cmd, "PREFIX"); + redis_cmd_cat_str(cmd, Z_STRVAL_P(prefix), Z_STRLEN_P(prefix)); } } - if (bcast) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "BCAST"); - } - if (optin) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OPTIN"); - } - if (optout) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OPTOUT"); - } - if (noloop) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "NOLOOP"); - } - return SUCCESS; + redis_cmd_cat_literal_if(cmd, bcast, "BCAST"); + redis_cmd_cat_literal_if(cmd, optin, "OPTIN"); + redis_cmd_cat_literal_if(cmd, optout, "OPTOUT"); + redis_cmd_cat_literal_if(cmd, noloop, "NOLOOP"); + + return cmd; } -int -redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; zend_string *op = NULL; zval *z_args = NULL; + RedisCmd *cmd = NULL; + void *ctx = NULL; int argc = 0; ZEND_PARSE_PARAMETERS_START(1, -1) Z_PARAM_STR(op) Z_PARAM_OPTIONAL Z_PARAM_VARIADIC('*', z_args, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_string_equals_literal_ci(op, "INFO")) { - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "INFO"); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "INFO"); } else if (zend_string_equals_literal_ci(op, "LIST")) { - if (redis_build_client_list_command(&cmdstr, argc, z_args) != 0) { - return FAILURE; - } - *ctx = PHPREDIS_CTX_PTR; + cmd = redis_build_client_list_command(redis_sock, argc, z_args); + ctx = PHPREDIS_CTX_PTR; } else if (zend_string_equals_literal_ci(op, "CACHING")) { if (argc < 1) { - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "CACHING"); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "CACHING"); if (Z_TYPE(z_args[0]) == IS_STRING && ( ZVAL_STRICMP_STATIC(&z_args[0], "yes") || ZVAL_STRICMP_STATIC(&z_args[0], "no") )) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } else if (zend_is_true(&z_args[0])) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "YES"); + redis_cmd_cat_literal(cmd, "YES"); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO"); + redis_cmd_cat_literal(cmd, "NO"); } - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "GETNAME")) { - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GETNAME"); - *ctx = PHPREDIS_CTX_PTR + 3; + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "GETNAME"); + ctx = PHPREDIS_CTX_PTR + 3; } else if (zend_string_equals_literal_ci(op, "GETREDIR") || zend_string_equals_literal_ci(op, "ID")) { - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(op), ZSTR_LEN(op)); - *ctx = PHPREDIS_CTX_PTR + 2; + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_zstr(cmd, op); + ctx = PHPREDIS_CTX_PTR + 2; } else if (zend_string_equals_literal_ci(op, "KILL")) { - if (redis_build_client_kill_command(&cmdstr, argc, z_args) != 0) { - return FAILURE; - } - *ctx = PHPREDIS_CTX_PTR + 1; + cmd = redis_build_client_kill_command(redis_sock, argc, z_args); + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "NO-EVICT")) { if (argc < 1) { - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO-EVICT"); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "NO-EVICT"); if (Z_TYPE(z_args[0]) == IS_STRING && ( ZVAL_STRICMP_STATIC(&z_args[0], "on") || ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } else if (zend_is_true(&z_args[0])) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ON"); + redis_cmd_cat_literal(cmd, "ON"); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "OFF"); + redis_cmd_cat_literal(cmd, "OFF"); } - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "PAUSE")) { if (argc < 1 || Z_TYPE(z_args[0]) != IS_LONG || ( argc > 1 && ( @@ -5779,15 +5183,15 @@ redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) ) )) { - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 3 : 2, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "PAUSE"); - redis_cmd_append_sstr_long(&cmdstr, Z_LVAL(z_args[0])); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "PAUSE"); + redis_cmd_cat_long(cmd, Z_LVAL(z_args[0])); if (argc > 1) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "REPLY")) { if (argc > 0 && ( Z_TYPE(z_args[0]) != IS_STRING || ( @@ -5796,31 +5200,29 @@ redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, !ZVAL_STRICMP_STATIC(&z_args[0], "skip") ) )) { - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 0 ? 2 : 1, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REPLY"); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "REPLY"); if (argc > 0) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); } - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "SETNAME")) { if (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING) { - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SETNAME"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); - *ctx = PHPREDIS_CTX_PTR + 1; + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "SETNAME"); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "TRACKING")) { - if (redis_build_client_tracking_command(&cmdstr, argc, z_args) != 0) { - return FAILURE; - } - *ctx = PHPREDIS_CTX_PTR + 1; + cmd = redis_build_client_tracking_command(redis_sock, argc, z_args); + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "TRACKINGINFO")) { - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TRACKINGINFO"); - *ctx = PHPREDIS_CTX_PTR + 4; + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "TRACKINGINFO"); + ctx = PHPREDIS_CTX_PTR + 4; } else if (zend_string_equals_literal_ci(op, "UNBLOCK")) { if (argc < 1 || Z_TYPE(z_args[0]) != IS_STRING || ( argc > 1 && ( @@ -5830,96 +5232,92 @@ redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) ) )) { - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc > 1 ? 3 : 2, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNBLOCK"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "UNBLOCK"); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); if (argc > 1) { - redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); + redis_cmd_cat_str(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } - *ctx = PHPREDIS_CTX_PTR + 2; + ctx = PHPREDIS_CTX_PTR + 2; } else if (zend_string_equals_literal_ci(op, "UNPAUSE")) { - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "CLIENT"); - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "UNPAUSE"); - *ctx = PHPREDIS_CTX_PTR + 1; + cmd = redis_cmd_create_literal(redis_sock, "CLIENT"); + redis_cmd_cat_literal(cmd, "UNPAUSE"); + ctx = PHPREDIS_CTX_PTR + 1; } else { - return FAILURE; + return NULL; } - // Push out values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + if (cmd == NULL) + return NULL; - return SUCCESS; + redis_cmd_set_ctx(cmd, ctx); + + return cmd; } /* COMMAND */ -int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; zend_string *op = NULL, *zstr; zval *z_args = NULL; + RedisCmd *cmd; + void *ctx = NULL; int i, argc = 0; ZEND_PARSE_PARAMETERS_START(0, -1) Z_PARAM_OPTIONAL Z_PARAM_STR(op) Z_PARAM_VARIADIC('*', z_args, argc) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (op == NULL) { - *ctx = NULL; + ctx = NULL; argc = 0; } else if (zend_string_equals_literal_ci(op, "COUNT")) { - *ctx = PHPREDIS_CTX_PTR; + ctx = PHPREDIS_CTX_PTR; argc = 0; } else if (zend_string_equals_literal_ci(op, "DOCS") || zend_string_equals_literal_ci(op, "INFO") ) { - *ctx = NULL; + ctx = NULL; } else if (zend_string_equals_literal_ci(op, "GETKEYS") || zend_string_equals_literal_ci(op, "LIST") ) { - *ctx = PHPREDIS_CTX_PTR + 1; + ctx = PHPREDIS_CTX_PTR + 1; } else if (zend_string_equals_literal_ci(op, "GETKEYSANDFLAGS")) { - *ctx = PHPREDIS_CTX_PTR + 2; + ctx = PHPREDIS_CTX_PTR + 2; } else { php_error_docref(NULL, E_WARNING, "Unknown COMMAND operation '%s'", ZSTR_VAL(op)); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, !!op + argc, "COMMAND"); - if (op) redis_cmd_append_sstr_zstr(&cmdstr, op); + cmd = redis_cmd_create_literal(redis_sock, "COMMAND"); + if (op) redis_cmd_cat_zstr(cmd, op); for (i = 0; i < argc; ++i) { zstr = zval_get_string(&z_args[i]); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + redis_cmd_cat_zstr(cmd, zstr); zend_string_release(zstr); } - // Push out values - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - /* Any slot will do */ - CMD_RAND_SLOT(slot); + redis_cmd_randslot(cmd); + redis_cmd_set_ctx(cmd, ctx); - return SUCCESS; + return cmd; } -int -redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd * +redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *src = NULL, *dst = NULL; - smart_string cmdstr = {0}; HashTable *opts = NULL; zend_bool replace = 0; zend_string *zkey; zend_long db = -1; - short slot2; + RedisCmd *cmd; zval *zv; ZEND_PARSE_PARAMETERS_START(2, 3) @@ -5927,7 +5325,7 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR(dst) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(opts) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, zv) { @@ -5943,30 +5341,22 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); } - if (slot && db != -1) { + if (redis_sock && redis_sock->type == REDIS_SOCK_CLUSTER && db != -1) { php_error_docref(NULL, E_WARNING, "Can't copy to a specific DB in cluster mode"); - return FAILURE; + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (db > -1 ? 2 : 0) + replace, "COPY"); - redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot); - redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL); - - if (slot && *slot != slot2) { - php_error_docref(NULL, E_WARNING, "Keys must hash to the same slot!"); - efree(cmdstr.c); - return FAILURE; - } + cmd = redis_cmd_create_literal(redis_sock, "COPY"); + redis_cmd_cat_key_zstr(cmd, src); + redis_cmd_try_cat_key_zstr(cmd, dst); if (db > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DB"); - redis_cmd_append_sstr_long(&cmdstr, db); + redis_cmd_cat_literal(cmd, "DB"); + redis_cmd_cat_long(cmd, db); } - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE"); + redis_cmd_cat_literal_if(cmd, replace, "REPLACE"); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } static xdelExMode zstr_to_xdelex_mode(zend_string *s) { @@ -5984,24 +5374,22 @@ static xdelExMode zstr_to_xdelex_mode(zend_string *s) { return REDIS_XDELEX_NONE; } -void redis_cmd_append_delex_mode(smart_string *cmdstr, xdelExMode mode) { +void redis_cmd_cat_delex_mode(RedisCmd *cmd, xdelExMode mode) { if (mode == REDIS_XDELEX_KEEPREF) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "KEEPREF"); + redis_cmd_cat_literal(cmd, "KEEPREF"); } else if (mode == REDIS_XDELEX_DELREF) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "DELREF"); + redis_cmd_cat_literal(cmd, "DELREF"); } else if (mode == REDIS_XDELEX_ACKED) { - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ACKED"); + redis_cmd_cat_literal(cmd, "ACKED"); } } -int redis_xdelex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd * +redis_xdelex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { xdelExMode mode = REDIS_XDELEX_NONE; zend_string *key, *mstr = NULL; - smart_string cmdstr = {0}; HashTable *ids; - int argc = 3; + RedisCmd *cmd; zval *id; ZEND_PARSE_PARAMETERS_START(2, 3) @@ -6009,42 +5397,37 @@ int redis_xdelex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ARRAY_HT(ids) Z_PARAM_OPTIONAL Z_PARAM_STR(mstr) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(ids) == 0) { php_error_docref(NULL, E_WARNING, "At least one ID must be specified"); - return FAILURE; + return NULL; } mode = zstr_to_xdelex_mode(mstr); - argc += (mode != REDIS_XDELEX_NONE) + zend_hash_num_elements(ids); - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("XDELEX")); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "XDELEX"); + redis_cmd_cat_key_zstr(cmd, key); - redis_cmd_append_delex_mode(&cmdstr, mode); + redis_cmd_cat_delex_mode(cmd, mode); - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("IDS")); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ids)); + redis_cmd_cat_literal(cmd, "IDS"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(ids)); ZEND_HASH_FOREACH_VAL(ids, id) { - redis_cmd_append_sstr_zval(&cmdstr, id, NULL); + redis_cmd_cat_zval_zstr(cmd, id); } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } // XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] -int redis_xackdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd * +redis_xackdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *key, *group, *mode = NULL; xdelExMode dtype = REDIS_XDELEX_NONE; - smart_string cmdstr = {0}; HashTable *ids; - int argc; + RedisCmd *cmd; zval *id; ZEND_PARSE_PARAMETERS_START(3, 4) @@ -6053,62 +5436,56 @@ int redis_xackdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ARRAY_HT(ids) Z_PARAM_OPTIONAL Z_PARAM_STR(mode) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(ids) == 0) { php_error_docref(NULL, E_WARNING, "At least one ID must be specified"); - return FAILURE; + return NULL; } dtype = zstr_to_xdelex_mode(mode); - argc = 4 + (dtype != REDIS_XDELEX_NONE) + zend_hash_num_elements(ids); - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("XACKDEL")); + cmd = redis_cmd_create_literal(redis_sock, "XACKDEL"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zstr(&cmdstr, group); - redis_cmd_append_delex_mode(&cmdstr, dtype); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, group); + redis_cmd_cat_delex_mode(cmd, dtype); - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("IDS")); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ids)); + redis_cmd_cat_literal(cmd, "IDS"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(ids)); ZEND_HASH_FOREACH_VAL(ids, id) { - redis_cmd_append_sstr_zval(&cmdstr, id, NULL); + redis_cmd_cat_zval_zstr(cmd, id); } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* XADD */ -int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; - zend_string *arrkey; - zval *z_fields, *value; +RedisCmd *redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *key, *id, *arrkey; zend_long maxlen = 0; zend_bool approx = 0, nomkstream = 0; zend_ulong idx; - HashTable *ht_fields; - int fcount, argc; - char *key, *id; - size_t keylen, idlen; + HashTable *fields; + RedisCmd *cmd; + zval *value; + int fcount; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|lbb", &key, &keylen, - &id, &idlen, &z_fields, &maxlen, &approx, - &nomkstream) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 6) + Z_PARAM_STR(key) + Z_PARAM_STR(id) + Z_PARAM_ARRAY_HT(fields) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(maxlen) + Z_PARAM_BOOL(approx) + Z_PARAM_BOOL(nomkstream) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* At least one field and string are required */ - ht_fields = Z_ARRVAL_P(z_fields); - if ((fcount = zend_hash_num_elements(ht_fields)) == 0) { - return FAILURE; + if ((fcount = zend_hash_num_elements(fields)) == 0) { + return NULL; } if (maxlen < 0 || (maxlen == 0 && approx != 0)) { @@ -6116,48 +5493,36 @@ int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, "Warning: Invalid MAXLEN argument or approximate flag"); } - - /* Calculate argc for XADD. It's a bit complex because we've got - * an optional MAXLEN argument which can either take the form MAXLEN N - * or MAXLEN ~ N */ - argc = 2 + nomkstream + (fcount * 2) + (maxlen > 0 ? (approx ? 3 : 2) : 0); - /* XADD key ID field string [field string ...] */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XADD"); - redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "XADD"); + redis_cmd_cat_key_zstr(cmd, key); - if (nomkstream) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NOMKSTREAM"); - } + redis_cmd_cat_literal_if(cmd, nomkstream, "NOMKSTREAM"); /* Now append our MAXLEN bits if we've got them */ if (maxlen > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN"); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, approx, "~"); - redis_cmd_append_sstr_long(&cmdstr, maxlen); + redis_cmd_cat_literal(cmd, "MAXLEN"); + redis_cmd_cat_literal_if(cmd, approx, "~"); + redis_cmd_cat_long(cmd, maxlen); } /* Now append ID and field(s) */ - redis_cmd_append_sstr(&cmdstr, id, idlen); - ZEND_HASH_FOREACH_KEY_VAL(ht_fields, idx, arrkey, value) { - redis_cmd_append_sstr_arrkey(&cmdstr, arrkey, idx); - redis_cmd_append_sstr_zval(&cmdstr, value, redis_sock); + redis_cmd_cat_zstr(cmd, id); + ZEND_HASH_FOREACH_KEY_VAL(fields, idx, arrkey, value) { + redis_cmd_cat_arrkey(cmd, arrkey, idx); + redis_cmd_cat_zval(cmd, value); } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } // XPENDING key group [start end count [consumer] [idle]] -int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *key = NULL, *group = NULL, *start = NULL, *end = NULL, *consumer = NULL; zend_long count = -1, idle = 0; - smart_string cmdstr = {0}; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 7) Z_PARAM_STR(key) @@ -6168,304 +5533,238 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_LONG(count) Z_PARAM_STR_OR_NULL(consumer) Z_PARAM_LONG(idle) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* If we've been passed a start argument, we also need end and count */ if (start != NULL && (end == NULL || count < 0)) { php_error_docref(NULL, E_WARNING, "'$start' must be accompanied by '$end' and '$count' arguments"); - return FAILURE; + return NULL; } - /* Calculate argc. It's either 2, 5, 6 or 7 */ - argc = 2 + (start != NULL ? 3 + (consumer != NULL) + (idle != 0) : 0); - /* Construct command and add required arguments */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XPENDING"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zstr(&cmdstr, group); + cmd = redis_cmd_create_literal(redis_sock, "XPENDING"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, group); /* Add optional argumentst */ if (start) { if (idle != 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "IDLE"); - redis_cmd_append_sstr_long(&cmdstr, (long)idle); + redis_cmd_cat_literal(cmd, "IDLE"); + redis_cmd_cat_long(cmd, idle); } - redis_cmd_append_sstr_zstr(&cmdstr, start); - redis_cmd_append_sstr_zstr(&cmdstr, end); - redis_cmd_append_sstr_long(&cmdstr, (long)count); + redis_cmd_cat_zstr(cmd, start); + redis_cmd_cat_zstr(cmd, end); + redis_cmd_cat_long(cmd, count); /* Finally add consumer if we have it */ - if (consumer) redis_cmd_append_sstr_zstr(&cmdstr, consumer); + if (consumer) redis_cmd_cat_zstr(cmd, consumer); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* X[REV]RANGE key start end [COUNT count] */ -static int +static RedisCmd * redis_xrange_generic_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, zend_bool have_count_literal, char **cmd, - int *cmd_len, short *slot, void **ctx) + char *kw, zend_bool have_count_literal) { - smart_string cmdstr = {0}; - char *key, *start, *end; - size_t keylen, startlen, endlen; + zend_string *key, *start, *end; zend_long count = -1; - int argc; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &keylen, - &start, &startlen, &end, &endlen, &count) - == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS|l", &key, &start, &end, + &count) == FAILURE) { - return FAILURE; + return NULL; } - argc = 3 + ((have_count_literal ? 2 : 1) * (count > -1)); - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); - redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); - redis_cmd_append_sstr(&cmdstr, start, startlen); - redis_cmd_append_sstr(&cmdstr, end, endlen); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, start); + redis_cmd_cat_zstr(cmd, end); if (count > -1) { - if (have_count_literal) - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT")); - redis_cmd_append_sstr_long(&cmdstr, count); + redis_cmd_cat_literal_if(cmd, have_count_literal, "COUNT"); + redis_cmd_cat_long(cmd, count); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } -int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { return redis_xrange_generic_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, kw, 1, cmd, cmd_len, slot, ctx); + redis_sock, kw, 1); } -int redis_vrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_vrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { return redis_xrange_generic_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, kw, 0, cmd, cmd_len, slot, ctx); + redis_sock, kw, 0); } /* Helper function to take an associative array and append the Redis * STREAMS stream [stream...] id [id ...] arguments to a command string. */ -static int -append_stream_args(smart_string *cmdstr, HashTable *ht, RedisSock *redis_sock, - short *slot) -{ - char *kptr, kbuf[40]; - int klen, i, pos = 0; - zend_string *key, *idstr; - short oldslot = -1; - zval **id; +static zend_bool redis_cmd_cat_stream_args(RedisCmd *cmd, HashTable *ht) { + zend_string *key; zend_ulong idx; + zval *zid; /* Append STREAM qualifier */ - REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "STREAMS"); - - /* Allocate memory to keep IDs */ - id = emalloc(sizeof(*id) * zend_hash_num_elements(ht)); + redis_cmd_cat_literal(cmd, "STREAMS"); /* Iterate over our stream => id array appending streams and retaining each * value for final arguments */ - ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, id[pos++]) { - if (key) { - klen = ZSTR_LEN(key); - kptr = ZSTR_VAL(key); - } else { - klen = snprintf(kbuf, sizeof(kbuf), "%ld", (long)idx); - kptr = (char*)kbuf; - } - - /* Append stream key */ - redis_cmd_append_sstr_key(cmdstr, kptr, klen, redis_sock, slot); - - /* Protect the user against CROSSSLOT to avoid confusion */ - if (slot) { - if (oldslot != -1 && *slot != oldslot) { - php_error_docref(NULL, E_WARNING, - "Warning, not all keys hash to the same slot!"); - efree(id); - return FAILURE; - } - oldslot = *slot; + ZEND_HASH_FOREACH_KEY_VAL(ht, idx, key, zid) { + if (!redis_cmd_cat_key_arrkey(cmd, key, idx)) { + redis_crosslot_warning(); + return 0; } } ZEND_HASH_FOREACH_END(); - /* Add our IDs */ - for (i = 0; i < pos; i++) { - idstr = zval_get_string(id[i]); - redis_cmd_append_sstr(cmdstr, ZSTR_VAL(idstr), ZSTR_LEN(idstr)); - zend_string_release(idstr); - } + ZEND_HASH_FOREACH_VAL(ht, zid) { + redis_cmd_cat_zval_zstr(cmd, zid); + } ZEND_HASH_FOREACH_END(); - /* Clean up ID container array */ - efree(id); - - return 0; + return 1; } /* XREAD [COUNT count] [BLOCK ms] STREAMS key [key ...] id [id ...] */ -int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd *redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long count = -1, block = -1; - zval *z_streams; - int argc, scount; - HashTable *kt; + HashTable *streams; + RedisCmd *cmd; + int scount; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|ll", &z_streams, - &count, &block) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_ARRAY_HT(streams) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(count) + Z_PARAM_LONG(block) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* At least one stream and ID is required */ - kt = Z_ARRVAL_P(z_streams); - if ((scount = zend_hash_num_elements(kt)) < 1) { - return FAILURE; + if ((scount = zend_hash_num_elements(streams)) < 1) { + return NULL; } /* Calculate argc and start constructing command */ - argc = 1 + (2 * scount) + (2 * (count > -1)) + (2 * (block > -1)); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XREAD"); + cmd = redis_cmd_create_literal(redis_sock, "XREAD"); /* Append COUNT if we have it */ if (count > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); } /* Append BLOCK if we have it */ if (block > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BLOCK"); - redis_cmd_append_sstr_long(&cmdstr, block); + redis_cmd_cat_literal(cmd, "BLOCK"); + redis_cmd_cat_long(cmd, block); } /* Append final STREAM key [key ...] id [id ...] arguments */ - if (append_stream_args(&cmdstr, kt, redis_sock, slot) < 0) { - efree(cmdstr.c); - return FAILURE; + if (!redis_cmd_cat_stream_args(cmd, streams)) { + redis_cmd_free(cmd); + return NULL; } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* XREADGROUP GROUP group consumer [COUNT count] [BLOCK ms] * STREAMS key [key ...] id [id ...] */ -int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; - zval *z_streams; - HashTable *kt; - char *group, *consumer; - size_t grouplen, consumerlen; - int scount, argc; +RedisCmd *redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_bool count_is_null = 1, block_is_null = 1; + zend_string *group, *consumer; zend_long count, block; - zend_bool no_count = 1, no_block = 1; + HashTable *streams; + RedisCmd *cmd; + int scount; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|l!l!", &group, - &grouplen, &consumer, &consumerlen, &z_streams, - &count, &no_count, &block, &no_block) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 5) + Z_PARAM_STR(group) + Z_PARAM_STR(consumer) + Z_PARAM_ARRAY_HT(streams) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(count, count_is_null) + Z_PARAM_LONG_OR_NULL(block, block_is_null) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* Negative COUNT or BLOCK is illegal so abort immediately */ - if ((!no_count && count < 0) || (!no_block && block < 0)) { + if ((!count_is_null && count < 0) || (!block_is_null && block < 0)) { php_error_docref(NULL, E_WARNING, "Negative values for COUNT or BLOCK are illegal."); - return FAILURE; + return NULL; } /* Redis requires at least one stream */ - kt = Z_ARRVAL_P(z_streams); - if ((scount = zend_hash_num_elements(kt)) < 1) { - return FAILURE; + if ((scount = zend_hash_num_elements(streams)) < 1) { + return NULL; } /* Calculate argc and start constructing commands */ - argc = 4 + (2 * scount) + (2 * !no_count) + (2 * !no_block); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XREADGROUP"); + cmd = redis_cmd_create_literal(redis_sock, "XREADGROUP"); /* Group and consumer */ - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GROUP"); - redis_cmd_append_sstr(&cmdstr, group, grouplen); - redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); + redis_cmd_cat_literal(cmd, "GROUP"); + redis_cmd_cat_zstr(cmd, group); + redis_cmd_cat_zstr(cmd, consumer); /* Append COUNT if we have it */ - if (!no_count) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, count); + if (!count_is_null) { + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); } /* Append BLOCK argument if we have it */ - if (!no_block) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BLOCK"); - redis_cmd_append_sstr_long(&cmdstr, block); + if (!block_is_null) { + redis_cmd_cat_literal(cmd, "BLOCK"); + redis_cmd_cat_long(cmd, block); } /* Finally append stream and id args */ - if (append_stream_args(&cmdstr, kt, redis_sock, slot) < 0) { - efree(cmdstr.c); - return FAILURE; + if (!redis_cmd_cat_stream_args(cmd, streams)) { + redis_cmd_free(cmd); + return NULL; } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* XACK key group id [id ...] */ -int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; - char *key, *group; - size_t keylen, grouplen; - zend_string *idstr; + zend_string *key, *group; zval *z_ids, *z_id; HashTable *ht_ids; + RedisCmd *cmd; int idcount; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa", &key, &keylen, - &group, &grouplen, &z_ids) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSa", &key, &group, &z_ids) + == FAILURE) { - return FAILURE; + return NULL; } ht_ids = Z_ARRVAL_P(z_ids); if ((idcount = zend_hash_num_elements(ht_ids)) < 1) { - return FAILURE; + return NULL; } /* Create command and add initial arguments */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + idcount, "XACK"); - redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); - redis_cmd_append_sstr(&cmdstr, group, grouplen); + cmd = redis_cmd_create_literal(redis_sock, "XACK"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, group); /* Append IDs */ ZEND_HASH_FOREACH_VAL(ht_ids, z_id) { - idstr = zval_get_string(z_id); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(idstr), ZSTR_LEN(idstr)); - zend_string_release(idstr); + redis_cmd_cat_zval_zstr(cmd, z_id); } ZEND_HASH_FOREACH_END(); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* XCLAIM options container */ @@ -6522,183 +5821,145 @@ static int64_t get_xclaim_i64_arg(const char *key, zval *zv) { } /* Helper to extract XCLAIM options */ -static void get_xclaim_options(zval *z_arr, xclaimOptions *opt) { +static void get_xclaim_options(xclaimOptions *dst, HashTable *ht) { zend_string *zkey; - HashTable *ht; zval *zv; /* Initialize options array to sane defaults */ - memset(opt, 0, sizeof(*opt)); - opt->retrycount = -1; - opt->idle.time = -1; + memset(dst, 0, sizeof(*dst)); + dst->retrycount = -1; + dst->idle.time = -1; /* Early return if we don't have any options */ - if (z_arr == NULL) + if (ht == NULL) return; /* Iterate over our options array */ - ht = Z_ARRVAL_P(z_arr); ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, zv) { if (zkey) { if (zend_string_equals_literal_ci(zkey, "TIME")) { - opt->idle.type = "TIME"; - opt->idle.time = get_xclaim_i64_arg("TIME", zv); + dst->idle.type = "TIME"; + dst->idle.time = get_xclaim_i64_arg("TIME", zv); } else if (zend_string_equals_literal_ci(zkey, "IDLE")) { - opt->idle.type = "IDLE"; - opt->idle.time = get_xclaim_i64_arg("IDLE", zv); + dst->idle.type = "IDLE"; + dst->idle.time = get_xclaim_i64_arg("IDLE", zv); } else if (zend_string_equals_literal_ci(zkey, "RETRYCOUNT")) { - opt->retrycount = zval_get_long(zv); + dst->retrycount = zval_get_long(zv); } } else if (Z_TYPE_P(zv) == IS_STRING) { if (zend_string_equals_literal_ci(Z_STR_P(zv), "FORCE")) { - opt->force = 1; + dst->force = 1; } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "JUSTID")) { - opt->justid = 1; + dst->justid = 1; } } } ZEND_HASH_FOREACH_END(); } -/* Count argc for any options we may have */ -static int xclaim_options_argc(xclaimOptions *opt) { - int argc = 0; - - if (opt->idle.type != NULL && opt->idle.time != -1) - argc += 2; - if (opt->retrycount != -1) - argc += 2; - if (opt->force) - argc++; - if (opt->justid) - argc++; - - return argc; -} - /* Append XCLAIM options */ -static void append_xclaim_options(smart_string *cmd, xclaimOptions *opt) { +static void redis_cmd_cat_xclaim_options(RedisCmd *cmd, xclaimOptions *opt) { /* IDLE/TIME long */ if (opt->idle.type != NULL && opt->idle.time != -1) { - redis_cmd_append_sstr(cmd, opt->idle.type, strlen(opt->idle.type)); - redis_cmd_append_sstr_i64(cmd, opt->idle.time); + redis_cmd_cat_str(cmd, opt->idle.type, strlen(opt->idle.type)); + redis_cmd_cat_long(cmd, opt->idle.time); } /* RETRYCOUNT */ if (opt->retrycount != -1) { - REDIS_CMD_APPEND_SSTR_STATIC(cmd, "RETRYCOUNT"); - redis_cmd_append_sstr_long(cmd, opt->retrycount); + redis_cmd_cat_literal(cmd, "RETRYCOUNT"); + redis_cmd_cat_long(cmd, opt->retrycount); } /* FORCE and JUSTID */ - if (opt->force) - REDIS_CMD_APPEND_SSTR_STATIC(cmd, "FORCE"); - if (opt->justid) - REDIS_CMD_APPEND_SSTR_STATIC(cmd, "JUSTID"); + redis_cmd_cat_literal_if(cmd, opt->force, "FORCE"); + redis_cmd_cat_literal_if(cmd, opt->justid, "JUSTID"); } -int -redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; - char *key, *group, *consumer, *start; - size_t keylen, grouplen, consumerlen, startlen; +RedisCmd * +redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { + zend_string *key, *group, *consumer, *start; zend_long min_idle, count = -1; zend_bool justid = 0; - int argc; + RedisCmd *cmd; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssls|lb", &key, &keylen, - &group, &grouplen, &consumer, &consumerlen, - &min_idle, &start, &startlen, &count, &justid - ) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(5, 7) + Z_PARAM_STR(key) + Z_PARAM_STR(group) + Z_PARAM_STR(consumer) + Z_PARAM_LONG(min_idle) + Z_PARAM_STR(start) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(count) + Z_PARAM_BOOL(justid) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - argc = 5 + (count > 0 ? 2 : 0) + justid; - - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XAUTOCLAIM"); - redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); - redis_cmd_append_sstr(&cmdstr, group, grouplen); - redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); - redis_cmd_append_sstr_long(&cmdstr, min_idle); - redis_cmd_append_sstr(&cmdstr, start, startlen); + cmd = redis_cmd_create_literal(redis_sock, "XAUTOCLAIM"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, group); + redis_cmd_cat_zstr(cmd, consumer); + redis_cmd_cat_long(cmd, min_idle); + redis_cmd_cat_zstr(cmd, start); if (count > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); } - if (justid) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "JUSTID"); - } + redis_cmd_cat_literal_if(cmd, justid, "JUSTID"); - // Set the context to distinguish XCLAIM from XAUTOCLAIM which - // have slightly different reply structures. - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* XCLAIM [IDLE ] [TIME ] [RETRYCOUNT ] [FORCE] [JUSTID] */ -int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - smart_string cmdstr = {0}; - char *key, *group, *consumer; - size_t keylen, grouplen, consumerlen; + zend_string *key, *group, *consumer; + HashTable *ids, *htopts = NULL; zend_long min_idle; - int argc, id_count; - zval *z_ids, *z_id, *z_opts = NULL; - zend_string *zstr; - HashTable *ht_ids; xclaimOptions opts; + RedisCmd *cmd; + int id_count; + zval *zv; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssla|a", &key, &keylen, - &group, &grouplen, &consumer, &consumerlen, &min_idle, - &z_ids, &z_opts) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(5, 6) + Z_PARAM_STR(key) + Z_PARAM_STR(group) + Z_PARAM_STR(consumer) + Z_PARAM_LONG(min_idle) + Z_PARAM_ARRAY_HT(ids) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(htopts) + ZEND_PARSE_PARAMETERS_END_EX(return NULL); /* At least one id is required */ - ht_ids = Z_ARRVAL_P(z_ids); - if ((id_count = zend_hash_num_elements(ht_ids)) < 1) { - return FAILURE; + if ((id_count = zend_hash_num_elements(ids)) < 1) { + return NULL; } /* Extract options array if we've got them */ - get_xclaim_options(z_opts, &opts); - - /* Now we have enough information to calculate argc */ - argc = 4 + id_count + xclaim_options_argc(&opts); + get_xclaim_options(&opts, htopts); /* Start constructing our command */ - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XCLAIM"); - redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); - redis_cmd_append_sstr(&cmdstr, group, grouplen); - redis_cmd_append_sstr(&cmdstr, consumer, consumerlen); - redis_cmd_append_sstr_long(&cmdstr, min_idle); + cmd = redis_cmd_create_literal(redis_sock, "XCLAIM"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, group); + redis_cmd_cat_zstr(cmd, consumer); + redis_cmd_cat_long(cmd, min_idle); /* Add IDs */ - ZEND_HASH_FOREACH_VAL(ht_ids, z_id) { - zstr = zval_get_string(z_id); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); + ZEND_HASH_FOREACH_VAL(ids, zv) { + redis_cmd_cat_zval_zstr(cmd, zv); } ZEND_HASH_FOREACH_END(); /* Finally add our options */ - append_xclaim_options(&cmdstr, &opts); + redis_cmd_cat_xclaim_options(cmd, &opts); - /* Success */ - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } /* XGROUP HELP @@ -6708,14 +5969,13 @@ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, * XGROUP DELCONSUMER key group consumer * XGROUP DESTROY key group */ -int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +RedisCmd *redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *op = NULL, *key = NULL, *group = NULL, *id_or_consumer = NULL; int nargs, is_create = 0, is_setid = 0; zend_long entries_read = -2; - smart_string cmdstr = {0}; zend_bool mkstream = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 6) Z_PARAM_STR(op) @@ -6725,7 +5985,7 @@ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR(id_or_consumer) Z_PARAM_BOOL(mkstream) Z_PARAM_LONG(entries_read) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_string_equals_literal_ci(op, "HELP")) { nargs = 0; @@ -6739,47 +5999,42 @@ int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, nargs = 2; } else { php_error_docref(NULL, E_WARNING, "Unknown XGROUP operation '%s'", ZSTR_VAL(op)); - return FAILURE; + return NULL; } if (ZEND_NUM_ARGS() < nargs) { php_error_docref(NULL, E_WARNING, "Operation '%s' requires %d arguments", ZSTR_VAL(op), nargs); - return FAILURE; + return NULL; } mkstream &= is_create; if (!(is_create || is_setid)) entries_read = -2; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + nargs + !!mkstream + (entries_read != -2 ? 2 : 0), "XGROUP"); - redis_cmd_append_sstr_zstr(&cmdstr, op); + cmd = redis_cmd_create_literal(redis_sock, "XGROUP"); + redis_cmd_cat_zstr(cmd, op); - if (nargs-- > 0) redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, group); - if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, id_or_consumer); + if (nargs-- > 0) redis_cmd_cat_key_zstr(cmd, key); + if (nargs-- > 0) redis_cmd_cat_zstr(cmd, group); + if (nargs-- > 0) redis_cmd_cat_zstr(cmd, id_or_consumer); - REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, !!mkstream, "MKSTREAM"); + redis_cmd_cat_literal_if(cmd, !!mkstream, "MKSTREAM"); if (entries_read != -2) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ENTRIESREAD"); - redis_cmd_append_sstr_long(&cmdstr, entries_read); + redis_cmd_cat_literal(cmd, "ENTRIESREAD"); + redis_cmd_cat_long(cmd, entries_read); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* XINFO CONSUMERS key group * XINFO GROUPS key * XINFO STREAM key [FULL [COUNT N]] * XINFO HELP */ -int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *op = NULL, *key = NULL, *arg = NULL; - smart_string cmdstr = {0}; zend_long count = -1; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_STR(op) @@ -6787,40 +6042,36 @@ int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR_OR_NULL(key) Z_PARAM_STR_OR_NULL(arg) Z_PARAM_LONG(count) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if ((arg != NULL && key == NULL) || (count != -1 && (key == NULL || arg == NULL))) { - php_error_docref(NULL, E_WARNING, "Cannot pass a non-null optional argument after a NULL one."); - return FAILURE; + php_error_docref(NULL, E_WARNING, + "Cannot pass a non-null optional argument after a NULL one."); + return NULL; } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (key != NULL) + (arg != NULL) + (count > -1 ? 2 : 0), "XINFO"); - redis_cmd_append_sstr_zstr(&cmdstr, op); + cmd = redis_cmd_create_literal(redis_sock, "XINFO"); + redis_cmd_cat_zstr(cmd, op); if (key != NULL) - redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, key); if (arg != NULL) - redis_cmd_append_sstr_zstr(&cmdstr, arg); + redis_cmd_cat_zstr(cmd, arg); if (count > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, count); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } // XTRIM key [= | ~] threshold [LIMIT count] -int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *key = NULL, *threshold = NULL; zend_bool approx = 0, minid = 0; - smart_string cmdstr = {0}; zend_long limit = -1; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 5) Z_PARAM_STR(key) @@ -6829,56 +6080,53 @@ int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_BOOL(approx) Z_PARAM_BOOL(minid) Z_PARAM_LONG(limit) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - argc = 4 + (approx && limit > -1 ? 2 : 0); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XTRIM"); + cmd = redis_cmd_create_literal(redis_sock, "XTRIM"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, key); if (minid) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MINID"); + redis_cmd_cat_literal(cmd, "MINID"); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MAXLEN"); + redis_cmd_cat_literal(cmd, "MAXLEN"); } if (approx) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "~"); + redis_cmd_cat_literal(cmd, "~"); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "="); + redis_cmd_cat_literal(cmd, "="); } - redis_cmd_append_sstr_zstr(&cmdstr, threshold); + redis_cmd_cat_zstr(cmd, threshold); if (limit > -1 && approx) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT"); - redis_cmd_append_sstr_long(&cmdstr, limit); + redis_cmd_cat_literal(cmd, "LIMIT"); + redis_cmd_cat_long(cmd, limit); } else if (limit > -1) { - php_error_docref(NULL, E_WARNING, "Cannot use LIMIT without an approximate match, ignoring"); + php_error_docref(NULL, E_WARNING, + "Cannot use LIMIT without an approximate match, ignoring"); } else if (ZEND_NUM_ARGS() == 5) { php_error_docref(NULL, E_WARNING, "Limit must be >= 0"); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - return SUCCESS; + return cmd; } // [P]EXPIRE[AT] [NX | XX | GT | LT] -int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +RedisCmd *redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw) { zend_string *key = NULL, *mode = NULL; - smart_string cmdstr = {0}; zend_long timeout = 0; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(key) Z_PARAM_LONG(timeout) Z_PARAM_OPTIONAL Z_PARAM_STR_OR_NULL(mode) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (mode != NULL && !(zend_string_equals_literal_ci(mode, "NX") || zend_string_equals_literal_ci(mode, "XX") || @@ -6886,28 +6134,24 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_string_equals_literal_ci(mode, "GT"))) { php_error_docref(NULL, E_WARNING, "Unknown expiration modifier '%s'", ZSTR_VAL(mode)); - return FAILURE; + return NULL; } - redis_cmd_init_sstr(&cmdstr, 2 + (mode != NULL), kw, strlen(kw)); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_long(&cmdstr, timeout); - if (mode != NULL) redis_cmd_append_sstr_zstr(&cmdstr, mode); + cmd = redis_cmd_create(redis_sock, kw, strlen(kw)); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, timeout); + if (mode != NULL) redis_cmd_cat_zstr(cmd, mode); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -static int +static RedisCmd * generic_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, size_t kw_len, int has_unit, char **cmd, - int *cmd_len, short *slot) + char *kw, size_t kw_len, int has_unit) { zend_string *key, *mem, *unit = NULL; - smart_string cmdstr = {0}; zend_long expiry; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, has_unit ? 4 : 3) Z_PARAM_STR(key) @@ -6917,63 +6161,54 @@ generic_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_OPTIONAL Z_PARAM_STR_OR_NULL(unit) } - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - redis_cmd_init_sstr(&cmdstr, 3 + (unit != NULL), kw, kw_len); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zstr(&cmdstr, mem); - redis_cmd_append_sstr_long(&cmdstr, expiry); + cmd = redis_cmd_create(redis_sock, kw, kw_len); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zstr(cmd, mem); + redis_cmd_cat_long(cmd, expiry); if (unit != NULL) { - redis_cmd_append_sstr_zstr(&cmdstr, unit); + redis_cmd_cat_zstr(cmd, unit); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd * +redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { return generic_expiremember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, ZEND_STRL("EXPIREMEMBER"), 1, - cmd, cmd_len, slot); + redis_sock, ZEND_STRL("EXPIREMEMBER"), 1); } -int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd * +redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { return generic_expiremember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, ZEND_STRL("EXPIREMEMBERAT"), 0, - cmd, cmd_len, slot); + redis_sock, ZEND_STRL("EXPIREMEMBERAT"), 0); } -int +RedisCmd * redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) + char *kw) { if (zend_parse_parameters_none() == FAILURE) { - return FAILURE; + return NULL; } - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "s", kw, strlen(kw)); - return SUCCESS; + return redis_cmd_fmt(redis_sock, "SENTINEL", "s", kw, strlen(kw)); } -int +RedisCmd * redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) + char *kw) { zend_string *name; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) { - return FAILURE; + return NULL; } - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SENTINEL", "sS", kw, strlen(kw), name); - return SUCCESS; + return redis_cmd_fmt(redis_sock, "SENTINEL", "sS", kw, strlen(kw), name); } typedef enum redisVAddQuant { @@ -7075,8 +6310,7 @@ static void free_vadd_options(redisVAddOptions *o) { zend_string_release(o->attributes); } -static void -redis_cmd_append_sstr_fp32(smart_string *dst, HashTable *ht) { +static void redis_cmd_cat_fp32(RedisCmd *cmd, HashTable *ht) { uint32_t i = 0; char *aux; zval *zv; @@ -7087,29 +6321,25 @@ redis_cmd_append_sstr_fp32(smart_string *dst, HashTable *ht) { redis_copy_fp32(&aux[4 * i++], zval_get_double(zv)); } ZEND_HASH_FOREACH_END(); - redis_cmd_append_sstr(dst, aux, 4 * i); + redis_cmd_cat_str(cmd, aux, 4 * i); efree(aux); } -static void redis_cmd_append_sstr_fp32_values(smart_string *dst, HashTable *ht) { +static void redis_cmd_cat_fp32_values(RedisCmd *dst, HashTable *ht) { zval *zv; ZEND_HASH_FOREACH_VAL(ht, zv) { - redis_cmd_append_sstr_dbl(dst, zval_get_double(zv)); + redis_cmd_cat_double(dst, zval_get_double(zv)); } ZEND_HASH_FOREACH_END(); } -int -redis_vadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_vadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { HashTable *options = NULL, *vector; - smart_string cmdstr = {0}; redisVAddOptions opts; zend_string *key; zval *element; - int argc; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STR(key) @@ -7117,83 +6347,59 @@ redis_vadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ZVAL(element) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(options) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); if (zend_hash_num_elements(vector) < 1) { php_error_docref(NULL, E_WARNING, "At least one vector element is required"); - return FAILURE; + return NULL; } parse_vadd_options(&opts, options); - // Two formulations: - // VADD FP32 [vector] - // VADD VALUES .. - // So and are always present and we need to calculate the - // other arguments dynamically. - argc = 2 + - (opts.reduce > -1 ? 2 : 0) + - (opts.values ? 2 + zend_hash_num_elements(vector) : 2) + - !!opts.cas + - (opts.attributes != NULL ? 2 : 0) + - (opts.numlinks > 0 ? 2 : 0) + - (opts.ef > 0 ? 2 : 0) + - !!(opts.quant != REDIS_QUANT_NONE); - - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "VADD"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + cmd = redis_cmd_create_literal(redis_sock, "VADD"); + redis_cmd_cat_key_zstr(cmd, key); if (opts.reduce > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REDUCE"); - redis_cmd_append_sstr_long(&cmdstr, opts.reduce); + redis_cmd_cat_literal(cmd, "REDUCE"); + redis_cmd_cat_long(cmd, opts.reduce); } if (opts.values) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "VALUES"); - redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(vector)); - redis_cmd_append_sstr_fp32_values(&cmdstr, vector); + redis_cmd_cat_literal(cmd, "VALUES"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(vector)); + redis_cmd_cat_fp32_values(cmd, vector); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FP32"); - redis_cmd_append_sstr_fp32(&cmdstr, vector); + redis_cmd_cat_literal(cmd, "FP32"); + redis_cmd_cat_fp32(cmd, vector); } - redis_cmd_append_sstr_zval(&cmdstr, element, redis_sock); + redis_cmd_cat_zval(cmd, element); - if (opts.cas) - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "CAS"); + redis_cmd_cat_literal_if(cmd, opts.cas, "CAS"); - if (opts.quant != REDIS_QUANT_NONE) { - if (opts.quant == REDIS_QUANT_Q8) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "Q8"); - } else if (opts.quant == REDIS_QUANT_BIN) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BIN"); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NOQUANT"); - } - } + redis_cmd_cat_literal_if(cmd, opts.quant == REDIS_QUANT_Q8, "Q8"); + redis_cmd_cat_literal_if(cmd, opts.quant == REDIS_QUANT_BIN, "BIN"); + redis_cmd_cat_literal_if(cmd, opts.quant == REDIS_QUANT_NOQUANT, "NOQUANT"); if (opts.ef > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "EF"); - redis_cmd_append_sstr_long(&cmdstr, opts.ef); + redis_cmd_cat_literal(cmd, "EF"); + redis_cmd_cat_long(cmd, opts.ef); } if (opts.attributes != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SETATTR"); - redis_cmd_append_sstr_zstr(&cmdstr, opts.attributes); + redis_cmd_cat_literal(cmd, "SETATTR"); + redis_cmd_cat_zstr(cmd, opts.attributes); } if (opts.numlinks > -1) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "M"); - redis_cmd_append_sstr_long(&cmdstr, opts.numlinks); + redis_cmd_cat_literal(cmd, "M"); + redis_cmd_cat_long(cmd, opts.numlinks); } free_vadd_options(&opts); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } typedef enum redisVSimMemberType { @@ -7279,112 +6485,94 @@ static redisVSimMemberType detect_vsim_type(zval *zv) { return REDIS_VSIM_FP32; } -int -redis_vsim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd * +redis_vsim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { HashTable *options = NULL; - smart_string cmdstr = {0}; redisVSimOptions opts; zend_string *key; + RedisCmd *cmd; zval *member; - int argc; ZEND_PARSE_PARAMETERS_START(2, 3) { Z_PARAM_STR(key) Z_PARAM_ZVAL(member) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_HT_OR_NULL(options) - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); parse_vsim_options(&opts, options); if (opts.type == REDIS_VSIM_NONE) opts.type = detect_vsim_type(member); + if (opts.type == REDIS_VSIM_NONE) { + zend_error_noreturn(E_ERROR, + "Internal error. type shouldn't be REDIS_VSIM_NONE!"); + return NULL; + } + /* Sanity check on the member type */ if (opts.type != REDIS_VSIM_ELE && Z_TYPE_P(member) != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "member must be an array when not querying in ELE mode"); - return FAILURE; + free_vsim_options(&opts); + return NULL; } - argc = 1 + (opts.count > 0 ? 2 : 0) + (opts.ef > 0 ? 2 : 0) - + (opts.filter != NULL ? 2 : 0) + (opts.filter_ef > 0 ? 2 : 0) - + !!opts.truth + !!opts.nothread + !!opts.withscores; + cmd = redis_cmd_create_literal(redis_sock, "VSIM"); - if (opts.type == REDIS_VSIM_ELE || opts.type == REDIS_VSIM_FP32) { - // ELE or FP32 - argc += 2; - } else if (opts.type == REDIS_VSIM_VALUES) { - // VALUES - argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(member)); - } else { - zend_error_noreturn(E_ERROR, - "Internal error. type shouldn't be REDIS_VSIM_NONE!"); - } - - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "VSIM"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_cat_key_zstr(cmd, key); if (opts.type == REDIS_VSIM_FP32) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FP32"); - redis_cmd_append_sstr_fp32(&cmdstr, Z_ARRVAL_P(member)); + redis_cmd_cat_literal(cmd, "FP32"); + redis_cmd_cat_fp32(cmd, Z_ARRVAL_P(member)); } else if (opts.type == REDIS_VSIM_VALUES) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "VALUES"); - redis_cmd_append_sstr_long(&cmdstr, - zend_hash_num_elements(Z_ARRVAL_P(member))); - redis_cmd_append_sstr_fp32_values(&cmdstr, Z_ARRVAL_P(member)); + redis_cmd_cat_literal(cmd, "VALUES"); + redis_cmd_cat_long(cmd, zend_hash_num_elements(Z_ARRVAL_P(member))); + redis_cmd_cat_fp32_values(cmd, Z_ARRVAL_P(member)); } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ELE"); - redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + redis_cmd_cat_literal(cmd, "ELE"); + redis_cmd_cat_zval(cmd, member); } if (opts.withscores) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_literal(cmd, "WITHSCORES"); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } if (opts.count > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); - redis_cmd_append_sstr_long(&cmdstr, opts.count); + redis_cmd_cat_literal(cmd, "COUNT"); + redis_cmd_cat_long(cmd, opts.count); } if (opts.ef > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "EF"); - redis_cmd_append_sstr_long(&cmdstr, opts.ef); + redis_cmd_cat_literal(cmd, "EF"); + redis_cmd_cat_long(cmd, opts.ef); } if (opts.filter != NULL) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FILTER"); - redis_cmd_append_sstr_zstr(&cmdstr, opts.filter); + redis_cmd_cat_literal(cmd, "FILTER"); + redis_cmd_cat_zstr(cmd, opts.filter); } if (opts.filter_ef > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FILTER-EF"); - redis_cmd_append_sstr_long(&cmdstr, opts.filter_ef); + redis_cmd_cat_literal(cmd, "FILTER-EF"); + redis_cmd_cat_long(cmd, opts.filter_ef); } - if (opts.truth) - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TRUTH"); - if (opts.nothread) - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NOTHREAD"); + redis_cmd_cat_literal_if(cmd, opts.truth, "TRUTH"); + redis_cmd_cat_literal_if(cmd, opts.nothread, "NOTHREAD"); free_vsim_options(&opts); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int -redis_vemb_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd * +redis_vemb_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_bool raw = 0; zend_string *key; + RedisCmd *cmd; zval *member; ZEND_PARSE_PARAMETERS_START(2, 3) { @@ -7392,29 +6580,22 @@ redis_vemb_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ZVAL(member); Z_PARAM_OPTIONAL Z_PARAM_BOOL(raw) - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + !!raw, "VEMB"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + cmd = redis_cmd_create_literal(redis_sock, "VEMB"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zval(cmd, member); - if (raw) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "RAW"); - } + redis_cmd_cat_literal_if(cmd, raw, "RAW"); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int -redis_vlinks_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd * +redis_vlinks_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_bool withscores = 0; zend_string *key; + RedisCmd *cmd; zval *member; ZEND_PARSE_PARAMETERS_START(2, 3) { @@ -7422,28 +6603,25 @@ redis_vlinks_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ZVAL(member) Z_PARAM_OPTIONAL Z_PARAM_BOOL(withscores) - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); + + cmd = redis_cmd_create_literal(redis_sock, "VLINKS"); + + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zval(cmd, member); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + !!withscores, "VLINKS"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); if (withscores) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_cat_literal(cmd, "WITHSCORES"); + redis_cmd_set_ctx(cmd, PHPREDIS_CTX_PTR); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int redis_vgetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd *redis_vgetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_bool raw = 0; zend_string *key; + RedisCmd *cmd; zval *member; ZEND_PARSE_PARAMETERS_START(2, 3) { @@ -7451,58 +6629,47 @@ int redis_vgetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ZVAL(member) Z_PARAM_OPTIONAL Z_PARAM_BOOL(raw) - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "VGETATTR"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + cmd = redis_cmd_create_literal(redis_sock, "VGETATTR"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zval(cmd, member); - *ctx = raw ? NULL : PHPREDIS_CTX_PTR; + redis_cmd_set_ctx(cmd, raw ? NULL : PHPREDIS_CTX_PTR); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } -int -redis_vsetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; +RedisCmd *redis_vsetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *key, *attr; zval *member, *zattr; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(3, 3) { Z_PARAM_STR(key) Z_PARAM_ZVAL(member) Z_PARAM_ZVAL(zattr) - } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + } ZEND_PARSE_PARAMETERS_END_EX(return NULL); attr = zval_to_vattr(zattr); if (zattr == NULL) - return FAILURE; + return NULL; - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "VSETATTR"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); - redis_cmd_append_sstr_zstr(&cmdstr, attr); + cmd = redis_cmd_create_literal(redis_sock, "VSETATTR"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_zval(cmd, member); + redis_cmd_cat_zstr(cmd, attr); zend_string_release(attr); - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } // GCRA key max-burst requests-per-period period [NUM_REQUESTS count] -int redis_gcra_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ +RedisCmd *redis_gcra_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_long max_burst, req_per_period, period, tokens = 0; - smart_string cmdstr = {0}; zend_string *key; + RedisCmd *cmd; ZEND_PARSE_PARAMETERS_START(4, 5) Z_PARAM_STR(key) @@ -7511,23 +6678,20 @@ int redis_gcra_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_LONG(period) Z_PARAM_OPTIONAL Z_PARAM_LONG(tokens) - ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + ZEND_PARSE_PARAMETERS_END_EX(return NULL); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 4 + (tokens > 0 ? 2 : 0), "GCRA"); - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - redis_cmd_append_sstr_long(&cmdstr, max_burst); - redis_cmd_append_sstr_long(&cmdstr, req_per_period); - redis_cmd_append_sstr_long(&cmdstr, period); + cmd = redis_cmd_create_literal(redis_sock, "GCRA"); + redis_cmd_cat_key_zstr(cmd, key); + redis_cmd_cat_long(cmd, max_burst); + redis_cmd_cat_long(cmd, req_per_period); + redis_cmd_cat_long(cmd, period); if (tokens > 0) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TOKENS"); - redis_cmd_append_sstr_long(&cmdstr, tokens); + redis_cmd_cat_literal(cmd, "TOKENS"); + redis_cmd_cat_long(cmd, tokens); } - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; - - return SUCCESS; + return cmd; } /* @@ -7756,11 +6920,10 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, } void redis_prefix_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { - char *key; size_t key_len; + char *key; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) - ==FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { RETURN_FALSE; } diff --git a/redis_commands.h b/redis_commands.h index fee9f431..cac361dc 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -4,6 +4,7 @@ #include "common.h" #include "library.h" #include "cluster_library.h" +#include "redis_cmd.h" /* Pick a random slot, any slot (for stuff like publish/subscribe) */ #define CMD_RAND_SLOT(slot) \ @@ -26,424 +27,154 @@ typedef struct subscribeContext { } subscribeContext; /* Construct a raw command */ -int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len); +RedisCmd *redis_build_raw_cmd(zval *z_args, int argc); /* Construct a script command */ -smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args); +RedisCmd *redis_build_script_cmd(int argc, zval *z_args); -typedef int (*redis_cmd_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); +typedef RedisCmd * redis_cmd_cb(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); -typedef int (*redis_kw_cmd_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx); +typedef RedisCmd * redis_kw_cmd_cb(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw); /* Process a command but with a specific command building function * and keyword which is passed to us*/ void redis_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, - redis_kw_cmd_cb cmd_cb, FailableResultCallback resp_cb, - void *ctx); + redis_kw_cmd_cb cmd_cb, FailableResultCallback resp_cb); #define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ redis_process_kw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, \ - cmdfunc, resp_func, NULL) - -/* Redis command generics. Many commands share common prototypes meaning that - * we can write one function to handle all of them. For example, there are - * many COMMAND key value commands, or COMMAND key commands. */ - -int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx); - -int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -/* Construct SCAN and similar commands, as well as check iterator */ -int redis_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - REDIS_SCAN_TYPE type, char **cmd, int *cmd_len); - -/* ZRANGE, ZREVRANGE, ZRANGEBYSCORE, and ZREVRANGEBYSCORE callback type */ -typedef int (*zrange_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *,char**,int*,int*,short*,void**); - -int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx); - -int redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -/* Commands which need a unique construction mechanism. This is either because - * they don't share a signature with any other command, or because there is - * specific processing we do (e.g. verifying subarguments) that make them - * unique */ - -int redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_delex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hgetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hsetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hgetdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, + cmdfunc, resp_func) + +/* Redis command builders */ + +RedisCmd *redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_opt_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_long_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_kv_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_long_long_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_str_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_val_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_key_str_arr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_blocking_pop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_function_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_intercard_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_slowlog_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_lcs_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_mpop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_restore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_vrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_georadiusbymember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_brpoplpush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_delex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hgetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hsetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hgetdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hmset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hstrlen_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_bitop_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_bitcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_bitpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_pfcount_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_pfadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_pfmerge_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_auth_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_setbit_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_linsert_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_lrem_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_lpos_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_smove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_zincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_zadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_object_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_fmt_scan_cmd(REDIS_SCAN_TYPE type, char *key, int key_len, uint64_t it, char *pat, int pat_len, long count); - -int redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vsim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vemb_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vgetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vsetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_gcra_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vlinks_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xdelex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_randmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_msetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); - -int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +RedisCmd *redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_vadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_vsim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_vemb_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_vgetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_vsetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_gcra_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_vlinks_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xdelex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xackdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xinfo_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xread_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_randmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_msetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +RedisCmd *redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); +RedisCmd *redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw); /* Commands that don't communicate with Redis at all (such as getOption, * setOption, _prefix, _serialize, etc). These can be handled in one place diff --git a/redis_sentinel.c b/redis_sentinel.c index fdaa191f..b205799c 100644 --- a/redis_sentinel.c +++ b/redis_sentinel.c @@ -50,7 +50,8 @@ PHP_METHOD(RedisSentinel, __construct) ZEND_PARSE_PARAMETERS_END_EX(RETURN_THROWS()); sentinel = PHPREDIS_ZVAL_GET_OBJECT(redis_sentinel_object, getThis()); - sentinel->sock = redis_sock_create(ZEND_STRL("127.0.0.1"), 26379, 0, 0, 0, NULL, 0); + sentinel->sock = redis_sock_create(REDIS_SOCK_SENTINEL, ZEND_STRL("127.0.0.1"), + 26379, 0, 0, 0, NULL, 0); if (opts != NULL && redis_sock_configure(sentinel->sock, opts) != SUCCESS) { RETURN_THROWS(); } diff --git a/redis_session.c b/redis_session.c index 5e32348f..d937222e 100644 --- a/redis_session.c +++ b/redis_session.c @@ -19,6 +19,7 @@ */ #include "common.h" +#include "redis_cmd.h" #if PHP_VERSION_ID < 80400 #include @@ -246,7 +247,7 @@ session_uncompress_data(RedisSock *redis_sock, char *data, size_t len, } /* Send a command to Redis. Returns byte count written to socket (-1 on failure) */ -static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, +static int redis_simple_cmd(RedisSock *redis_sock, const char *cmd, int cmdlen, char **reply, int *replylen) { *reply = NULL; @@ -259,13 +260,6 @@ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, return len_written; } -static int -redis_simple_cmd_sstr(RedisSock *redis_sock, smart_string *cmd, - char **reply, int *replylen) -{ - return redis_simple_cmd(redis_sock, cmd->c, cmd->len, reply, replylen); -} - static inline int weighted_seed(zend_string *key) { /* In GCC the fast-path is one 32-bit load with a union */ union { int pos; } u; @@ -306,8 +300,8 @@ redis_pool_get_sock(redis_pool *pool, zend_string *key) { } /* Helper to set our session lock key */ -static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len - ) +static int +set_session_lock_key(RedisSock *redis_sock, const char *cmd, int cmd_len) { char *reply; int sent_len, reply_len; @@ -359,8 +353,8 @@ static void generate_lock_secret(redis_session_lock_status *status) { static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status) { zend_long wait_time, expiry, retries, attempt = 0; - int cmd_len, result; - char *cmd; + RedisCmd *cmd; + int result; /* Short circuit if we are already locked or not using session locks */ if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled")) @@ -388,17 +382,18 @@ lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status) { generate_lock_secret(lock_status); if (expiry > 0) { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSssd", lock_status->lock_key, - lock_status->lock_secret, "NX", 2, "PX", 2, - expiry * 1000); + cmd = redis_cmd_fmt(redis_sock, "SET", "SSssd", lock_status->lock_key, + lock_status->lock_secret, "NX", 2, "PX", 2, + expiry * 1000); } else { - cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSs", lock_status->lock_key, - lock_status->lock_secret, "NX", 2); + cmd = redis_cmd_fmt(redis_sock, "SET", "SSs", lock_status->lock_key, + lock_status->lock_secret, "NX", 2); } /* Attempt to get our lock */ for (;;) { - result = set_session_lock_key(redis_sock, cmd, cmd_len); + result = set_session_lock_key(redis_sock, redis_cmd_str(cmd), + redis_cmd_len(cmd)); if (result == SUCCESS) { lock_status->is_locked = 1; @@ -417,16 +412,21 @@ lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status) { usleep(wait_time); } - /* Cleanup SET command */ - efree(cmd); + redis_cmd_free(cmd); /* Success if we're locked */ return lock_status->is_locked ? SUCCESS : FAILURE; } -#define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !redis_strncmp(reply, ZSTR_VAL(secret), len)) -static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status) -{ +static zend_always_inline +zend_bool is_lock_secret(const char *str, size_t len, zend_string *secret) { + return len == ZSTR_LEN(secret) && !redis_strncmp(str, ZSTR_VAL(secret), len); +} + +static int +write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status) { + RedisCmd *cmd; + if (!INI_INT("redis.session.locking_enabled")) { return 1; } @@ -434,20 +434,23 @@ static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_ Therefore it is guaranteed that the current process is still holding the lock */ if (lock_status->is_locked && INI_INT("redis.session.lock_expire") != 0) { - char *cmd, *reply = NULL; - int replylen, cmdlen; + char *reply = NULL; + int replylen; + /* Command to get our lock key value and compare secrets */ - cmdlen = REDIS_SPPRINTF(&cmd, "GET", "S", lock_status->lock_key); + cmd = redis_cmd_fmt(redis_sock, "GET", "S", lock_status->lock_key); /* Attempt to refresh the lock */ - redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); - /* Cleanup */ - efree(cmd); + redis_simple_cmd(redis_sock, redis_cmd_str(cmd), redis_cmd_len(cmd), + &reply, &replylen); + + redis_cmd_free(cmd); if (reply == NULL) { lock_status->is_locked = 0; } else { - lock_status->is_locked = IS_LOCK_SECRET(reply, replylen, lock_status->lock_secret); + lock_status->is_locked = is_lock_secret(reply, replylen, + lock_status->lock_secret); efree(reply); } @@ -488,44 +491,47 @@ get_del_result(RedisSock *redis_sock, const char *reply, int len) static delResult lock_release_delex(RedisSock *redis_sock, redis_session_lock_status *status) { - smart_string cmd = {0}; delResult result; + RedisCmd *cmd; char *reply; int len; - REDIS_CMD_INIT_SSTR_STATIC(&cmd, 3, "DELEX"); - redis_cmd_append_sstr_zstr(&cmd, status->lock_key); - REDIS_CMD_APPEND_SSTR_STATIC(&cmd, "IFEQ"); - redis_cmd_append_sstr_zstr(&cmd, status->lock_secret); + cmd = redis_cmd_create_literal(redis_sock, "DELEX"); - redis_simple_cmd_sstr(redis_sock, &cmd, &reply, &len); + redis_cmd_cat_zstr(cmd, status->lock_key); + redis_cmd_cat_literal(cmd, "IFEQ"); + redis_cmd_cat_zstr(cmd, status->lock_secret); + + redis_simple_cmd(redis_sock, redis_cmd_str(cmd), redis_cmd_len(cmd), &reply, + &len); result = get_del_result(redis_sock, reply, len); if (reply) efree(reply); - smart_string_free(&cmd); + redis_cmd_free(cmd); return result; } static delResult lock_release_delifeq(RedisSock *redis_sock, redis_session_lock_status *status) { - smart_string cmd = {0}; delResult result; + RedisCmd *cmd; char *reply; int len; - REDIS_CMD_INIT_SSTR_STATIC(&cmd, 2, "DELIFEQ"); + cmd = redis_cmd_create_literal(redis_sock, "DELIFEQ"); - redis_cmd_append_sstr_zstr(&cmd, status->lock_key); - redis_cmd_append_sstr_zstr(&cmd, status->lock_secret); + redis_cmd_cat_zstr(cmd, status->lock_key); + redis_cmd_cat_zstr(cmd, status->lock_secret); - redis_simple_cmd_sstr(redis_sock, &cmd, &reply, &len); + redis_simple_cmd(redis_sock, redis_cmd_str(cmd), redis_cmd_len(cmd), &reply, + &len); result = get_del_result(redis_sock, reply, len); if (reply) efree(reply); - smart_string_free(&cmd); + redis_cmd_free(cmd); return result; } @@ -536,18 +542,20 @@ lock_release_delifeq(RedisSock *redis_sock, redis_session_lock_status *status) { * calls should then succeed using EVALSHA. */ static void lock_release_lua(RedisSock *redis_sock, redis_session_lock_status *status) { - int i, cmdlen, replylen; - char *cmd, *reply; + int i, replylen; + RedisCmd *cmd; + char *reply; /* We first want to try EVALSHA and then fall back to EVAL */ for (i = 0; status->is_locked && i < sizeof(lua_cmd)/sizeof(*lua_cmd); i++) { - cmdlen = REDIS_SPPRINTF(&cmd, lua_cmd[i].kw, "sdSS", lua_cmd[i].str, - lua_cmd[i].len, 1, status->lock_key, - status->lock_secret); + cmd = redis_cmd_fmt(redis_sock, lua_cmd[i].kw, "sdSS", lua_cmd[i].str, + lua_cmd[i].len, 1, status->lock_key, + status->lock_secret); /* Send it off */ - redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); + redis_simple_cmd(redis_sock, redis_cmd_str(cmd), redis_cmd_len(cmd), + &reply, &replylen); /* Release lock and cleanup reply if we got one */ if (reply != NULL) { @@ -556,7 +564,7 @@ lock_release_lua(RedisSock *redis_sock, redis_session_lock_status *status) { } /* Cleanup command */ - efree(cmd); + redis_cmd_free(cmd); } /* Something has failed if we are still locked */ @@ -708,6 +716,7 @@ PS_OPEN_FUNC(redis) } RedisSock *redis_sock; + const char *persistent_id_str; char *addr, *scheme; size_t addrlen; int port, addr_free = 0; @@ -723,8 +732,10 @@ PS_OPEN_FUNC(redis) addrlen = strlen(addr); } - redis_sock = redis_sock_create(addr, addrlen, port, timeout, read_timeout, - persistent, persistent_id ? ZSTR_VAL(persistent_id) : NULL, + persistent_id_str = persistent_id ? ZSTR_VAL(persistent_id) : NULL; + redis_sock = redis_sock_create(REDIS_SOCK_SESSION, addr, addrlen, + port, timeout, read_timeout, + persistent, persistent_id_str, retry_interval); if (db >= 0) { /* default is -1 which leaves the choice to redis. */ @@ -799,6 +810,11 @@ redis_session_key(RedisSock *redis_sock, const char *key, int key_len) ZSTR_LEN(redis_sock->prefix), key, key_len); } +static zend_always_inline zend_string * +redis_session_key_zstr(RedisSock *redis_sock, zend_string *key) { + return redis_session_key(redis_sock, ZSTR_VAL(key), ZSTR_LEN(key)); +} + /* {{{ PS_CREATE_SID_FUNC */ PS_CREATE_SID_FUNC(redis) @@ -847,13 +863,12 @@ PS_CREATE_SID_FUNC(redis) */ PS_VALIDATE_SID_FUNC(redis) { - char *cmd, *response; - int cmd_len, response_len; + int response_len; + char *response; + RedisCmd *cmd; - const char *skey = ZSTR_VAL(key); - size_t skeylen = ZSTR_LEN(key); - - if (!skeylen) return FAILURE; + if (ZSTR_LEN(key) < 1) + return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, key); @@ -864,16 +879,19 @@ PS_VALIDATE_SID_FUNC(redis) } /* send EXISTS command */ - zend_string *session = redis_session_key(redis_sock, skey, skeylen); - cmd_len = REDIS_SPPRINTF(&cmd, "EXISTS", "S", session); + zend_string *session = redis_session_key_zstr(redis_sock, key); + cmd = redis_cmd_fmt(redis_sock, "EXISTS", "S", session); zend_string_release(session); - if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { + + if (redis_sock_write_cmd(redis_sock, cmd) < 0 || + (response = redis_sock_read(redis_sock, &response_len)) == NULL) + { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } - efree(cmd); + redis_cmd_free(cmd); if (response_len == 2 && response[0] == ':' && response[1] == '1') { efree(response); @@ -889,13 +907,12 @@ PS_VALIDATE_SID_FUNC(redis) */ PS_UPDATE_TIMESTAMP_FUNC(redis) { - char *cmd, *response; - int cmd_len, response_len; + int response_len; + char *response; + RedisCmd *cmd; - const char *skey = ZSTR_VAL(key); - size_t skeylen = ZSTR_LEN(key); - - if (!skeylen) return FAILURE; + if (ZSTR_LEN(key) < 1) + return FAILURE; /* No need to update the session timestamp if we've already done so */ if (INI_INT("redis.session.early_refresh")) { @@ -911,17 +928,19 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) } /* send EXPIRE command */ - zend_string *session = redis_session_key(redis_sock, skey, skeylen); - cmd_len = REDIS_SPPRINTF(&cmd, "EXPIRE", "Sd", session, session_gc_maxlifetime()); + zend_string *session = redis_session_key_zstr(redis_sock, key); + cmd = redis_cmd_fmt(redis_sock, "EXPIRE", "Sd", session, session_gc_maxlifetime()); zend_string_release(session); - if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { + if (redis_sock_write(redis_sock, redis_cmd_str(cmd), redis_cmd_len(cmd)) < 0 || + (response = redis_sock_read(redis_sock, &response_len)) == NULL) + { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } - efree(cmd); + redis_cmd_free(cmd); if (response_len == 2 && response[0] == ':') { efree(response); @@ -937,10 +956,11 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) */ PS_READ_FUNC(redis) { - char *resp, *cmd, *compressed_buf; - int resp_len, cmd_len, compressed_free; + char *resp, *compressed_buf; + int resp_len, compressed_free; const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key), compressed_len; + RedisCmd *cmd; if (!skeylen) return FAILURE; @@ -958,10 +978,13 @@ PS_READ_FUNC(redis) /* Update the session ttl if early refresh is enabled */ if (INI_INT("redis.session.early_refresh")) { - cmd_len = REDIS_SPPRINTF(&cmd, "GETEX", "Ssd", pool->lock_status.session_key, - "EX", 2, session_gc_maxlifetime()); + cmd = redis_cmd_create_literal(redis_sock, "GETEX"); + redis_cmd_cat_zstr(cmd, pool->lock_status.session_key); + redis_cmd_cat_literal(cmd, "EX"); + redis_cmd_cat_long(cmd, session_gc_maxlifetime()); } else { - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); + cmd = redis_cmd_create_literal(redis_sock, "GET"); + redis_cmd_cat_zstr(cmd, pool->lock_status.session_key); } if (lock_acquire(redis_sock, &pool->lock_status) != SUCCESS) { @@ -970,18 +993,18 @@ PS_READ_FUNC(redis) php_error_docref(NULL, E_WARNING, "Failed to acquire session lock, session will be read only"); } else { php_error_docref(NULL, E_WARNING, "Failed to acquire session lock"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } } - if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { + if (redis_sock_write_cmd(redis_sock, cmd) < 0) { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } - efree(cmd); + redis_cmd_free(cmd); /* Read response from Redis. If we get a NULL response from redis_sock_read * this can indicate an error, OR a "NULL bulk" reply (empty session data) @@ -1011,13 +1034,14 @@ PS_READ_FUNC(redis) */ PS_WRITE_FUNC(redis) { - char *cmd, *response; - int cmd_len, response_len, compressed_free; - const char *skey = ZSTR_VAL(key); - size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); + char *response; + int response_len, compressed_free; + size_t svallen; + RedisCmd *cmd; char *sval; - if (!skeylen) return FAILURE; + if (ZSTR_LEN(key) < 1) + return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, key); @@ -1028,12 +1052,13 @@ PS_WRITE_FUNC(redis) } /* send SET command */ - zend_string *session = redis_session_key(redis_sock, skey, skeylen); + zend_string *session = redis_session_key_zstr(redis_sock, key); compressed_free = session_compress_data(redis_sock, ZSTR_VAL(val), ZSTR_LEN(val), &sval, &svallen); - cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, session_gc_maxlifetime(), sval, svallen); + cmd = redis_cmd_fmt(redis_sock, "SETEX", "Sds", session, + session_gc_maxlifetime(), sval, svallen); zend_string_release(session); if (compressed_free) { efree(sval); @@ -1041,17 +1066,19 @@ PS_WRITE_FUNC(redis) if (!write_allowed(redis_sock, &pool->lock_status)) { php_error_docref(NULL, E_WARNING, "Unable to write session: session lock not held"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } - if (redis_sock_write(redis_sock, cmd, cmd_len ) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { + if (redis_sock_write_cmd(redis_sock, cmd) < 0 || + (response = redis_sock_read(redis_sock, &response_len)) == NULL) + { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } - efree(cmd); + redis_cmd_free(cmd); if (is_redis_ok(response, response_len)) { efree(response); @@ -1068,10 +1095,9 @@ PS_WRITE_FUNC(redis) */ PS_DESTROY_FUNC(redis) { - char *cmd, *response; - int cmd_len, response_len; - const char *skey = ZSTR_VAL(key); - size_t skeylen = ZSTR_LEN(key); + char *response; + int response_len; + RedisCmd *cmd; redis_pool *pool = PS_GET_MOD_DATA(); redis_pool_member *rpm = redis_pool_get_sock(pool, key); @@ -1085,18 +1111,22 @@ PS_DESTROY_FUNC(redis) lock_release(redis_sock, &pool->lock_status); /* send DEL command */ - zend_string *session = redis_session_key(redis_sock, skey, skeylen); - cmd_len = REDIS_SPPRINTF(&cmd, "DEL", "S", session); + zend_string *session = redis_session_key_zstr(redis_sock, key); + cmd = redis_cmd_fmt(redis_sock, "DEL", "S", session); zend_string_release(session); - if (redis_sock_write(redis_sock, cmd, cmd_len) < 0 || (response = redis_sock_read(redis_sock, &response_len)) == NULL) { + if (redis_sock_write_cmd(redis_sock, cmd) < 0 || + (response = redis_sock_read(redis_sock, &response_len)) == NULL) + { php_error_docref(NULL, E_WARNING, "Error communicating with Redis server"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } - efree(cmd); + redis_cmd_free(cmd); - if (response_len == 2 && response[0] == ':' && (response[1] == '0' || response[1] == '1')) { + if (response_len == 2 && response[0] == ':' && + (response[1] == '0' || response[1] == '1')) + { efree(response); return SUCCESS; } else { @@ -1261,9 +1291,10 @@ PS_CREATE_SID_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; + char *skey; + RedisCmd *cmd; zend_string *sid; - int cmdlen, skeylen; + int skeylen; int retries = 3; short slot; @@ -1280,21 +1311,21 @@ PS_CREATE_SID_FUNC(rediscluster) /* Create session key if it doesn't already exist */ skey = cluster_session_key(c, ZSTR_VAL(sid), ZSTR_LEN(sid), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL, &cmd, "SET", "ssssd", skey, + cmd = redis_cmd_fmt(NULL, "SET", "ssssd", skey, skeylen, "", 0, "NX", 2, "EX", 2, session_gc_maxlifetime()); efree(skey); /* Attempt to kick off our command */ c->readonly = 0; - if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { + if (cluster_send_command(c,slot,redis_cmd_str(cmd),redis_cmd_len(cmd)) < 0 || c->err) { php_error_docref(NULL, E_NOTICE, "Redis connection not available"); - efree(cmd); + redis_cmd_free(cmd); zend_string_release(sid); return php_session_create_id(NULL);; } - efree(cmd); + redis_cmd_free(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 1); @@ -1326,8 +1357,9 @@ PS_VALIDATE_SID_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; - int cmdlen, skeylen; + char *skey; + RedisCmd *cmd; + int skeylen; int res = FAILURE; short slot; @@ -1338,18 +1370,18 @@ PS_VALIDATE_SID_FUNC(rediscluster) } skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXISTS", "s", skey, skeylen); + cmd = redis_cmd_fmt(NULL, "EXISTS", "s", skey, skeylen); efree(skey); /* We send to master, to ensure consistency */ c->readonly = 0; - if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { + if (cluster_send_command(c,slot,redis_cmd_str(cmd),redis_cmd_len(cmd)) < 0 || c->err) { php_error_docref(NULL, E_NOTICE, "Redis connection not available"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } - efree(cmd); + redis_cmd_free(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); @@ -1375,8 +1407,9 @@ PS_VALIDATE_SID_FUNC(rediscluster) PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; - int cmdlen, skeylen; + char *skey; + RedisCmd *cmd; + int skeylen; short slot; /* No need to update the session timestamp if we've already done so */ @@ -1386,20 +1419,20 @@ PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL, &cmd, "EXPIRE", "sd", skey, + cmd = redis_cmd_fmt(NULL, "EXPIRE", "sd", skey, skeylen, session_gc_maxlifetime()); efree(skey); /* Attempt to send EXPIRE command */ c->readonly = 0; - if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { + if (cluster_send_command(c,slot,redis_cmd_str(cmd),redis_cmd_len(cmd)) < 0 || c->err) { php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry"); - efree(cmd); + redis_cmd_free(cmd); return FAILURE; } /* Clean up our command */ - efree(cmd); + redis_cmd_free(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); @@ -1420,8 +1453,9 @@ PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { PS_READ_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey, *compressed_buf; - int cmdlen, skeylen, free_flag, compressed_free; + char *skey, *compressed_buf; + RedisCmd *cmd; + int skeylen, free_flag, compressed_free; size_t compressed_len; short slot; @@ -1430,24 +1464,24 @@ PS_READ_FUNC(rediscluster) { /* Update the session ttl if early refresh is enabled */ if (INI_INT("redis.session.early_refresh")) { - cmdlen = redis_spprintf(NULL, NULL, &cmd, "GETEX", "ssd", skey, + cmd = redis_cmd_fmt(NULL, "GETEX", "ssd", skey, skeylen, "EX", 2, session_gc_maxlifetime()); c->readonly = 0; } else { - cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen); + cmd = redis_cmd_fmt(NULL, "GET", "s", skey, skeylen); c->readonly = 1; } efree(skey); /* Attempt to kick off our command */ - if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { - efree(cmd); + if (cluster_send_command(c,slot,redis_cmd_str(cmd),redis_cmd_len(cmd)) < 0 || c->err) { + redis_cmd_free(cmd); return FAILURE; } /* Clean up command */ - efree(cmd); + redis_cmd_free(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); @@ -1481,8 +1515,9 @@ PS_READ_FUNC(rediscluster) { PS_WRITE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey, *sval; - int cmdlen, skeylen, compressed_free; + char *skey, *sval; + RedisCmd *cmd; + int skeylen, compressed_free; size_t svallen; short slot; @@ -1491,7 +1526,7 @@ PS_WRITE_FUNC(rediscluster) { /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL, &cmd, "SETEX", "sds", skey, + cmd = redis_cmd_fmt(NULL, "SETEX", "sds", skey, skeylen, session_gc_maxlifetime(), sval, svallen); efree(skey); @@ -1501,13 +1536,13 @@ PS_WRITE_FUNC(rediscluster) { /* Attempt to send command */ c->readonly = 0; - if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { - efree(cmd); + if (cluster_send_command(c,slot,redis_cmd_str(cmd),redis_cmd_len(cmd)) < 0 || c->err) { + redis_cmd_free(cmd); return FAILURE; } /* Clean up our command */ - efree(cmd); + redis_cmd_free(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); @@ -1527,24 +1562,25 @@ PS_WRITE_FUNC(rediscluster) { PS_DESTROY_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; - int cmdlen, skeylen; + char *skey; + RedisCmd *cmd; + int skeylen; short slot; /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); - cmdlen = redis_spprintf(NULL, NULL, &cmd, "DEL", "s", skey, skeylen); + cmd = redis_cmd_fmt(NULL, "DEL", "s", skey, skeylen); efree(skey); /* Attempt to send command */ - if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { - efree(cmd); + if (cluster_send_command(c,slot,redis_cmd_str(cmd),redis_cmd_len(cmd)) < 0 || c->err) { + redis_cmd_free(cmd); return FAILURE; } /* Clean up our command */ - efree(cmd); + redis_cmd_free(cmd); /* Attempt to read reply */ reply = cluster_read_resp(c, 0); diff --git a/sentinel_library.c b/sentinel_library.c index bed1aca3..3790484a 100644 --- a/sentinel_library.c +++ b/sentinel_library.c @@ -31,7 +31,8 @@ create_sentinel_object(zend_class_entry *ce) } PHP_REDIS_API int -sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, RedisCmdCtx ctx) { char inbuf[4096]; int i, nelem; diff --git a/sentinel_library.h b/sentinel_library.h index 88d9a564..7e902aa1 100644 --- a/sentinel_library.h +++ b/sentinel_library.h @@ -8,6 +8,6 @@ typedef redis_object redis_sentinel_object; zend_object *create_sentinel_object(zend_class_entry *ce); -PHP_REDIS_API int sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int sentinel_mbulk_reply_zipped_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx); #endif /* REDIS_SENTINEL_LIBRARY_H */ diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 4d673d47..394aaa82 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -31,11 +31,11 @@ class Redis_Test extends TestSuite { $result = [Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP]; if (defined('Redis::SERIALIZER_IGBINARY')) - $result[] = Redis::SERIALIZER_IGBINARY; + $result['igbinary'] = Redis::SERIALIZER_IGBINARY; if (defined('Redis::SERIALIZER_JSON')) - $result[] = Redis::SERIALIZER_JSON; + $result['json'] = Redis::SERIALIZER_JSON; if (defined('Redis::SERIALIZER_MSGPACK')) - $result[] = Redis::SERIALIZER_MSGPACK; + $result['msgpack'] = Redis::SERIALIZER_MSGPACK; return $result; } @@ -6032,9 +6032,9 @@ class Redis_Test extends TestSuite { $this->redis->getOption(Redis::OPT_COMPRESSION) ]; - foreach ($this->getSerializers() as $ser) { + foreach ($this->getSerializers() as $ser_name => $ser) { $compressors = $this->getCompressors(); - foreach ($compressors as $cmp) { + foreach ($compressors as $cmp_name => $cmp) { $this->redis->setOption(Redis::OPT_SERIALIZER, $ser); $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); @@ -6052,10 +6052,12 @@ class Redis_Test extends TestSuite { $this->redis->setOption(Redis::OPT_SERIALIZER, $ser); $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); - $this->assertEquals($raw, $this->redis->_pack($v)); + $this->assertEquals($raw, $this->redis->_pack($v), + "$ser_name + $cmp_name"); $unpacked = $this->redis->get('packkey'); - $this->assertEquals($unpacked, $this->redis->_unpack($raw)); + $this->assertEquals($unpacked, $this->redis->_unpack($raw), + "$ser_name + $cmp_name"); } } } @@ -8763,6 +8765,13 @@ class Redis_Test extends TestSuite { $list = $this->redis->command('list', 'filterby', 'pattern', 'lol*'); $this->assertIsArray($list); $this->assertEquals(['lolwut'], $list); + + $keys_and_flags = $this->redis->command( + 'getkeysandflags', 'MSET', 'key1', 'value1', + ); + $this->assertEquals( + [['key1', ['OW', 'update']]], $keys_and_flags, + ); } } diff --git a/tests/TestSuite.php b/tests/TestSuite.php index 30cde97b..80fe8d8f 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -464,12 +464,14 @@ class TestSuite return false; } - protected function assertEquals($expected, $actual): bool { + protected function assertEquals($expected, $actual, ?string $context = NULL): bool { if ($expected === $actual) return true; - self::$errors[] = $this->assertionTrace("%s !== %s", $this->printArg($actual), - $this->printArg($expected)); + $context = $context === NULL ? '' : " ($context)"; + + self::$errors[] = $this->assertionTrace("%s !== %s%s", $this->printArg($actual), + $this->printArg($expected), $context); return false; }