static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, int kw_len, int min_argc, int has_timeout, char **cmd, int *cmd_len, short *slot) { zval *z_args, *z_ele, *ztimeout = NULL; HashTable *ht_arr; char *key; int key_free, i, tail; size_t key_len; int single_array = 0, argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; short kslot = -1; zend_string *zstr; if (argc < min_argc) { zend_wrong_param_count(); return FAILURE; } // Allocate args z_args = emalloc(argc * sizeof(zval)); if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { efree(z_args); return FAILURE; } // Handle our "single array" case if (has_timeout == 0) { single_array = argc==1 && Z_TYPE(z_args[0]) == IS_ARRAY; } else { single_array = argc==2 && Z_TYPE(z_args[0]) == IS_ARRAY && (Z_TYPE(z_args[1]) == IS_LONG || Z_TYPE(z_args[1]) == IS_DOUBLE); if (single_array) ztimeout = &z_args[1]; } // If we're running a single array, rework args if (single_array) { ht_arr = Z_ARRVAL(z_args[0]); argc = zend_hash_num_elements(ht_arr); if (has_timeout) argc++; efree(z_args); z_args = NULL; /* If the array is empty, we can simply abort */ if (argc == 0) return FAILURE; } // Begin construction of our command redis_cmd_init_sstr(&cmdstr, argc, kw, kw_len); if (single_array) { ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { zstr = zval_get_string(z_ele); key = ZSTR_VAL(zstr); key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); // Protect against CROSSLOT errors if (slot) { if (kslot == -1) { kslot = cluster_hash_key(key, key_len); } else if (cluster_hash_key(key,key_len)!=kslot) { zend_string_release(zstr); if (key_free) efree(key); php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot!"); return FAILURE; } } // Append this key, free it if we prefixed redis_cmd_append_sstr(&cmdstr, key, key_len); zend_string_release(zstr); if (key_free) efree(key); } ZEND_HASH_FOREACH_END(); if (ztimeout) { ZEND_ASSERT(Z_TYPE_P(ztimeout) == IS_LONG || Z_TYPE_P(ztimeout) == IS_DOUBLE); if (Z_TYPE_P(ztimeout) == IS_LONG) { redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(ztimeout)); } else { redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(ztimeout)); } } } else { if (has_timeout) { zend_uchar type = Z_TYPE(z_args[argc - 1]); if (type == IS_LONG || type == IS_DOUBLE) { ztimeout = &z_args[argc - 1]; } else { php_error_docref(NULL, E_ERROR, "Timeout value must be a long or double"); efree(z_args); return FAILURE; } } tail = has_timeout ? argc-1 : argc; for(i = 0; i < tail; i++) { zstr = zval_get_string(&z_args[i]); key = ZSTR_VAL(zstr); key_len = ZSTR_LEN(zstr); key_free = redis_key_prefix(redis_sock, &key, &key_len); /* Protect against CROSSSLOT errors if we've got a slot */ if (slot) { if ( kslot == -1) { kslot = cluster_hash_key(key, key_len); } else if (cluster_hash_key(key,key_len)!=kslot) { php_error_docref(NULL, E_WARNING, "Not all keys hash to the same slot"); zend_string_release(zstr); if (key_free) efree(key); efree(z_args); return FAILURE; } } // Append this key redis_cmd_append_sstr(&cmdstr, key, key_len); zend_string_release(zstr); if (key_free) efree(key); } if (ztimeout) { ZEND_ASSERT(Z_TYPE_P(ztimeout) == IS_LONG || Z_TYPE_P(ztimeout) == IS_DOUBLE); if (Z_TYPE_P(ztimeout) == IS_DOUBLE) { redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(ztimeout)); } else { redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(ztimeout)); } } // Cleanup args efree(z_args); } // Push out parameters if (slot) *slot = kslot; *cmd = cmdstr.c; *cmd_len = cmdstr.len; return SUCCESS; }