1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) 1997-2009 The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Michael Grunder <michael.grunder@gmail.com> |
14 | Maintainer: Nicolas Favre-Felix <n.favre-felix@owlient.eu> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "common.h"
23 #include "php_redis.h"
24 #include "ext/standard/info.h"
25 #include "crc16.h"
26 #include "redis_cluster.h"
27 #include "redis_commands.h"
28 #include <zend_exceptions.h>
29 #include "library.h"
30 #include <php_variables.h>
31 #include <SAPI.h>
32
33 zend_class_entry *redis_cluster_ce;
34
35 /* Exception handler */
36 zend_class_entry *redis_cluster_exception_ce;
37
38 /* Handlers for RedisCluster */
39 zend_object_handlers RedisCluster_handlers;
40
41 ZEND_BEGIN_ARG_INFO_EX(arginfo_ctor, 0, 0, 1)
42 ZEND_ARG_INFO(0, name)
43 ZEND_ARG_ARRAY_INFO(0, seeds, 0)
44 ZEND_ARG_INFO(0, timeout)
45 ZEND_ARG_INFO(0, read_timeout)
46 ZEND_ARG_INFO(0, persistent)
47 ZEND_ARG_INFO(0, auth)
48 ZEND_END_ARG_INFO()
49
50 ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1)
51 ZEND_ARG_INFO(0, key)
52 ZEND_ARG_VARIADIC_INFO(0, other_keys)
53 ZEND_END_ARG_INFO()
54
55 ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1)
56 ZEND_ARG_ARRAY_INFO(0, keys, 0)
57 ZEND_END_ARG_INFO()
58
59 ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1)
60 ZEND_ARG_INFO(0, pattern)
61 ZEND_END_ARG_INFO()
62
63 ZEND_BEGIN_ARG_INFO_EX(arginfo_key_or_address, 0, 0, 1)
64 ZEND_ARG_INFO(0, key_or_address)
65 ZEND_END_ARG_INFO()
66
67 ZEND_BEGIN_ARG_INFO_EX(arginfo_key_or_address_variadic, 0, 0, 1)
68 ZEND_ARG_INFO(0, key_or_address)
69 ZEND_ARG_INFO(0, arg)
70 ZEND_ARG_VARIADIC_INFO(0, other_args)
71 ZEND_END_ARG_INFO()
72
73 ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 1)
74 ZEND_ARG_INFO(0, key_or_address)
75 ZEND_ARG_INFO(0, option)
76 ZEND_END_ARG_INFO()
77
78 ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 1)
79 ZEND_ARG_INFO(0, key_or_address)
80 ZEND_ARG_INFO(0, async)
81 ZEND_END_ARG_INFO()
82
83 /* Argument info for HSCAN, SSCAN, HSCAN */
84 ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan_cl, 0, 0, 2)
85 ZEND_ARG_INFO(0, str_key)
86 ZEND_ARG_INFO(1, i_iterator)
87 ZEND_ARG_INFO(0, str_pattern)
88 ZEND_ARG_INFO(0, i_count)
89 ZEND_END_ARG_INFO()
90
91 /* Argument info for SCAN */
92 ZEND_BEGIN_ARG_INFO_EX(arginfo_scan_cl, 0, 0, 2)
93 ZEND_ARG_INFO(1, i_iterator)
94 ZEND_ARG_INFO(0, str_node)
95 ZEND_ARG_INFO(0, str_pattern)
96 ZEND_ARG_INFO(0, i_count)
97 ZEND_END_ARG_INFO()
98
99 ZEND_BEGIN_ARG_INFO_EX(arginfo_acl_cl, 0, 0, 2)
100 ZEND_ARG_INFO(0, key_or_address)
101 ZEND_ARG_INFO(0, subcmd)
102 ZEND_ARG_VARIADIC_INFO(0, args)
103 ZEND_END_ARG_INFO()
104
105 /* Function table */
106 zend_function_entry redis_cluster_functions[] = {
107 PHP_ME(RedisCluster, __construct, arginfo_ctor, ZEND_ACC_PUBLIC)
108 PHP_ME(RedisCluster, _masters, arginfo_void, ZEND_ACC_PUBLIC)
109 PHP_ME(RedisCluster, _prefix, arginfo_key, ZEND_ACC_PUBLIC)
110 PHP_ME(RedisCluster, _redir, arginfo_void, ZEND_ACC_PUBLIC)
111 PHP_ME(RedisCluster, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
112 PHP_ME(RedisCluster, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
113 PHP_ME(RedisCluster, _compress, arginfo_value, ZEND_ACC_PUBLIC)
114 PHP_ME(RedisCluster, _uncompress, arginfo_value, ZEND_ACC_PUBLIC)
115 PHP_ME(RedisCluster, _pack, arginfo_value, ZEND_ACC_PUBLIC)
116 PHP_ME(RedisCluster, _unpack, arginfo_value, ZEND_ACC_PUBLIC)
117 PHP_ME(RedisCluster, acl, arginfo_acl_cl, ZEND_ACC_PUBLIC)
118 PHP_ME(RedisCluster, append, arginfo_key_value, ZEND_ACC_PUBLIC)
119 PHP_ME(RedisCluster, bgrewriteaof, arginfo_key_or_address, ZEND_ACC_PUBLIC)
120 PHP_ME(RedisCluster, bgsave, arginfo_key_or_address, ZEND_ACC_PUBLIC)
121 PHP_ME(RedisCluster, bitcount, arginfo_key, ZEND_ACC_PUBLIC)
122 PHP_ME(RedisCluster, bitop, arginfo_bitop, ZEND_ACC_PUBLIC)
123 PHP_ME(RedisCluster, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC)
124 PHP_ME(RedisCluster, blpop, arginfo_blrpop, ZEND_ACC_PUBLIC)
125 PHP_ME(RedisCluster, brpop, arginfo_blrpop, ZEND_ACC_PUBLIC)
126 PHP_ME(RedisCluster, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC)
127 PHP_ME(RedisCluster, clearlasterror, arginfo_void, ZEND_ACC_PUBLIC)
128 PHP_ME(RedisCluster, bzpopmax, arginfo_blrpop, ZEND_ACC_PUBLIC)
129 PHP_ME(RedisCluster, bzpopmin, arginfo_blrpop, ZEND_ACC_PUBLIC)
130 PHP_ME(RedisCluster, client, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
131 PHP_ME(RedisCluster, close, arginfo_void, ZEND_ACC_PUBLIC)
132 PHP_ME(RedisCluster, cluster, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
133 PHP_ME(RedisCluster, command, arginfo_command, ZEND_ACC_PUBLIC)
134 PHP_ME(RedisCluster, config, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
135 PHP_ME(RedisCluster, dbsize, arginfo_key_or_address, ZEND_ACC_PUBLIC)
136 PHP_ME(RedisCluster, decr, arginfo_key, ZEND_ACC_PUBLIC)
137 PHP_ME(RedisCluster, decrby, arginfo_key_value, ZEND_ACC_PUBLIC)
138 PHP_ME(RedisCluster, del, arginfo_del, ZEND_ACC_PUBLIC)
139 PHP_ME(RedisCluster, discard, arginfo_void, ZEND_ACC_PUBLIC)
140 PHP_ME(RedisCluster, dump, arginfo_key, ZEND_ACC_PUBLIC)
141 PHP_ME(RedisCluster, echo, arginfo_echo, ZEND_ACC_PUBLIC)
142 PHP_ME(RedisCluster, eval, arginfo_eval, ZEND_ACC_PUBLIC)
143 PHP_ME(RedisCluster, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC)
144 PHP_ME(RedisCluster, exec, arginfo_void, ZEND_ACC_PUBLIC)
145 PHP_ME(RedisCluster, exists, arginfo_key, ZEND_ACC_PUBLIC)
146 PHP_ME(RedisCluster, expire, arginfo_expire, ZEND_ACC_PUBLIC)
147 PHP_ME(RedisCluster, expireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
148 PHP_ME(RedisCluster, flushall, arginfo_flush, ZEND_ACC_PUBLIC)
149 PHP_ME(RedisCluster, flushdb, arginfo_flush, ZEND_ACC_PUBLIC)
150 PHP_ME(RedisCluster, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC)
151 PHP_ME(RedisCluster, geodist, arginfo_geodist, ZEND_ACC_PUBLIC)
152 PHP_ME(RedisCluster, geohash, arginfo_key_members, ZEND_ACC_PUBLIC)
153 PHP_ME(RedisCluster, geopos, arginfo_key_members, ZEND_ACC_PUBLIC)
154 PHP_ME(RedisCluster, georadius, arginfo_georadius, ZEND_ACC_PUBLIC)
155 PHP_ME(RedisCluster, georadius_ro, arginfo_georadius, ZEND_ACC_PUBLIC)
156 PHP_ME(RedisCluster, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
157 PHP_ME(RedisCluster, georadiusbymember_ro, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
158 PHP_ME(RedisCluster, get, arginfo_key, ZEND_ACC_PUBLIC)
159 PHP_ME(RedisCluster, getbit, arginfo_key_offset, ZEND_ACC_PUBLIC)
160 PHP_ME(RedisCluster, getlasterror, arginfo_void, ZEND_ACC_PUBLIC)
161 PHP_ME(RedisCluster, getmode, arginfo_void, ZEND_ACC_PUBLIC)
162 PHP_ME(RedisCluster, getoption, arginfo_getoption, ZEND_ACC_PUBLIC)
163 PHP_ME(RedisCluster, getrange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
164 PHP_ME(RedisCluster, getset, arginfo_key_value, ZEND_ACC_PUBLIC)
165 PHP_ME(RedisCluster, hdel, arginfo_key_members, ZEND_ACC_PUBLIC)
166 PHP_ME(RedisCluster, hexists, arginfo_key_member, ZEND_ACC_PUBLIC)
167 PHP_ME(RedisCluster, hget, arginfo_key_member, ZEND_ACC_PUBLIC)
168 PHP_ME(RedisCluster, hgetall, arginfo_key, ZEND_ACC_PUBLIC)
169 PHP_ME(RedisCluster, hincrby, arginfo_key_member_value, ZEND_ACC_PUBLIC)
170 PHP_ME(RedisCluster, hincrbyfloat, arginfo_key_member_value, ZEND_ACC_PUBLIC)
171 PHP_ME(RedisCluster, hkeys, arginfo_key, ZEND_ACC_PUBLIC)
172 PHP_ME(RedisCluster, hlen, arginfo_key, ZEND_ACC_PUBLIC)
173 PHP_ME(RedisCluster, hmget, arginfo_hmget, ZEND_ACC_PUBLIC)
174 PHP_ME(RedisCluster, hmset, arginfo_hmset, ZEND_ACC_PUBLIC)
175 PHP_ME(RedisCluster, hscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC)
176 PHP_ME(RedisCluster, hset, arginfo_key_member_value, ZEND_ACC_PUBLIC)
177 PHP_ME(RedisCluster, hsetnx, arginfo_key_member_value, ZEND_ACC_PUBLIC)
178 PHP_ME(RedisCluster, hstrlen, arginfo_key_member, ZEND_ACC_PUBLIC)
179 PHP_ME(RedisCluster, hvals, arginfo_key, ZEND_ACC_PUBLIC)
180 PHP_ME(RedisCluster, incr, arginfo_key, ZEND_ACC_PUBLIC)
181 PHP_ME(RedisCluster, incrby, arginfo_key_value, ZEND_ACC_PUBLIC)
182 PHP_ME(RedisCluster, incrbyfloat, arginfo_key_value, ZEND_ACC_PUBLIC)
183 PHP_ME(RedisCluster, info, arginfo_info, ZEND_ACC_PUBLIC)
184 PHP_ME(RedisCluster, keys, arginfo_keys, ZEND_ACC_PUBLIC)
185 PHP_ME(RedisCluster, lastsave, arginfo_key_or_address, ZEND_ACC_PUBLIC)
186 PHP_ME(RedisCluster, lget, arginfo_lindex, ZEND_ACC_PUBLIC)
187 PHP_ME(RedisCluster, lindex, arginfo_lindex, ZEND_ACC_PUBLIC)
188 PHP_ME(RedisCluster, linsert, arginfo_linsert, ZEND_ACC_PUBLIC)
189 PHP_ME(RedisCluster, llen, arginfo_key, ZEND_ACC_PUBLIC)
190 PHP_ME(RedisCluster, lpop, arginfo_key, ZEND_ACC_PUBLIC)
191 PHP_ME(RedisCluster, lpush, arginfo_key_value, ZEND_ACC_PUBLIC)
192 PHP_ME(RedisCluster, lpushx, arginfo_key_value, ZEND_ACC_PUBLIC)
193 PHP_ME(RedisCluster, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
194 PHP_ME(RedisCluster, lrem, arginfo_key_value, ZEND_ACC_PUBLIC)
195 PHP_ME(RedisCluster, lset, arginfo_lset, ZEND_ACC_PUBLIC)
196 PHP_ME(RedisCluster, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC)
197 PHP_ME(RedisCluster, mget, arginfo_mget, ZEND_ACC_PUBLIC)
198 PHP_ME(RedisCluster, mset, arginfo_pairs, ZEND_ACC_PUBLIC)
199 PHP_ME(RedisCluster, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC)
200 PHP_ME(RedisCluster, multi, arginfo_void, ZEND_ACC_PUBLIC)
201 PHP_ME(RedisCluster, object, arginfo_object, ZEND_ACC_PUBLIC)
202 PHP_ME(RedisCluster, persist, arginfo_key, ZEND_ACC_PUBLIC)
203 PHP_ME(RedisCluster, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
204 PHP_ME(RedisCluster, pexpireat, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
205 PHP_ME(RedisCluster, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC)
206 PHP_ME(RedisCluster, pfcount, arginfo_key, ZEND_ACC_PUBLIC)
207 PHP_ME(RedisCluster, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC)
208 PHP_ME(RedisCluster, ping, arginfo_key_or_address, ZEND_ACC_PUBLIC)
209 PHP_ME(RedisCluster, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
210 PHP_ME(RedisCluster, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC)
211 PHP_ME(RedisCluster, pttl, arginfo_key, ZEND_ACC_PUBLIC)
212 PHP_ME(RedisCluster, publish, arginfo_publish, ZEND_ACC_PUBLIC)
213 PHP_ME(RedisCluster, pubsub, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
214 PHP_ME(RedisCluster, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC)
215 PHP_ME(RedisCluster, randomkey, arginfo_key_or_address, ZEND_ACC_PUBLIC)
216 PHP_ME(RedisCluster, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC)
217 PHP_ME(RedisCluster, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC)
218 PHP_ME(RedisCluster, renamenx, arginfo_key_newkey, ZEND_ACC_PUBLIC)
219 PHP_ME(RedisCluster, restore, arginfo_restore, ZEND_ACC_PUBLIC)
220 PHP_ME(RedisCluster, role, arginfo_void, ZEND_ACC_PUBLIC)
221 PHP_ME(RedisCluster, rpop, arginfo_key, ZEND_ACC_PUBLIC)
222 PHP_ME(RedisCluster, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC)
223 PHP_ME(RedisCluster, rpush, arginfo_key_value, ZEND_ACC_PUBLIC)
224 PHP_ME(RedisCluster, rpushx, arginfo_key_value, ZEND_ACC_PUBLIC)
225 PHP_ME(RedisCluster, sadd, arginfo_key_value, ZEND_ACC_PUBLIC)
226 PHP_ME(RedisCluster, saddarray, arginfo_sadd_array, ZEND_ACC_PUBLIC)
227 PHP_ME(RedisCluster, save, arginfo_key_or_address, ZEND_ACC_PUBLIC)
228 PHP_ME(RedisCluster, scan, arginfo_scan_cl, ZEND_ACC_PUBLIC)
229 PHP_ME(RedisCluster, scard, arginfo_key, ZEND_ACC_PUBLIC)
230 PHP_ME(RedisCluster, script, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
231 PHP_ME(RedisCluster, sdiff, arginfo_nkeys, ZEND_ACC_PUBLIC)
232 PHP_ME(RedisCluster, sdiffstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
233 PHP_ME(RedisCluster, set, arginfo_set, ZEND_ACC_PUBLIC)
234 PHP_ME(RedisCluster, setbit, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
235 PHP_ME(RedisCluster, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
236 PHP_ME(RedisCluster, setnx, arginfo_key_value, ZEND_ACC_PUBLIC)
237 PHP_ME(RedisCluster, setoption, arginfo_setoption, ZEND_ACC_PUBLIC)
238 PHP_ME(RedisCluster, setrange, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
239 PHP_ME(RedisCluster, sinter, arginfo_nkeys, ZEND_ACC_PUBLIC)
240 PHP_ME(RedisCluster, sinterstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
241 PHP_ME(RedisCluster, sismember, arginfo_key_value, ZEND_ACC_PUBLIC)
242 PHP_ME(RedisCluster, slowlog, arginfo_key_or_address_variadic, ZEND_ACC_PUBLIC)
243 PHP_ME(RedisCluster, smembers, arginfo_key, ZEND_ACC_PUBLIC)
244 PHP_ME(RedisCluster, smove, arginfo_smove, ZEND_ACC_PUBLIC)
245 PHP_ME(RedisCluster, sort, arginfo_sort, ZEND_ACC_PUBLIC)
246 PHP_ME(RedisCluster, spop, arginfo_key, ZEND_ACC_PUBLIC)
247 PHP_ME(RedisCluster, srandmember, arginfo_srand_member, ZEND_ACC_PUBLIC)
248 PHP_ME(RedisCluster, srem, arginfo_key_value, ZEND_ACC_PUBLIC)
249 PHP_ME(RedisCluster, sscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC)
250 PHP_ME(RedisCluster, strlen, arginfo_key, ZEND_ACC_PUBLIC)
251 PHP_ME(RedisCluster, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC)
252 PHP_ME(RedisCluster, sunion, arginfo_nkeys, ZEND_ACC_PUBLIC)
253 PHP_ME(RedisCluster, sunionstore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
254 PHP_ME(RedisCluster, time, arginfo_void, ZEND_ACC_PUBLIC)
255 PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC)
256 PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC)
257 PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
258 PHP_ME(RedisCluster, unlink, arginfo_del, ZEND_ACC_PUBLIC)
259 PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
260 PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC)
261 PHP_ME(RedisCluster, xack, arginfo_xack, ZEND_ACC_PUBLIC)
262 PHP_ME(RedisCluster, xadd, arginfo_xadd, ZEND_ACC_PUBLIC)
263 PHP_ME(RedisCluster, xclaim, arginfo_xclaim, ZEND_ACC_PUBLIC)
264 PHP_ME(RedisCluster, xdel, arginfo_xdel, ZEND_ACC_PUBLIC)
265 PHP_ME(RedisCluster, xgroup, arginfo_xgroup, ZEND_ACC_PUBLIC)
266 PHP_ME(RedisCluster, xinfo, arginfo_xinfo, ZEND_ACC_PUBLIC)
267 PHP_ME(RedisCluster, xlen, arginfo_key, ZEND_ACC_PUBLIC)
268 PHP_ME(RedisCluster, xpending, arginfo_xpending, ZEND_ACC_PUBLIC)
269 PHP_ME(RedisCluster, xrange, arginfo_xrange, ZEND_ACC_PUBLIC)
270 PHP_ME(RedisCluster, xread, arginfo_xread, ZEND_ACC_PUBLIC)
271 PHP_ME(RedisCluster, xreadgroup, arginfo_xreadgroup, ZEND_ACC_PUBLIC)
272 PHP_ME(RedisCluster, xrevrange, arginfo_xrange, ZEND_ACC_PUBLIC)
273 PHP_ME(RedisCluster, xtrim, arginfo_xtrim, ZEND_ACC_PUBLIC)
274 PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC)
275 PHP_ME(RedisCluster, zcard, arginfo_key, ZEND_ACC_PUBLIC)
276 PHP_ME(RedisCluster, zcount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
277 PHP_ME(RedisCluster, zincrby, arginfo_zincrby, ZEND_ACC_PUBLIC)
278 PHP_ME(RedisCluster, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
279 PHP_ME(RedisCluster, zlexcount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
280 PHP_ME(RedisCluster, zpopmax, arginfo_key, ZEND_ACC_PUBLIC)
281 PHP_ME(RedisCluster, zpopmin, arginfo_key, ZEND_ACC_PUBLIC)
282 PHP_ME(RedisCluster, zrange, arginfo_zrange, ZEND_ACC_PUBLIC)
283 PHP_ME(RedisCluster, zrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
284 PHP_ME(RedisCluster, zrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
285 PHP_ME(RedisCluster, zrank, arginfo_key_member, ZEND_ACC_PUBLIC)
286 PHP_ME(RedisCluster, zrem, arginfo_key_members, ZEND_ACC_PUBLIC)
287 PHP_ME(RedisCluster, zremrangebylex, arginfo_key_min_max, ZEND_ACC_PUBLIC)
288 PHP_ME(RedisCluster, zremrangebyrank, arginfo_key_min_max, ZEND_ACC_PUBLIC)
289 PHP_ME(RedisCluster, zremrangebyscore, arginfo_key_min_max, ZEND_ACC_PUBLIC)
290 PHP_ME(RedisCluster, zrevrange, arginfo_zrange, ZEND_ACC_PUBLIC)
291 PHP_ME(RedisCluster, zrevrangebylex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
292 PHP_ME(RedisCluster, zrevrangebyscore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
293 PHP_ME(RedisCluster, zrevrank, arginfo_key_member, ZEND_ACC_PUBLIC)
294 PHP_ME(RedisCluster, zscan, arginfo_kscan_cl, ZEND_ACC_PUBLIC)
295 PHP_ME(RedisCluster, zscore, arginfo_key_member, ZEND_ACC_PUBLIC)
296 PHP_ME(RedisCluster, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
297 PHP_FE_END
298 };
299
300 /* Our context seeds will be a hash table with RedisSock* pointers */
ht_free_seed(zval * data)301 static void ht_free_seed(zval *data) {
302 RedisSock *redis_sock = *(RedisSock**)data;
303 if (redis_sock) redis_free_socket(redis_sock);
304 }
305
306 /* Free redisClusterNode objects we've stored */
ht_free_node(zval * data)307 static void ht_free_node(zval *data) {
308 redisClusterNode *node = *(redisClusterNode**)data;
309 cluster_free_node(node);
310 }
311
312 /* Create redisCluster context */
create_cluster_context(zend_class_entry * class_type)313 zend_object * create_cluster_context(zend_class_entry *class_type) {
314 redisCluster *cluster;
315
316 // Allocate our actual struct
317 cluster = ecalloc(1, sizeof(redisCluster) + zend_object_properties_size(class_type));
318
319 // We're not currently subscribed anywhere
320 cluster->subscribed_slot = -1;
321
322 // Allocate our RedisSock we'll use to store prefix/serialization flags
323 cluster->flags = ecalloc(1, sizeof(RedisSock));
324
325 // Allocate our hash table for seeds
326 ALLOC_HASHTABLE(cluster->seeds);
327 zend_hash_init(cluster->seeds, 0, NULL, ht_free_seed, 0);
328
329 // Allocate our hash table for connected Redis objects
330 ALLOC_HASHTABLE(cluster->nodes);
331 zend_hash_init(cluster->nodes, 0, NULL, ht_free_node, 0);
332
333 // Initialize it
334 zend_object_std_init(&cluster->std, class_type);
335
336 object_properties_init(&cluster->std, class_type);
337 memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers));
338 RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std);
339 RedisCluster_handlers.free_obj = free_cluster_context;
340 RedisCluster_handlers.clone_obj = NULL;
341
342 cluster->std.handlers = &RedisCluster_handlers;
343
344 return &cluster->std;
345 }
346
347 /* Free redisCluster context */
free_cluster_context(zend_object * object)348 void free_cluster_context(zend_object *object) {
349 redisCluster *cluster = PHPREDIS_GET_OBJECT(redisCluster, object);
350
351 cluster_free(cluster, 0);
352 zend_object_std_dtor(&cluster->std);
353 }
354
355 /* Take user provided seeds and return unique and valid ones */
356 /* Attempt to connect to a Redis cluster provided seeds and timeout options */
redis_cluster_init(redisCluster * c,HashTable * ht_seeds,double timeout,double read_timeout,int persistent,zend_string * user,zend_string * pass,zval * context)357 static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double timeout,
358 double read_timeout, int persistent, zend_string *user,
359 zend_string *pass, zval *context)
360 {
361 zend_string *hash = NULL, **seeds;
362 redisCachedCluster *cc;
363 uint32_t nseeds;
364 char *err;
365
366 /* Validate our arguments and get a sanitized seed array */
367 seeds = cluster_validate_args(timeout, read_timeout, ht_seeds, &nseeds, &err);
368 if (seeds == NULL) {
369 CLUSTER_THROW_EXCEPTION(err, 0);
370 return;
371 }
372
373 if (user && ZSTR_LEN(user))
374 c->flags->user = zend_string_copy(user);
375 if (pass && ZSTR_LEN(pass))
376 c->flags->pass = zend_string_copy(pass);
377 if (context) {
378 redis_sock_set_stream_context(c->flags, context);
379 }
380
381 c->flags->timeout = timeout;
382 c->flags->read_timeout = read_timeout;
383 c->flags->persistent = persistent;
384 c->waitms = timeout * 1000L;
385
386 /* Attempt to load slots from cache if caching is enabled */
387 if (CLUSTER_CACHING_ENABLED()) {
388 /* Exit early if we can load from cache */
389 hash = cluster_hash_seeds(seeds, nseeds);
390 if ((cc = cluster_cache_load(hash))) {
391 cluster_init_cache(c, cc);
392 goto cleanup;
393 }
394 }
395
396 /* Initialize seeds and attempt to map keyspace */
397 cluster_init_seeds(c, seeds, nseeds);
398 if (cluster_map_keyspace(c) == SUCCESS && hash)
399 cluster_cache_store(hash, c->nodes);
400
401 cleanup:
402 if (hash) zend_string_release(hash);
403 free_seed_array(seeds, nseeds);
404 }
405
406
407 /* Attempt to load a named cluster configured in php.ini */
redis_cluster_load(redisCluster * c,char * name,int name_len)408 void redis_cluster_load(redisCluster *c, char *name, int name_len) {
409 zval z_seeds, z_tmp, *z_value;
410 zend_string *user = NULL, *pass = NULL;
411 double timeout = 0, read_timeout = 0;
412 int persistent = 0;
413 char *iptr;
414 HashTable *ht_seeds = NULL;
415
416 /* Seeds */
417 array_init(&z_seeds);
418 if ((iptr = INI_STR("redis.clusters.seeds")) != NULL) {
419 sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_seeds);
420 }
421 if ((z_value = zend_hash_str_find(Z_ARRVAL(z_seeds), name, name_len)) != NULL) {
422 ht_seeds = Z_ARRVAL_P(z_value);
423 } else {
424 zval_dtor(&z_seeds);
425 CLUSTER_THROW_EXCEPTION("Couldn't find seeds for cluster", 0);
426 return;
427 }
428
429 /* Connection timeout */
430 if ((iptr = INI_STR("redis.clusters.timeout")) != NULL) {
431 array_init(&z_tmp);
432 sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
433 redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &timeout);
434 zval_dtor(&z_tmp);
435 }
436
437 /* Read timeout */
438 if ((iptr = INI_STR("redis.clusters.read_timeout")) != NULL) {
439 array_init(&z_tmp);
440 sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
441 redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &read_timeout);
442 zval_dtor(&z_tmp);
443 }
444
445 /* Persistent connections */
446 if ((iptr = INI_STR("redis.clusters.persistent")) != NULL) {
447 array_init(&z_tmp);
448 sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
449 redis_conf_bool(Z_ARRVAL(z_tmp), name, name_len, &persistent);
450 zval_dtor(&z_tmp);
451 }
452
453 if ((iptr = INI_STR("redis.clusters.auth"))) {
454 array_init(&z_tmp);
455 sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp);
456 redis_conf_auth(Z_ARRVAL(z_tmp), name, name_len, &user, &pass);
457 zval_dtor(&z_tmp);
458 }
459
460 /* Attempt to create/connect to the cluster */
461 redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass, NULL);
462
463 /* Clean up */
464 zval_dtor(&z_seeds);
465 if (user) zend_string_release(user);
466 if (pass) zend_string_release(pass);
467 }
468
469 /*
470 * PHP Methods
471 */
472
473 /* Create a RedisCluster Object */
PHP_METHOD(RedisCluster,__construct)474 PHP_METHOD(RedisCluster, __construct) {
475 zval *object, *z_seeds = NULL, *z_auth = NULL, *context = NULL;
476 zend_string *user = NULL, *pass = NULL;
477 double timeout = 0.0, read_timeout = 0.0;
478 size_t name_len;
479 zend_bool persistent = 0;
480 redisCluster *c = GET_CONTEXT();
481 char *name;
482
483 // Parse arguments
484 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
485 "Os!|addbza!", &object, redis_cluster_ce, &name,
486 &name_len, &z_seeds, &timeout, &read_timeout,
487 &persistent, &z_auth, &context) == FAILURE)
488 {
489 RETURN_FALSE;
490 }
491
492 /* If we've got a string try to load from INI */
493 if (ZEND_NUM_ARGS() < 2) {
494 if (name_len == 0) { // Require a name
495 CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0);
496 }
497 redis_cluster_load(c, name, name_len);
498 return;
499 }
500
501 /* The normal case, loading from arguments */
502 redis_extract_auth_info(z_auth, &user, &pass);
503 redis_cluster_init(c, Z_ARRVAL_P(z_seeds), timeout, read_timeout,
504 persistent, user, pass, context);
505
506 if (user) zend_string_release(user);
507 if (pass) zend_string_release(pass);
508 }
509
510 /*
511 * RedisCluster method implementation
512 */
513
514 /* {{{ proto bool RedisCluster::close() */
PHP_METHOD(RedisCluster,close)515 PHP_METHOD(RedisCluster, close) {
516 cluster_disconnect(GET_CONTEXT(), 1);
517 RETURN_TRUE;
518 }
519
520 /* {{{ proto string RedisCluster::get(string key) */
PHP_METHOD(RedisCluster,get)521 PHP_METHOD(RedisCluster, get) {
522 CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_resp, 1);
523 }
524 /* }}} */
525
526 /* {{{ proto bool RedisCluster::set(string key, string value) */
PHP_METHOD(RedisCluster,set)527 PHP_METHOD(RedisCluster, set) {
528 CLUSTER_PROCESS_CMD(set, cluster_bool_resp, 0);
529 }
530 /* }}} */
531
532 /* Generic handler for MGET/MSET/MSETNX */
533 static int
distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS,redisCluster * c,short slot,clusterMultiCmd * mc,zval * z_ret,int last,cluster_cb cb)534 distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot,
535 clusterMultiCmd *mc, zval *z_ret, int last, cluster_cb cb)
536 {
537 clusterMultiCtx *ctx;
538
539 // Finalize multi command
540 cluster_multi_fini(mc);
541
542 // Spin up multi context
543 ctx = emalloc(sizeof(clusterMultiCtx));
544 ctx->z_multi = z_ret;
545 ctx->count = mc->argc;
546 ctx->last = last;
547
548 // Attempt to send the command
549 if (cluster_send_command(c,slot,mc->cmd.c,mc->cmd.len) < 0 || c->err != NULL) {
550 efree(ctx);
551 return -1;
552 }
553
554 if (CLUSTER_IS_ATOMIC(c)) {
555 // Process response now
556 cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx);
557 } else {
558 CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx);
559 }
560
561 // Clear out our command but retain allocated memory
562 CLUSTER_MULTI_CLEAR(mc);
563
564 return 0;
565 }
566
567 /* Container struct for a key/value pair pulled from an array */
568 typedef struct clusterKeyValHT {
569 char kbuf[22];
570
571 char *key;
572 size_t key_len;
573 int key_free;
574 short slot;
575
576 char *val;
577 size_t val_len;
578 int val_free;
579 } clusterKeyValHT;
580
581 /* Helper to pull a key/value pair from a HashTable */
get_key_val_ht(redisCluster * c,HashTable * ht,HashPosition * ptr,clusterKeyValHT * kv)582 static int get_key_val_ht(redisCluster *c, HashTable *ht, HashPosition *ptr,
583 clusterKeyValHT *kv)
584 {
585 zval *z_val;
586 zend_ulong idx;
587
588 // Grab the key, convert it to a string using provided kbuf buffer if it's
589 // a LONG style key
590 zend_string *zkey;
591 switch (zend_hash_get_current_key_ex(ht, &zkey, &idx, ptr)) {
592 case HASH_KEY_IS_STRING:
593 kv->key_len = ZSTR_LEN(zkey);
594 kv->key = ZSTR_VAL(zkey);
595 break;
596 case HASH_KEY_IS_LONG:
597 kv->key_len = snprintf(kv->kbuf,sizeof(kv->kbuf),"%ld",(long)idx);
598 kv->key = kv->kbuf;
599 break;
600 default:
601 CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0);
602 return -1;
603 }
604
605 // Prefix our key if we need to, set the slot
606 kv->key_free = redis_key_prefix(c->flags, &(kv->key), &(kv->key_len));
607 kv->slot = cluster_hash_key(kv->key, kv->key_len);
608
609 // Now grab our value
610 if ((z_val = zend_hash_get_current_data_ex(ht, ptr)) == NULL) {
611 CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0);
612 return -1;
613 }
614
615 // Serialize our value if required
616 kv->val_free = redis_pack(c->flags,z_val,&(kv->val),&(kv->val_len));
617
618 // Success
619 return 0;
620 }
621
622 /* Helper to pull, prefix, and hash a key from a HashTable value */
get_key_ht(redisCluster * c,HashTable * ht,HashPosition * ptr,clusterKeyValHT * kv)623 static int get_key_ht(redisCluster *c, HashTable *ht, HashPosition *ptr,
624 clusterKeyValHT *kv)
625 {
626 zval *z_key;
627
628 if ((z_key = zend_hash_get_current_data_ex(ht, ptr)) == NULL) {
629 // Shouldn't happen, but check anyway
630 CLUSTER_THROW_EXCEPTION("Internal Zend HashTable error", 0);
631 return -1;
632 }
633
634 // Always want to work with strings
635 convert_to_string(z_key);
636
637 kv->key = Z_STRVAL_P(z_key);
638 kv->key_len = Z_STRLEN_P(z_key);
639 kv->key_free = redis_key_prefix(c->flags, &(kv->key), &(kv->key_len));
640
641 // Hash our key
642 kv->slot = cluster_hash_key(kv->key, kv->key_len);
643
644 // Success
645 return 0;
646 }
647
648 /* Turn variable arguments into a HashTable for processing */
method_args_to_ht(zval * z_args,int argc)649 static HashTable *method_args_to_ht(zval *z_args, int argc) {
650 HashTable *ht_ret;
651 int i;
652
653 /* Allocate our hash table */
654 ALLOC_HASHTABLE(ht_ret);
655 zend_hash_init(ht_ret, argc, NULL, NULL, 0);
656
657 /* Populate our return hash table with our arguments */
658 for (i = 0; i < argc; i++) {
659 zend_hash_next_index_insert(ht_ret, &z_args[i]);
660 }
661
662 /* Return our hash table */
663 return ht_ret;
664 }
665
666 /* Convenience handler for commands that take multiple keys such as
667 * MGET, DEL, and UNLINK */
cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS,char * kw,int kw_len,zval * z_ret,cluster_cb cb)668 static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
669 zval *z_ret, cluster_cb cb)
670 {
671 redisCluster *c = GET_CONTEXT();
672 clusterMultiCmd mc = {0};
673 clusterKeyValHT kv;
674 zval *z_args;
675 HashTable *ht_arr;
676 HashPosition ptr;
677 int i = 1, argc = ZEND_NUM_ARGS(), ht_free = 0;
678 short slot;
679
680 /* If we don't have any arguments we're invalid */
681 if (!argc) return -1;
682
683 /* Extract our arguments into an array */
684 z_args = ecalloc(argc, sizeof(zval));
685 if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
686 efree(z_args);
687 return -1;
688 }
689
690 /* Determine if we're working with a single array or variadic args */
691 if (argc == 1 && Z_TYPE(z_args[0]) == IS_ARRAY) {
692 ht_arr = Z_ARRVAL(z_args[0]);
693 argc = zend_hash_num_elements(ht_arr);
694 if (!argc) {
695 efree(z_args);
696 return -1;
697 }
698 } else {
699 ht_arr = method_args_to_ht(z_args, argc);
700 ht_free = 1;
701 }
702
703 /* MGET is readonly, DEL is not */
704 c->readonly = kw_len == 4 && CLUSTER_IS_ATOMIC(c);
705
706 // Initialize our "multi" command handler with command/len
707 CLUSTER_MULTI_INIT(mc, kw, kw_len);
708
709 // Process the first key outside of our loop, so we don't have to check if
710 // it's the first iteration every time, needlessly
711 zend_hash_internal_pointer_reset_ex(ht_arr, &ptr);
712 if (get_key_ht(c, ht_arr, &ptr, &kv) < 0) {
713 efree(z_args);
714 return -1;
715 }
716
717 // Process our key and add it to the command
718 cluster_multi_add(&mc, kv.key, kv.key_len);
719
720 // Free key if we prefixed
721 if (kv.key_free) efree(kv.key);
722
723 // Move to the next key
724 zend_hash_move_forward_ex(ht_arr, &ptr);
725
726 // Iterate over keys 2...N
727 slot = kv.slot;
728 while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) {
729 if (get_key_ht(c, ht_arr, &ptr, &kv) < 0) {
730 cluster_multi_free(&mc);
731 if (ht_free) {
732 zend_hash_destroy(ht_arr);
733 efree(ht_arr);
734 }
735 efree(z_args);
736 return -1;
737 }
738
739 // If the slots have changed, kick off the keys we've aggregated
740 if (slot != kv.slot) {
741 // Process this batch of MGET keys
742 if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot,
743 &mc, z_ret, i == argc, cb) < 0)
744 {
745 cluster_multi_free(&mc);
746 if (ht_free) {
747 zend_hash_destroy(ht_arr);
748 efree(ht_arr);
749 }
750 efree(z_args);
751 return -1;
752 }
753 }
754
755 // Add this key to the command
756 cluster_multi_add(&mc, kv.key, kv.key_len);
757
758 // Free key if we prefixed
759 if (kv.key_free) efree(kv.key);
760
761 // Update the last slot we encountered, and the key we're on
762 slot = kv.slot;
763 i++;
764
765 zend_hash_move_forward_ex(ht_arr, &ptr);
766 }
767 efree(z_args);
768
769 // If we've got straggler(s) process them
770 if (mc.argc > 0) {
771 if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot,
772 &mc, z_ret, 1, cb) < 0)
773 {
774 cluster_multi_free(&mc);
775 if (ht_free) {
776 zend_hash_destroy(ht_arr);
777 efree(ht_arr);
778 }
779 return -1;
780 }
781 }
782
783 // Free our command
784 cluster_multi_free(&mc);
785
786 /* Clean up our hash table if we constructed it from variadic args */
787 if (ht_free) {
788 zend_hash_destroy(ht_arr);
789 efree(ht_arr);
790 }
791
792 /* Return our object if we're in MULTI mode */
793 if (!CLUSTER_IS_ATOMIC(c))
794 RETVAL_ZVAL(getThis(), 1, 0);
795
796 // Success
797 return 0;
798 }
799
800 /* Handler for both MSET and MSETNX */
cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS,char * kw,int kw_len,zval * z_ret,cluster_cb cb)801 static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
802 zval *z_ret, cluster_cb cb)
803 {
804 redisCluster *c = GET_CONTEXT();
805 clusterKeyValHT kv;
806 clusterMultiCmd mc = {0};
807 zval *z_arr;
808 HashTable *ht_arr;
809 HashPosition ptr;
810 int i = 1, argc;
811 short slot;
812
813 // Parse our arguments
814 if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) {
815 return -1;
816 }
817
818 // No reason to send zero args
819 ht_arr = Z_ARRVAL_P(z_arr);
820 if ((argc = zend_hash_num_elements(ht_arr)) == 0) {
821 return -1;
822 }
823
824 /* This is a write command */
825 c->readonly = 0;
826
827 // Set up our multi command handler
828 CLUSTER_MULTI_INIT(mc, kw, kw_len);
829
830 // Process the first key/value pair outside of our loop
831 zend_hash_internal_pointer_reset_ex(ht_arr, &ptr);
832 if (get_key_val_ht(c, ht_arr, &ptr, &kv) ==-1) return -1;
833 zend_hash_move_forward_ex(ht_arr, &ptr);
834
835 // Add this to our multi cmd, set slot, free key if we prefixed
836 cluster_multi_add(&mc, kv.key, kv.key_len);
837 cluster_multi_add(&mc, kv.val, kv.val_len);
838 if (kv.key_free) efree(kv.key);
839 if (kv.val_free) efree(kv.val);
840
841 // While we've got more keys to set
842 slot = kv.slot;
843 while (zend_hash_has_more_elements_ex(ht_arr, &ptr) ==SUCCESS) {
844 // Pull the next key/value pair
845 if (get_key_val_ht(c, ht_arr, &ptr, &kv) ==-1) {
846 return -1;
847 }
848
849 // If the slots have changed, process responses
850 if (slot != kv.slot) {
851 if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
852 slot, &mc, z_ret, i == argc, cb) < 0)
853 {
854 cluster_multi_free(&mc);
855 return -1;
856 }
857 }
858
859 // Add this key and value to our command
860 cluster_multi_add(&mc, kv.key, kv.key_len);
861 cluster_multi_add(&mc, kv.val, kv.val_len);
862
863 // Free our key and value if we need to
864 if (kv.key_free) efree(kv.key);
865 if (kv.val_free) efree(kv.val);
866
867 // Update our slot, increment position
868 slot = kv.slot;
869 i++;
870
871 // Move on
872 zend_hash_move_forward_ex(ht_arr, &ptr);
873 }
874
875 // If we've got stragglers, process them too
876 if (mc.argc > 0) {
877 if (distcmd_resp_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, slot, &mc,
878 z_ret, 1, cb) < 0)
879 {
880 cluster_multi_free(&mc);
881 return -1;
882 }
883 }
884
885 // Free our command
886 cluster_multi_free(&mc);
887
888 /* Return our object if we're in MULTI mode */
889 if (!CLUSTER_IS_ATOMIC(c))
890 RETVAL_ZVAL(getThis(), 1, 0);
891
892 // Success
893 return 0;
894 }
895
896 /* Generic passthru for DEL and UNLINK which act identically */
cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS,char * kw,int kw_len)897 static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS,
898 char *kw, int kw_len)
899 {
900 zval *z_ret = emalloc(sizeof(*z_ret));
901
902 // Initialize a LONG value to zero for our return
903 ZVAL_LONG(z_ret, 0);
904
905 // Parse args, process
906 if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, kw_len, z_ret,
907 cluster_del_resp) < 0)
908 {
909 efree(z_ret);
910 RETURN_FALSE;
911 }
912 }
913
914 /* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */
PHP_METHOD(RedisCluster,del)915 PHP_METHOD(RedisCluster, del) {
916 cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL") - 1);
917 }
918
919 /* {{{ proto array RedisCluster::unlink(string key1, string key2, ... keyN) */
PHP_METHOD(RedisCluster,unlink)920 PHP_METHOD(RedisCluster, unlink) {
921 cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1);
922 }
923
924 /* {{{ proto array RedisCluster::mget(array keys) */
PHP_METHOD(RedisCluster,mget)925 PHP_METHOD(RedisCluster, mget) {
926 zval *z_ret = emalloc(sizeof(*z_ret));
927
928 array_init(z_ret);
929
930 // Parse args, process
931 if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MGET",
932 sizeof("MGET")-1, z_ret, cluster_mbulk_mget_resp) < 0)
933 {
934 zval_dtor(z_ret);
935 efree(z_ret);
936 RETURN_FALSE;
937 }
938 }
939
940 /* {{{ proto bool RedisCluster::mset(array keyvalues) */
PHP_METHOD(RedisCluster,mset)941 PHP_METHOD(RedisCluster, mset) {
942 zval *z_ret = emalloc(sizeof(*z_ret));
943
944 ZVAL_TRUE(z_ret);
945
946 // Parse args and process. If we get a failure, free zval and return FALSE.
947 if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET",
948 sizeof("MSET")-1, z_ret, cluster_mset_resp) ==-1)
949 {
950 efree(z_ret);
951 RETURN_FALSE;
952 }
953 }
954
955 /* {{{ proto array RedisCluster::msetnx(array keyvalues) */
PHP_METHOD(RedisCluster,msetnx)956 PHP_METHOD(RedisCluster, msetnx) {
957 zval *z_ret = emalloc(sizeof(*z_ret));
958
959 array_init(z_ret);
960
961 // Parse args and process. If we get a failure, free mem and return FALSE
962 if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX",
963 sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp) ==-1)
964 {
965 zval_dtor(z_ret);
966 efree(z_ret);
967 RETURN_FALSE;
968 }
969 }
970 /* }}} */
971
972 /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */
PHP_METHOD(RedisCluster,setex)973 PHP_METHOD(RedisCluster, setex) {
974 CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp, 0);
975 }
976 /* }}} */
977
978 /* {{{ proto bool RedisCluster::psetex(string key, string value, int expiry) */
PHP_METHOD(RedisCluster,psetex)979 PHP_METHOD(RedisCluster, psetex) {
980 CLUSTER_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, cluster_bool_resp, 0);
981 }
982 /* }}} */
983
984 /* {{{ proto bool RedisCluster::setnx(string key, string value) */
PHP_METHOD(RedisCluster,setnx)985 PHP_METHOD(RedisCluster, setnx) {
986 CLUSTER_PROCESS_KW_CMD("SETNX", redis_kv_cmd, cluster_1_resp, 0);
987 }
988 /* }}} */
989
990 /* {{{ proto string RedisCluster::getSet(string key, string value) */
PHP_METHOD(RedisCluster,getset)991 PHP_METHOD(RedisCluster, getset) {
992 CLUSTER_PROCESS_KW_CMD("GETSET", redis_kv_cmd, cluster_bulk_resp, 0);
993 }
994 /* }}} */
995
996 /* {{{ proto int RedisCluster::exists(string key) */
PHP_METHOD(RedisCluster,exists)997 PHP_METHOD(RedisCluster, exists) {
998 CLUSTER_PROCESS_CMD(exists, cluster_long_resp, 1);
999 }
1000 /* }}} */
1001
1002 /* {{{ proto array Redis::keys(string pattern) */
PHP_METHOD(RedisCluster,keys)1003 PHP_METHOD(RedisCluster, keys) {
1004 redisCluster *c = GET_CONTEXT();
1005 redisClusterNode *node;
1006 size_t pat_len;
1007 char *pat, *cmd;
1008 clusterReply *resp;
1009 int i, cmd_len;
1010
1011 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pat, &pat_len)
1012 == FAILURE)
1013 {
1014 RETURN_FALSE;
1015 }
1016
1017 /* Prefix and then build our command */
1018 cmd_len = redis_spprintf(c->flags, NULL, &cmd, "KEYS", "k", pat, pat_len);
1019
1020 array_init(return_value);
1021
1022 /* Treat as readonly */
1023 c->readonly = CLUSTER_IS_ATOMIC(c);
1024
1025 /* Iterate over our known nodes */
1026 ZEND_HASH_FOREACH_PTR(c->nodes, node) {
1027 if (node == NULL) continue;
1028 if (cluster_send_slot(c, node->slot, cmd, cmd_len, TYPE_MULTIBULK
1029 ) < 0)
1030 {
1031 php_error_docref(0, E_ERROR, "Can't send KEYS to %s:%d",
1032 ZSTR_VAL(node->sock->host), node->sock->port);
1033 zval_dtor(return_value);
1034 efree(cmd);
1035 RETURN_FALSE;
1036 }
1037
1038 /* Ensure we can get a response */
1039 resp = cluster_read_resp(c, 0);
1040 if (!resp) {
1041 php_error_docref(0, E_WARNING,
1042 "Can't read response from %s:%d", ZSTR_VAL(node->sock->host),
1043 node->sock->port);
1044 continue;
1045 }
1046
1047 /* Iterate keys, adding to our big array */
1048 for(i = 0; i < resp->elements; i++) {
1049 /* Skip non bulk responses, they should all be bulk */
1050 if (resp->element[i]->type != TYPE_BULK) {
1051 continue;
1052 }
1053
1054 add_next_index_stringl(return_value, resp->element[i]->str,
1055 resp->element[i]->len);
1056 }
1057
1058 /* Free response, don't free data */
1059 cluster_free_reply(resp, 1);
1060 } ZEND_HASH_FOREACH_END();
1061
1062 efree(cmd);
1063 }
1064 /* }}} */
1065
1066 /* {{{ proto int RedisCluster::type(string key) */
PHP_METHOD(RedisCluster,type)1067 PHP_METHOD(RedisCluster, type) {
1068 CLUSTER_PROCESS_KW_CMD("TYPE", redis_key_cmd, cluster_type_resp, 1);
1069 }
1070 /* }}} */
1071
1072 /* {{{ proto string RedisCluster::pop(string key) */
PHP_METHOD(RedisCluster,lpop)1073 PHP_METHOD(RedisCluster, lpop) {
1074 CLUSTER_PROCESS_KW_CMD("LPOP", redis_key_cmd, cluster_bulk_resp, 0);
1075 }
1076 /* }}} */
1077
1078 /* {{{ proto string RedisCluster::rpop(string key) */
PHP_METHOD(RedisCluster,rpop)1079 PHP_METHOD(RedisCluster, rpop) {
1080 CLUSTER_PROCESS_KW_CMD("RPOP", redis_key_cmd, cluster_bulk_resp, 0);
1081 }
1082 /* }}} */
1083
1084 /* {{{ proto bool RedisCluster::lset(string key, long index, string val) */
PHP_METHOD(RedisCluster,lset)1085 PHP_METHOD(RedisCluster, lset) {
1086 CLUSTER_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd, cluster_bool_resp, 0);
1087 }
1088 /* }}} */
1089
1090 /* {{{ proto string RedisCluster::spop(string key) */
PHP_METHOD(RedisCluster,spop)1091 PHP_METHOD(RedisCluster, spop) {
1092 if (ZEND_NUM_ARGS() == 1) {
1093 CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_cmd, cluster_bulk_resp, 0);
1094 } else if (ZEND_NUM_ARGS() == 2) {
1095 CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, cluster_mbulk_resp, 0);
1096 } else {
1097 ZEND_WRONG_PARAM_COUNT();
1098 }
1099 }
1100 /* }}} */
1101
1102 /* {{{ proto string|array RedisCluster::srandmember(string key, [long count]) */
PHP_METHOD(RedisCluster,srandmember)1103 PHP_METHOD(RedisCluster, srandmember) {
1104 redisCluster *c = GET_CONTEXT();
1105 cluster_cb cb;
1106 char *cmd; int cmd_len; short slot;
1107 short have_count;
1108
1109 /* Treat as readonly */
1110 c->readonly = CLUSTER_IS_ATOMIC(c);
1111
1112 if (redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags,
1113 &cmd, &cmd_len, &slot, NULL, &have_count)
1114 == FAILURE)
1115 {
1116 RETURN_FALSE;
1117 }
1118
1119 if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) {
1120 efree(cmd);
1121 RETURN_FALSE;
1122 }
1123
1124 // Clean up command
1125 efree(cmd);
1126
1127 cb = have_count ? cluster_mbulk_resp : cluster_bulk_resp;
1128 if (CLUSTER_IS_ATOMIC(c)) {
1129 cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
1130 } else {
1131 void *ctx = NULL;
1132 CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx);
1133 RETURN_ZVAL(getThis(), 1, 0);
1134 }
1135 }
1136
1137 /* {{{ proto string RedisCluster::strlen(string key) */
PHP_METHOD(RedisCluster,strlen)1138 PHP_METHOD(RedisCluster, strlen) {
1139 CLUSTER_PROCESS_KW_CMD("STRLEN", redis_key_cmd, cluster_long_resp, 1);
1140 }
1141
1142 /* {{{ proto long RedisCluster::lpush(string key, string val1, ... valN) */
PHP_METHOD(RedisCluster,lpush)1143 PHP_METHOD(RedisCluster, lpush) {
1144 CLUSTER_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, cluster_long_resp, 0);
1145 }
1146 /* }}} */
1147
1148 /* {{{ proto long RedisCluster::rpush(string key, string val1, ... valN) */
PHP_METHOD(RedisCluster,rpush)1149 PHP_METHOD(RedisCluster, rpush) {
1150 CLUSTER_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, cluster_long_resp, 0);
1151 }
1152 /* }}} */
1153
1154 /* {{{ proto array RedisCluster::blpop(string key1, ... keyN, long timeout) */
PHP_METHOD(RedisCluster,blpop)1155 PHP_METHOD(RedisCluster, blpop) {
1156 CLUSTER_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, cluster_mbulk_resp, 0);
1157 }
1158 /* }}} */
1159
1160 /* {{{ proto array RedisCluster::brpop(string key1, ... keyN, long timeout */
PHP_METHOD(RedisCluster,brpop)1161 PHP_METHOD(RedisCluster, brpop) {
1162 CLUSTER_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, cluster_mbulk_resp, 0);
1163 }
1164 /* }}} */
1165
1166 /* {{{ proto long RedisCluster::rpushx(string key, mixed value) */
PHP_METHOD(RedisCluster,rpushx)1167 PHP_METHOD(RedisCluster, rpushx) {
1168 CLUSTER_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, cluster_long_resp, 0);
1169 }
1170 /* }}} */
1171
1172 /* {{{ proto long RedisCluster::lpushx(string key, mixed value) */
PHP_METHOD(RedisCluster,lpushx)1173 PHP_METHOD(RedisCluster, lpushx) {
1174 CLUSTER_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, cluster_long_resp, 0);
1175 }
1176 /* }}} */
1177
1178 /* {{{ proto long RedisCluster::linsert(string k,string pos,mix pvt,mix val) */
PHP_METHOD(RedisCluster,linsert)1179 PHP_METHOD(RedisCluster, linsert) {
1180 CLUSTER_PROCESS_CMD(linsert, cluster_long_resp, 0);
1181 }
1182 /* }}} */
1183
1184 /* {{{ proto string RedisCluster::lindex(string key, long index) */
PHP_METHOD(RedisCluster,lindex)1185 PHP_METHOD(RedisCluster, lindex) {
1186 CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp, 0);
1187 }
1188 /* }}} */
1189
1190 /* {{{ proto long RedisCluster::lrem(string key, long count, string val) */
PHP_METHOD(RedisCluster,lrem)1191 PHP_METHOD(RedisCluster, lrem) {
1192 CLUSTER_PROCESS_CMD(lrem, cluster_long_resp, 0);
1193 }
1194 /* }}} */
1195
1196 /* {{{ proto string RedisCluster::rpoplpush(string key, string key) */
PHP_METHOD(RedisCluster,rpoplpush)1197 PHP_METHOD(RedisCluster, rpoplpush) {
1198 CLUSTER_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, cluster_bulk_resp, 0);
1199 }
1200 /* }}} */
1201
1202 /* {{{ proto string RedisCluster::brpoplpush(string key, string key, long tm) */
PHP_METHOD(RedisCluster,brpoplpush)1203 PHP_METHOD(RedisCluster, brpoplpush) {
1204 CLUSTER_PROCESS_CMD(brpoplpush, cluster_bulk_resp, 0);
1205 }
1206 /* }}} */
1207
1208 /* {{{ proto long RedisCluster::llen(string key) */
PHP_METHOD(RedisCluster,llen)1209 PHP_METHOD(RedisCluster, llen) {
1210 CLUSTER_PROCESS_KW_CMD("LLEN", redis_key_cmd, cluster_long_resp, 1);
1211 }
1212 /* }}} */
1213
1214 /* {{{ proto long RedisCluster::scard(string key) */
PHP_METHOD(RedisCluster,scard)1215 PHP_METHOD(RedisCluster, scard) {
1216 CLUSTER_PROCESS_KW_CMD("SCARD", redis_key_cmd, cluster_long_resp, 1);
1217 }
1218 /* }}} */
1219
1220 /* {{{ proto array RedisCluster::smembers(string key) */
PHP_METHOD(RedisCluster,smembers)1221 PHP_METHOD(RedisCluster, smembers) {
1222 CLUSTER_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd, cluster_mbulk_resp, 1);
1223 }
1224 /* }}} */
1225
1226 /* {{{ proto long RedisCluster::sismember(string key) */
PHP_METHOD(RedisCluster,sismember)1227 PHP_METHOD(RedisCluster, sismember) {
1228 CLUSTER_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, cluster_1_resp, 1);
1229 }
1230 /* }}} */
1231
1232 /* {{{ proto long RedisCluster::sadd(string key, string val1 [, ...]) */
PHP_METHOD(RedisCluster,sadd)1233 PHP_METHOD(RedisCluster, sadd) {
1234 CLUSTER_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, cluster_long_resp, 0);
1235 }
1236 /* }}} */
1237
1238 /* {{{ proto long RedisCluster::saddarray(string key, array values) */
PHP_METHOD(RedisCluster,saddarray)1239 PHP_METHOD(RedisCluster, saddarray) {
1240 CLUSTER_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, cluster_long_resp, 0);
1241 }
1242 /* }}} */
1243
1244 /* {{{ proto long RedisCluster::srem(string key, string val1 [, ...]) */
PHP_METHOD(RedisCluster,srem)1245 PHP_METHOD(RedisCluster, srem) {
1246 CLUSTER_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, cluster_long_resp, 0);
1247 }
1248 /* }}} */
1249
1250 /* {{{ proto array RedisCluster::sunion(string key1, ... keyN) */
PHP_METHOD(RedisCluster,sunion)1251 PHP_METHOD(RedisCluster, sunion) {
1252 CLUSTER_PROCESS_CMD(sunion, cluster_mbulk_resp, 0);
1253 }
1254 /* }}} */
1255
1256 /* {{{ proto long RedisCluster::sunionstore(string dst, string k1, ... kN) */
PHP_METHOD(RedisCluster,sunionstore)1257 PHP_METHOD(RedisCluster, sunionstore) {
1258 CLUSTER_PROCESS_CMD(sunionstore, cluster_long_resp, 0);
1259 }
1260 /* }}} */
1261
1262 /* {{{ ptoto array RedisCluster::sinter(string k1, ... kN) */
PHP_METHOD(RedisCluster,sinter)1263 PHP_METHOD(RedisCluster, sinter) {
1264 CLUSTER_PROCESS_CMD(sinter, cluster_mbulk_resp, 0);
1265 }
1266 /* }}} */
1267
1268 /* {{{ ptoto long RedisCluster::sinterstore(string dst, string k1, ... kN) */
PHP_METHOD(RedisCluster,sinterstore)1269 PHP_METHOD(RedisCluster, sinterstore) {
1270 CLUSTER_PROCESS_CMD(sinterstore, cluster_long_resp, 0);
1271 }
1272 /* }}} */
1273
1274 /* {{{ proto array RedisCluster::sdiff(string k1, ... kN) */
PHP_METHOD(RedisCluster,sdiff)1275 PHP_METHOD(RedisCluster, sdiff) {
1276 CLUSTER_PROCESS_CMD(sdiff, cluster_mbulk_resp, 1);
1277 }
1278 /* }}} */
1279
1280 /* {{{ proto long RedisCluster::sdiffstore(string dst, string k1, ... kN) */
PHP_METHOD(RedisCluster,sdiffstore)1281 PHP_METHOD(RedisCluster, sdiffstore) {
1282 CLUSTER_PROCESS_CMD(sdiffstore, cluster_long_resp, 0);
1283 }
1284 /* }}} */
1285
1286 /* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */
PHP_METHOD(RedisCluster,smove)1287 PHP_METHOD(RedisCluster, smove) {
1288 CLUSTER_PROCESS_CMD(smove, cluster_1_resp, 0);
1289 }
1290 /* }}} */
1291
1292 /* {{{ proto bool RedisCluster::persist(string key) */
PHP_METHOD(RedisCluster,persist)1293 PHP_METHOD(RedisCluster, persist) {
1294 CLUSTER_PROCESS_KW_CMD("PERSIST", redis_key_cmd, cluster_1_resp, 0);
1295 }
1296 /* }}} */
1297
1298 /* {{{ proto long RedisCluster::ttl(string key) */
PHP_METHOD(RedisCluster,ttl)1299 PHP_METHOD(RedisCluster, ttl) {
1300 CLUSTER_PROCESS_KW_CMD("TTL", redis_key_cmd, cluster_long_resp, 1);
1301 }
1302 /* }}} */
1303
1304 /* {{{ proto long RedisCluster::pttl(string key) */
PHP_METHOD(RedisCluster,pttl)1305 PHP_METHOD(RedisCluster, pttl) {
1306 CLUSTER_PROCESS_KW_CMD("PTTL", redis_key_cmd, cluster_long_resp, 1);
1307 }
1308 /* }}} */
1309
1310 /* {{{ proto long RedisCluster::zcard(string key) */
PHP_METHOD(RedisCluster,zcard)1311 PHP_METHOD(RedisCluster, zcard) {
1312 CLUSTER_PROCESS_KW_CMD("ZCARD", redis_key_cmd, cluster_long_resp, 1);
1313 }
1314 /* }}} */
1315
1316 /* {{{ proto double RedisCluster::zscore(string key) */
PHP_METHOD(RedisCluster,zscore)1317 PHP_METHOD(RedisCluster, zscore) {
1318 CLUSTER_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd, cluster_dbl_resp, 1);
1319 }
1320 /* }}} */
1321
1322 /* {{{ proto long RedisCluster::zadd(string key,double score,string mem, ...) */
PHP_METHOD(RedisCluster,zadd)1323 PHP_METHOD(RedisCluster, zadd) {
1324 CLUSTER_PROCESS_CMD(zadd, cluster_long_resp, 0);
1325 }
1326 /* }}} */
1327
1328 /* {{{ proto double RedisCluster::zincrby(string key, double by, string mem) */
PHP_METHOD(RedisCluster,zincrby)1329 PHP_METHOD(RedisCluster, zincrby) {
1330 CLUSTER_PROCESS_CMD(zincrby, cluster_dbl_resp, 0);
1331 }
1332 /* }}} */
1333
1334 /* {{{ proto RedisCluster::zremrangebyscore(string k, string s, string e) */
PHP_METHOD(RedisCluster,zremrangebyscore)1335 PHP_METHOD(RedisCluster, zremrangebyscore) {
1336 CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd,
1337 cluster_long_resp, 0);
1338 }
1339 /* }}} */
1340
1341 /* {{{ proto RedisCluster::zcount(string key, string s, string e) */
PHP_METHOD(RedisCluster,zcount)1342 PHP_METHOD(RedisCluster, zcount) {
1343 CLUSTER_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, cluster_long_resp, 1);
1344 }
1345 /* }}} */
1346
1347 /* {{{ proto long RedisCluster::zrank(string key, mixed member) */
PHP_METHOD(RedisCluster,zrank)1348 PHP_METHOD(RedisCluster, zrank) {
1349 CLUSTER_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, cluster_long_resp, 1);
1350 }
1351 /* }}} */
1352
1353 /* {{{ proto long RedisCluster::zrevrank(string key, mixed member) */
PHP_METHOD(RedisCluster,zrevrank)1354 PHP_METHOD(RedisCluster, zrevrank) {
1355 CLUSTER_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, cluster_long_resp, 1);
1356 }
1357 /* }}} */
1358
1359 /* {{{ proto long RedisCluster::hlen(string key) */
PHP_METHOD(RedisCluster,hlen)1360 PHP_METHOD(RedisCluster, hlen) {
1361 CLUSTER_PROCESS_KW_CMD("HLEN", redis_key_cmd, cluster_long_resp, 1);
1362 }
1363 /* }}} */
1364
1365 /* {{{ proto array RedisCluster::hkeys(string key) */
PHP_METHOD(RedisCluster,hkeys)1366 PHP_METHOD(RedisCluster, hkeys) {
1367 CLUSTER_PROCESS_KW_CMD("HKEYS", redis_key_cmd, cluster_mbulk_raw_resp, 1);
1368 }
1369 /* }}} */
1370
1371 /* {{{ proto array RedisCluster::hvals(string key) */
PHP_METHOD(RedisCluster,hvals)1372 PHP_METHOD(RedisCluster, hvals) {
1373 CLUSTER_PROCESS_KW_CMD("HVALS", redis_key_cmd, cluster_mbulk_resp, 1);
1374 }
1375 /* }}} */
1376
1377 /* {{{ proto string RedisCluster::hget(string key, string mem) */
PHP_METHOD(RedisCluster,hget)1378 PHP_METHOD(RedisCluster, hget) {
1379 CLUSTER_PROCESS_KW_CMD("HGET", redis_key_str_cmd, cluster_bulk_resp, 1);
1380 }
1381 /* }}} */
1382
1383 /* {{{ proto bool RedisCluster::hset(string key, string mem, string val) */
PHP_METHOD(RedisCluster,hset)1384 PHP_METHOD(RedisCluster, hset) {
1385 CLUSTER_PROCESS_CMD(hset, cluster_long_resp, 0);
1386 }
1387 /* }}} */
1388
1389 /* {{{ proto bool RedisCluster::hsetnx(string key, string mem, string val) */
PHP_METHOD(RedisCluster,hsetnx)1390 PHP_METHOD(RedisCluster, hsetnx) {
1391 CLUSTER_PROCESS_CMD(hsetnx, cluster_1_resp, 0);
1392 }
1393 /* }}} */
1394
1395 /* {{{ proto array RedisCluster::hgetall(string key) */
PHP_METHOD(RedisCluster,hgetall)1396 PHP_METHOD(RedisCluster, hgetall) {
1397 CLUSTER_PROCESS_KW_CMD("HGETALL", redis_key_cmd,
1398 cluster_mbulk_zipstr_resp, 1);
1399 }
1400 /* }}} */
1401
1402 /* {{{ proto bool RedisCluster::hexists(string key, string member) */
PHP_METHOD(RedisCluster,hexists)1403 PHP_METHOD(RedisCluster, hexists) {
1404 CLUSTER_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, cluster_1_resp, 1);
1405 }
1406 /* }}} */
1407
1408 /* {{{ proto long RedisCluster::hincr(string key, string mem, long val) */
PHP_METHOD(RedisCluster,hincrby)1409 PHP_METHOD(RedisCluster, hincrby) {
1410 CLUSTER_PROCESS_CMD(hincrby, cluster_long_resp, 0);
1411 }
1412 /* }}} */
1413
1414 /* {{{ proto double RedisCluster::hincrbyfloat(string k, string m, double v) */
PHP_METHOD(RedisCluster,hincrbyfloat)1415 PHP_METHOD(RedisCluster, hincrbyfloat) {
1416 CLUSTER_PROCESS_CMD(hincrbyfloat, cluster_dbl_resp, 0);
1417 }
1418 /* }}} */
1419
1420 /* {{{ proto bool RedisCluster::hmset(string key, array key_vals) */
PHP_METHOD(RedisCluster,hmset)1421 PHP_METHOD(RedisCluster, hmset) {
1422 CLUSTER_PROCESS_CMD(hmset, cluster_bool_resp, 0);
1423 }
1424 /* }}} */
1425
1426 /* {{{ proto long RedisCluster::hdel(string key, string mem1, ... memN) */
PHP_METHOD(RedisCluster,hdel)1427 PHP_METHOD(RedisCluster, hdel) {
1428 CLUSTER_PROCESS_CMD(hdel, cluster_long_resp, 0);
1429 }
1430 /* }}} */
1431
1432 /* {{{ proto array RedisCluster::hmget(string key, array members) */
PHP_METHOD(RedisCluster,hmget)1433 PHP_METHOD(RedisCluster, hmget) {
1434 CLUSTER_PROCESS_CMD(hmget, cluster_mbulk_assoc_resp, 1);
1435 }
1436 /* }}} */
1437
1438 /* {{{ proto array RedisCluster::hstrlen(string key, string field) */
PHP_METHOD(RedisCluster,hstrlen)1439 PHP_METHOD(RedisCluster, hstrlen) {
1440 CLUSTER_PROCESS_CMD(hstrlen, cluster_long_resp, 1);
1441 }
1442 /* }}} */
1443
1444
1445 /* {{{ proto string RedisCluster::dump(string key) */
PHP_METHOD(RedisCluster,dump)1446 PHP_METHOD(RedisCluster, dump) {
1447 CLUSTER_PROCESS_KW_CMD("DUMP", redis_key_cmd, cluster_bulk_raw_resp, 1);
1448 }
1449
1450 /* {{{ proto long RedisCluster::incr(string key) */
PHP_METHOD(RedisCluster,incr)1451 PHP_METHOD(RedisCluster, incr) {
1452 CLUSTER_PROCESS_CMD(incr, cluster_long_resp, 0);
1453 }
1454 /* }}} */
1455
1456 /* {{{ proto long RedisCluster::incrby(string key, long byval) */
PHP_METHOD(RedisCluster,incrby)1457 PHP_METHOD(RedisCluster, incrby) {
1458 CLUSTER_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, cluster_long_resp, 0);
1459 }
1460 /* }}} */
1461
1462 /* {{{ proto long RedisCluster::decr(string key) */
PHP_METHOD(RedisCluster,decr)1463 PHP_METHOD(RedisCluster, decr) {
1464 CLUSTER_PROCESS_CMD(decr, cluster_long_resp, 0);
1465 }
1466 /* }}} */
1467
1468 /* {{{ proto long RedisCluster::decrby(string key, long byval) */
PHP_METHOD(RedisCluster,decrby)1469 PHP_METHOD(RedisCluster, decrby) {
1470 CLUSTER_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, cluster_long_resp, 0);
1471 }
1472 /* }}} */
1473
1474 /* {{{ proto double RedisCluster::incrbyfloat(string key, double val) */
PHP_METHOD(RedisCluster,incrbyfloat)1475 PHP_METHOD(RedisCluster, incrbyfloat) {
1476 CLUSTER_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd,
1477 cluster_dbl_resp, 0);
1478 }
1479 /* }}} */
1480
1481 /* {{{ proto double RedisCluster::decrbyfloat(string key, double val) */
PHP_METHOD(RedisCluster,decrbyfloat)1482 PHP_METHOD(RedisCluster, decrbyfloat) {
1483 CLUSTER_PROCESS_KW_CMD("DECRBYFLOAT", redis_key_dbl_cmd,
1484 cluster_dbl_resp, 0);
1485 }
1486 /* }}} */
1487
1488 /* {{{ proto bool RedisCluster::expire(string key, long sec) */
PHP_METHOD(RedisCluster,expire)1489 PHP_METHOD(RedisCluster, expire) {
1490 CLUSTER_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, cluster_1_resp, 0);
1491 }
1492 /* }}} */
1493
1494 /* {{{ proto bool RedisCluster::expireat(string key, long ts) */
PHP_METHOD(RedisCluster,expireat)1495 PHP_METHOD(RedisCluster, expireat) {
1496 CLUSTER_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, cluster_1_resp, 0);
1497 }
1498
1499 /* {{{ proto bool RedisCluster::pexpire(string key, long ms) */
PHP_METHOD(RedisCluster,pexpire)1500 PHP_METHOD(RedisCluster, pexpire) {
1501 CLUSTER_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, cluster_1_resp, 0);
1502 }
1503 /* }}} */
1504
1505 /* {{{ proto bool RedisCluster::pexpireat(string key, long ts) */
PHP_METHOD(RedisCluster,pexpireat)1506 PHP_METHOD(RedisCluster, pexpireat) {
1507 CLUSTER_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, cluster_1_resp, 0);
1508 }
1509 /* }}} */
1510
1511 /* {{{ proto long RedisCluster::append(string key, string val) */
PHP_METHOD(RedisCluster,append)1512 PHP_METHOD(RedisCluster, append) {
1513 CLUSTER_PROCESS_KW_CMD("APPEND", redis_kv_cmd, cluster_long_resp, 0);
1514 }
1515 /* }}} */
1516
1517 /* {{{ proto long RedisCluster::getbit(string key, long val) */
PHP_METHOD(RedisCluster,getbit)1518 PHP_METHOD(RedisCluster, getbit) {
1519 CLUSTER_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, cluster_long_resp, 1);
1520 }
1521 /* }}} */
1522
1523 /* {{{ proto long RedisCluster::setbit(string key, long offset, bool onoff) */
PHP_METHOD(RedisCluster,setbit)1524 PHP_METHOD(RedisCluster, setbit) {
1525 CLUSTER_PROCESS_CMD(setbit, cluster_long_resp, 0);
1526 }
1527
1528 /* {{{ proto long RedisCluster::bitop(string op,string key,[string key2,...]) */
PHP_METHOD(RedisCluster,bitop)1529 PHP_METHOD(RedisCluster, bitop)
1530 {
1531 CLUSTER_PROCESS_CMD(bitop, cluster_long_resp, 0);
1532 }
1533 /* }}} */
1534
1535 /* {{{ proto long RedisCluster::bitcount(string key, [int start, int end]) */
PHP_METHOD(RedisCluster,bitcount)1536 PHP_METHOD(RedisCluster, bitcount) {
1537 CLUSTER_PROCESS_CMD(bitcount, cluster_long_resp, 1);
1538 }
1539 /* }}} */
1540
1541 /* {{{ proto long RedisCluster::bitpos(string key, int bit, [int s, int end]) */
PHP_METHOD(RedisCluster,bitpos)1542 PHP_METHOD(RedisCluster, bitpos) {
1543 CLUSTER_PROCESS_CMD(bitpos, cluster_long_resp, 1);
1544 }
1545 /* }}} */
1546
1547 /* {{{ proto string Redis::lget(string key, long index) */
PHP_METHOD(RedisCluster,lget)1548 PHP_METHOD(RedisCluster, lget) {
1549 CLUSTER_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, cluster_bulk_resp, 1);
1550 }
1551 /* }}} */
1552
1553 /* {{{ proto string RedisCluster::getrange(string key, long start, long end) */
PHP_METHOD(RedisCluster,getrange)1554 PHP_METHOD(RedisCluster, getrange) {
1555 CLUSTER_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd,
1556 cluster_bulk_resp, 1);
1557 }
1558 /* }}} */
1559
1560 /* {{{ proto string RedisCluster::ltrim(string key, long start, long end) */
PHP_METHOD(RedisCluster,ltrim)1561 PHP_METHOD(RedisCluster, ltrim) {
1562 CLUSTER_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd, cluster_bool_resp, 0);
1563 }
1564 /* }}} */
1565
1566 /* {{{ proto array RedisCluster::lrange(string key, long start, long end) */
PHP_METHOD(RedisCluster,lrange)1567 PHP_METHOD(RedisCluster, lrange) {
1568 CLUSTER_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd,
1569 cluster_mbulk_resp, 1);
1570 }
1571 /* }}} */
1572
1573 /* {{{ proto long RedisCluster::zremrangebyrank(string k, long s, long e) */
PHP_METHOD(RedisCluster,zremrangebyrank)1574 PHP_METHOD(RedisCluster, zremrangebyrank) {
1575 CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd,
1576 cluster_long_resp, 0);
1577 }
1578 /* }}} */
1579
1580 /* {{{ proto long RedisCluster::publish(string key, string msg) */
PHP_METHOD(RedisCluster,publish)1581 PHP_METHOD(RedisCluster, publish) {
1582 CLUSTER_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, cluster_long_resp, 0);
1583 }
1584 /* }}} */
1585
1586 /* {{{ proto bool RedisCluster::rename(string key1, string key2) */
PHP_METHOD(RedisCluster,rename)1587 PHP_METHOD(RedisCluster, rename) {
1588 CLUSTER_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, cluster_bool_resp, 0);
1589 }
1590 /* }}} */
1591
1592 /* {{{ proto bool RedisCluster::renamenx(string key1, string key2) */
PHP_METHOD(RedisCluster,renamenx)1593 PHP_METHOD(RedisCluster, renamenx) {
1594 CLUSTER_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, cluster_1_resp, 0);
1595 }
1596 /* }}} */
1597
1598 /* {{{ proto long RedisCluster::pfcount(string key) */
PHP_METHOD(RedisCluster,pfcount)1599 PHP_METHOD(RedisCluster, pfcount) {
1600 CLUSTER_PROCESS_CMD(pfcount, cluster_long_resp, 1);
1601 }
1602 /* }}} */
1603
1604 /* {{{ proto bool RedisCluster::pfadd(string key, array vals) */
PHP_METHOD(RedisCluster,pfadd)1605 PHP_METHOD(RedisCluster, pfadd) {
1606 CLUSTER_PROCESS_CMD(pfadd, cluster_1_resp, 0);
1607 }
1608 /* }}} */
1609
1610 /* {{{ proto bool RedisCluster::pfmerge(string key, array keys) */
PHP_METHOD(RedisCluster,pfmerge)1611 PHP_METHOD(RedisCluster, pfmerge) {
1612 CLUSTER_PROCESS_CMD(pfmerge, cluster_bool_resp, 0);
1613 }
1614 /* }}} */
1615
1616 /* {{{ proto boolean RedisCluster::restore(string key, long ttl, string val) */
PHP_METHOD(RedisCluster,restore)1617 PHP_METHOD(RedisCluster, restore) {
1618 CLUSTER_PROCESS_KW_CMD("RESTORE", redis_key_long_str_cmd,
1619 cluster_bool_resp, 0);
1620 }
1621 /* }}} */
1622
1623 /* {{{ proto long RedisCluster::setrange(string key, long offset, string val) */
PHP_METHOD(RedisCluster,setrange)1624 PHP_METHOD(RedisCluster, setrange) {
1625 CLUSTER_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd,
1626 cluster_long_resp, 0);
1627 }
1628 /* }}} */
1629
1630 /* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZREVRANGEBYSCORE */
generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS,char * kw,zrange_cb fun)1631 static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw,
1632 zrange_cb fun)
1633 {
1634 redisCluster *c = GET_CONTEXT();
1635 c->readonly = CLUSTER_IS_ATOMIC(c);
1636 cluster_cb cb;
1637 char *cmd; int cmd_len; short slot;
1638 int withscores = 0;
1639
1640 if (fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,
1641 &withscores, &slot, NULL) == FAILURE)
1642 {
1643 efree(cmd);
1644 RETURN_FALSE;
1645 }
1646
1647 if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) {
1648 efree(cmd);
1649 RETURN_FALSE;
1650 }
1651
1652 efree(cmd);
1653
1654 cb = withscores ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp;
1655 if (CLUSTER_IS_ATOMIC(c)) {
1656 cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
1657 } else {
1658 void *ctx = NULL;
1659 CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx);
1660 RETURN_ZVAL(getThis(), 1, 0);
1661 }
1662 }
1663
1664 /* {{{ proto
1665 * array RedisCluster::zrange(string k, long s, long e, bool score = 0) */
PHP_METHOD(RedisCluster,zrange)1666 PHP_METHOD(RedisCluster, zrange) {
1667 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE",
1668 redis_zrange_cmd);
1669 }
1670 /* }}} */
1671
1672 /* {{{ proto
1673 * array RedisCluster::zrevrange(string k,long s,long e,bool scores = 0) */
PHP_METHOD(RedisCluster,zrevrange)1674 PHP_METHOD(RedisCluster, zrevrange) {
1675 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE",
1676 redis_zrange_cmd);
1677 }
1678 /* }}} */
1679
1680 /* {{{ proto array
1681 * RedisCluster::zrangebyscore(string k, long s, long e, array opts) */
PHP_METHOD(RedisCluster,zrangebyscore)1682 PHP_METHOD(RedisCluster, zrangebyscore) {
1683 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE",
1684 redis_zrangebyscore_cmd);
1685 }
1686 /* }}} */
1687
1688 /* {{{ proto RedisCluster::zunionstore(string dst, array keys, [array weights,
1689 * string agg]) */
PHP_METHOD(RedisCluster,zunionstore)1690 PHP_METHOD(RedisCluster, zunionstore) {
1691 CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, cluster_long_resp, 0);
1692 }
1693 /* }}} */
1694
1695 /* {{{ proto RedisCluster::zinterstore(string dst, array keys, [array weights,
1696 * string agg]) */
PHP_METHOD(RedisCluster,zinterstore)1697 PHP_METHOD(RedisCluster, zinterstore) {
1698 CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, cluster_long_resp, 0);
1699 }
1700 /* }}} */
1701
1702 /* {{{ proto RedisCluster::zrem(string key, string val1, ... valN) */
PHP_METHOD(RedisCluster,zrem)1703 PHP_METHOD(RedisCluster, zrem) {
1704 CLUSTER_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, cluster_long_resp, 0);
1705 }
1706 /* }}} */
1707
1708 /* {{{ proto array
1709 * RedisCluster::zrevrangebyscore(string k, long s, long e, array opts) */
PHP_METHOD(RedisCluster,zrevrangebyscore)1710 PHP_METHOD(RedisCluster, zrevrangebyscore) {
1711 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE",
1712 redis_zrangebyscore_cmd);
1713 }
1714 /* }}} */
1715
1716 /* {{{ proto array RedisCluster::zrangebylex(string key, string min, string max,
1717 * [offset, count]) */
PHP_METHOD(RedisCluster,zrangebylex)1718 PHP_METHOD(RedisCluster, zrangebylex) {
1719 CLUSTER_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd,
1720 cluster_mbulk_resp, 1);
1721 }
1722 /* }}} */
1723
1724 /* {{{ proto array RedisCluster::zrevrangebylex(string key, string min,
1725 * string min, [long off, long limit) */
PHP_METHOD(RedisCluster,zrevrangebylex)1726 PHP_METHOD(RedisCluster, zrevrangebylex) {
1727 CLUSTER_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd,
1728 cluster_mbulk_resp, 1);
1729 }
1730 /* }}} */
1731
1732 /* {{{ proto long RedisCluster::zlexcount(string key, string min, string max) */
PHP_METHOD(RedisCluster,zlexcount)1733 PHP_METHOD(RedisCluster, zlexcount) {
1734 CLUSTER_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, cluster_long_resp, 1);
1735 }
1736 /* }}} */
1737
1738 /* {{{ proto long RedisCluster::zremrangebylex(string key, string min, string max) */
PHP_METHOD(RedisCluster,zremrangebylex)1739 PHP_METHOD(RedisCluster, zremrangebylex) {
1740 CLUSTER_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd,
1741 cluster_long_resp, 0);
1742 }
1743 /* }}} */
1744
1745 /* {{{ proto array RedisCluster::zpopmax(string key) */
PHP_METHOD(RedisCluster,zpopmax)1746 PHP_METHOD(RedisCluster, zpopmax) {
1747 if (ZEND_NUM_ARGS() == 1) {
1748 CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, cluster_mbulk_zipdbl_resp, 0);
1749 } else if (ZEND_NUM_ARGS() == 2) {
1750 CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0);
1751 } else {
1752 ZEND_WRONG_PARAM_COUNT();
1753 }
1754 }
1755 /* }}} */
1756
1757 /* {{{ proto array RedisCluster::zpopmin(string key) */
PHP_METHOD(RedisCluster,zpopmin)1758 PHP_METHOD(RedisCluster, zpopmin) {
1759 if (ZEND_NUM_ARGS() == 1) {
1760 CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, cluster_mbulk_zipdbl_resp, 0);
1761 } else if (ZEND_NUM_ARGS() == 2) {
1762 CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0);
1763 } else {
1764 ZEND_WRONG_PARAM_COUNT();
1765 }
1766 }
1767 /* }}} */
1768
1769 /* {{{ proto array RedisCluster::bzPopMin(Array keys [, timeout]) }}} */
PHP_METHOD(RedisCluster,bzpopmax)1770 PHP_METHOD(RedisCluster, bzpopmax) {
1771 CLUSTER_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, cluster_mbulk_resp, 0);
1772 }
1773
1774 /* {{{ proto array RedisCluster::bzPopMax(Array keys [, timeout]) }}} */
PHP_METHOD(RedisCluster,bzpopmin)1775 PHP_METHOD(RedisCluster, bzpopmin) {
1776 CLUSTER_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, cluster_mbulk_resp, 0);
1777 }
1778
1779 /* {{{ proto RedisCluster::sort(string key, array options) */
PHP_METHOD(RedisCluster,sort)1780 PHP_METHOD(RedisCluster, sort) {
1781 redisCluster *c = GET_CONTEXT();
1782 char *cmd; int cmd_len, have_store; short slot;
1783
1784 if (redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &have_store,
1785 &cmd, &cmd_len, &slot, NULL) == FAILURE)
1786 {
1787 RETURN_FALSE;
1788 }
1789
1790 if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) {
1791 efree(cmd);
1792 RETURN_FALSE;
1793 }
1794
1795 efree(cmd);
1796
1797 // Response type differs based on presence of STORE argument
1798 if (!have_store) {
1799 cluster_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
1800 } else {
1801 cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
1802 }
1803 }
1804
1805 /* {{{ proto RedisCluster::object(string subcmd, string key) */
PHP_METHOD(RedisCluster,object)1806 PHP_METHOD(RedisCluster, object) {
1807 redisCluster *c = GET_CONTEXT();
1808 char *cmd; int cmd_len; short slot;
1809 REDIS_REPLY_TYPE rtype;
1810
1811 if (redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &rtype,
1812 &cmd, &cmd_len, &slot, NULL) == FAILURE)
1813 {
1814 RETURN_FALSE;
1815 }
1816
1817 if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) {
1818 efree(cmd);
1819 RETURN_FALSE;
1820 }
1821
1822 efree(cmd);
1823
1824 // Use the correct response type
1825 if (rtype == TYPE_INT) {
1826 cluster_long_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
1827 } else {
1828 cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
1829 }
1830 }
1831
1832 /* {{{ proto null RedisCluster::subscribe(array chans, callable cb) */
PHP_METHOD(RedisCluster,subscribe)1833 PHP_METHOD(RedisCluster, subscribe) {
1834 CLUSTER_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp, 0);
1835 }
1836 /* }}} */
1837
1838 /* {{{ proto null RedisCluster::psubscribe(array pats, callable cb) */
PHP_METHOD(RedisCluster,psubscribe)1839 PHP_METHOD(RedisCluster, psubscribe) {
1840 CLUSTER_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, cluster_sub_resp, 0);
1841 }
1842 /* }}} */
1843
generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS,redisCluster * c,char * kw)1844 static void generic_unsub_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
1845 char *kw)
1846 {
1847 char *cmd;
1848 int cmd_len;
1849 void *ctx;
1850 short slot;
1851
1852 // There is not reason to unsubscribe outside of a subscribe loop
1853 if (c->subscribed_slot == -1) {
1854 php_error_docref(0, E_WARNING,
1855 "You can't unsubscribe outside of a subscribe loop");
1856 RETURN_FALSE;
1857 }
1858
1859 // Call directly because we're going to set the slot manually
1860 if (redis_unsubscribe_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw,
1861 &cmd, &cmd_len, &slot, &ctx)
1862 == FAILURE)
1863 {
1864 RETURN_FALSE;
1865 }
1866
1867 // This has to operate on our subscribe slot
1868 if (cluster_send_slot(c, c->subscribed_slot, cmd, cmd_len, TYPE_MULTIBULK
1869 ) == FAILURE)
1870 {
1871 CLUSTER_THROW_EXCEPTION("Failed to UNSUBSCRIBE within our subscribe loop!", 0);
1872 RETURN_FALSE;
1873 }
1874
1875 // Now process response from the slot we're subscribed on
1876 cluster_unsub_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx);
1877
1878 // Cleanup our command
1879 efree(cmd);
1880 }
1881
1882 /* {{{ proto array RedisCluster::unsubscribe(array chans) */
PHP_METHOD(RedisCluster,unsubscribe)1883 PHP_METHOD(RedisCluster, unsubscribe) {
1884 generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(),
1885 "UNSUBSCRIBE");
1886 }
1887 /* }}} */
1888
1889 /* {{{ proto array RedisCluster::punsubscribe(array pats) */
PHP_METHOD(RedisCluster,punsubscribe)1890 PHP_METHOD(RedisCluster, punsubscribe) {
1891 generic_unsub_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(),
1892 "PUNSUBSCRIBE");
1893 }
1894 /* }}} */
1895
1896 /* {{{ proto mixed RedisCluster::eval(string script, [array args, int numkeys) */
PHP_METHOD(RedisCluster,eval)1897 PHP_METHOD(RedisCluster, eval) {
1898 CLUSTER_PROCESS_KW_CMD("EVAL", redis_eval_cmd, cluster_variant_raw_resp, 0);
1899 }
1900 /* }}} */
1901
1902 /* {{{ proto mixed RedisCluster::evalsha(string sha, [array args, int numkeys]) */
PHP_METHOD(RedisCluster,evalsha)1903 PHP_METHOD(RedisCluster, evalsha) {
1904 CLUSTER_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, cluster_variant_raw_resp, 0);
1905 }
1906 /* }}} */
1907
1908 /* Commands that do not interact with Redis, but just report stuff about
1909 * various options, etc */
1910
1911 /* {{{ proto string RedisCluster::getmode() */
PHP_METHOD(RedisCluster,getmode)1912 PHP_METHOD(RedisCluster, getmode) {
1913 redisCluster *c = GET_CONTEXT();
1914 RETURN_LONG(c->flags->mode);
1915 }
1916 /* }}} */
1917
1918 /* {{{ proto string RedisCluster::getlasterror() */
PHP_METHOD(RedisCluster,getlasterror)1919 PHP_METHOD(RedisCluster, getlasterror) {
1920 redisCluster *c = GET_CONTEXT();
1921
1922 if (c->err) {
1923 RETURN_STRINGL(ZSTR_VAL(c->err), ZSTR_LEN(c->err));
1924 }
1925 RETURN_NULL();
1926 }
1927 /* }}} */
1928
1929 /* {{{ proto bool RedisCluster::clearlasterror() */
PHP_METHOD(RedisCluster,clearlasterror)1930 PHP_METHOD(RedisCluster, clearlasterror) {
1931 redisCluster *c = GET_CONTEXT();
1932
1933 if (c->err) {
1934 zend_string_release(c->err);
1935 c->err = NULL;
1936 }
1937
1938 RETURN_TRUE;
1939 }
1940 /* }}} */
1941
1942 /* {{{ proto long RedisCluster::getOption(long option */
PHP_METHOD(RedisCluster,getoption)1943 PHP_METHOD(RedisCluster, getoption) {
1944 redisCluster *c = GET_CONTEXT();
1945 redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, c);
1946 }
1947 /* }}} */
1948
1949 /* {{{ proto bool RedisCluster::setOption(long option, mixed value) */
PHP_METHOD(RedisCluster,setoption)1950 PHP_METHOD(RedisCluster, setoption) {
1951 redisCluster *c = GET_CONTEXT();
1952 redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, c);
1953 }
1954 /* }}} */
1955
1956 /* {{{ proto string RedisCluster::_prefix(string key) */
PHP_METHOD(RedisCluster,_prefix)1957 PHP_METHOD(RedisCluster, _prefix) {
1958 redisCluster *c = GET_CONTEXT();
1959 redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
1960 }
1961 /* }}} */
1962
1963 /* {{{ proto string RedisCluster::_serialize(mixed val) */
PHP_METHOD(RedisCluster,_serialize)1964 PHP_METHOD(RedisCluster, _serialize) {
1965 redisCluster *c = GET_CONTEXT();
1966 redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
1967 }
1968 /* }}} */
1969
1970 /* {{{ proto mixed RedisCluster::_unserialize(string val) */
PHP_METHOD(RedisCluster,_unserialize)1971 PHP_METHOD(RedisCluster, _unserialize) {
1972 redisCluster *c = GET_CONTEXT();
1973 redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1974 c->flags, redis_cluster_exception_ce);
1975 }
1976 /* }}} */
1977
PHP_METHOD(RedisCluster,_compress)1978 PHP_METHOD(RedisCluster, _compress) {
1979 redisCluster *c = GET_CONTEXT();
1980 redis_compress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
1981 }
1982
PHP_METHOD(RedisCluster,_uncompress)1983 PHP_METHOD(RedisCluster, _uncompress) {
1984 redisCluster *c = GET_CONTEXT();
1985 redis_uncompress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags,
1986 redis_cluster_exception_ce);
1987 }
1988
PHP_METHOD(RedisCluster,_pack)1989 PHP_METHOD(RedisCluster, _pack) {
1990 redisCluster *c = GET_CONTEXT();
1991 redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
1992 }
1993
PHP_METHOD(RedisCluster,_unpack)1994 PHP_METHOD(RedisCluster, _unpack) {
1995 redisCluster *c = GET_CONTEXT();
1996 redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags);
1997 }
1998
1999 /* {{{ proto array RedisCluster::_masters() */
PHP_METHOD(RedisCluster,_masters)2000 PHP_METHOD(RedisCluster, _masters) {
2001 redisCluster *c = GET_CONTEXT();
2002 redisClusterNode *node;
2003
2004 array_init(return_value);
2005
2006 ZEND_HASH_FOREACH_PTR(c->nodes, node) {
2007 if (node == NULL) break;
2008
2009 zval z_sub;
2010
2011 array_init(&z_sub);
2012
2013 add_next_index_stringl(&z_sub, ZSTR_VAL(node->sock->host), ZSTR_LEN(node->sock->host));
2014 add_next_index_long(&z_sub, node->sock->port);
2015 add_next_index_zval(return_value, &z_sub);
2016 } ZEND_HASH_FOREACH_END();
2017 }
2018
PHP_METHOD(RedisCluster,_redir)2019 PHP_METHOD(RedisCluster, _redir) {
2020 redisCluster *c = GET_CONTEXT();
2021 char buf[255];
2022 size_t len;
2023
2024 len = snprintf(buf, sizeof(buf), "%s:%d", c->redir_host, c->redir_port);
2025 if (*c->redir_host && c->redir_host_len) {
2026 RETURN_STRINGL(buf, len);
2027 } else {
2028 RETURN_NULL();
2029 }
2030 }
2031
2032 /*
2033 * Transaction handling
2034 */
2035
2036 /* {{{ proto bool RedisCluster::multi() */
PHP_METHOD(RedisCluster,multi)2037 PHP_METHOD(RedisCluster, multi) {
2038 redisCluster *c = GET_CONTEXT();
2039
2040 if (c->flags->mode == MULTI) {
2041 php_error_docref(NULL, E_WARNING,
2042 "RedisCluster is already in MULTI mode, ignoring");
2043 RETURN_FALSE;
2044 }
2045
2046 /* Flag that we're in MULTI mode */
2047 c->flags->mode = MULTI;
2048
2049 /* Return our object so we can chain MULTI calls */
2050 RETVAL_ZVAL(getThis(), 1, 0);
2051 }
2052
2053 /* {{{ proto bool RedisCluster::watch() */
PHP_METHOD(RedisCluster,watch)2054 PHP_METHOD(RedisCluster, watch) {
2055 redisCluster *c = GET_CONTEXT();
2056 HashTable *ht_dist;
2057 clusterDistList *dl;
2058 smart_string cmd = {0};
2059 zval *z_args;
2060 int argc = ZEND_NUM_ARGS(), i;
2061 zend_ulong slot;
2062 zend_string *zstr;
2063
2064 // Disallow in MULTI mode
2065 if (c->flags->mode == MULTI) {
2066 php_error_docref(NULL, E_WARNING,
2067 "WATCH command not allowed in MULTI mode");
2068 RETURN_FALSE;
2069 }
2070
2071 // Don't need to process zero arguments
2072 if (!argc) RETURN_FALSE;
2073
2074 // Create our distribution HashTable
2075 ht_dist = cluster_dist_create();
2076
2077 // Allocate args, and grab them
2078 z_args = emalloc(sizeof(zval) * argc);
2079 if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
2080 efree(z_args);
2081 cluster_dist_free(ht_dist);
2082 RETURN_FALSE;
2083 }
2084
2085 // Loop through arguments, prefixing if needed
2086 for(i = 0 ; i < argc; i++) {
2087 // We'll need the key as a string
2088 zstr = zval_get_string(&z_args[i]);
2089
2090 // Add this key to our distribution handler
2091 if (cluster_dist_add_key(c, ht_dist, ZSTR_VAL(zstr), ZSTR_LEN(zstr), NULL) == FAILURE) {
2092 CLUSTER_THROW_EXCEPTION("Can't issue WATCH command as the keyspace isn't fully mapped", 0);
2093 zend_string_release(zstr);
2094 RETURN_FALSE;
2095 }
2096 zend_string_release(zstr);
2097 }
2098
2099 // Iterate over each node we'll be sending commands to
2100 ZEND_HASH_FOREACH_PTR(ht_dist, dl) {
2101 // Grab the clusterDistList pointer itself
2102 if (dl == NULL) {
2103 CLUSTER_THROW_EXCEPTION("Internal error in a PHP HashTable", 0);
2104 cluster_dist_free(ht_dist);
2105 efree(z_args);
2106 efree(cmd.c);
2107 RETURN_FALSE;
2108 } else if (zend_hash_get_current_key(ht_dist, NULL, &slot) != HASH_KEY_IS_LONG) {
2109 break;
2110 }
2111
2112 // Construct our watch command for this node
2113 redis_cmd_init_sstr(&cmd, dl->len, "WATCH", sizeof("WATCH")-1);
2114 for (i = 0; i < dl->len; i++) {
2115 redis_cmd_append_sstr(&cmd, dl->entry[i].key,
2116 dl->entry[i].key_len);
2117 }
2118
2119 // If we get a failure from this, we have to abort
2120 if (cluster_send_command(c,(short)slot,cmd.c,cmd.len) ==-1) {
2121 RETURN_FALSE;
2122 }
2123
2124 // This node is watching
2125 SLOT_SOCK(c, (short)slot)->watching = 1;
2126
2127 // Zero out our command buffer
2128 cmd.len = 0;
2129 } ZEND_HASH_FOREACH_END();
2130
2131 // Cleanup
2132 cluster_dist_free(ht_dist);
2133 efree(z_args);
2134 efree(cmd.c);
2135
2136 RETURN_TRUE;
2137 }
2138
2139 /* {{{ proto bool RedisCluster::unwatch() */
PHP_METHOD(RedisCluster,unwatch)2140 PHP_METHOD(RedisCluster, unwatch) {
2141 redisCluster *c = GET_CONTEXT();
2142 short slot;
2143
2144 // Send UNWATCH to nodes that need it
2145 for(slot = 0; slot < REDIS_CLUSTER_SLOTS; slot++) {
2146 if (c->master[slot] && SLOT_SOCK(c,slot)->watching) {
2147 if (cluster_send_slot(c, slot, RESP_UNWATCH_CMD,
2148 sizeof(RESP_UNWATCH_CMD)-1,
2149 TYPE_LINE) ==-1)
2150 {
2151 CLUSTER_RETURN_BOOL(c, 0);
2152 }
2153
2154 // No longer watching
2155 SLOT_SOCK(c,slot)->watching = 0;
2156 }
2157 }
2158
2159 CLUSTER_RETURN_BOOL(c, 1);
2160 }
2161
2162 /* {{{ proto array RedisCluster::exec() */
PHP_METHOD(RedisCluster,exec)2163 PHP_METHOD(RedisCluster, exec) {
2164 redisCluster *c = GET_CONTEXT();
2165 clusterFoldItem *fi;
2166
2167 // Verify we are in fact in multi mode
2168 if (CLUSTER_IS_ATOMIC(c)) {
2169 php_error_docref(NULL, E_WARNING, "RedisCluster is not in MULTI mode");
2170 RETURN_FALSE;
2171 }
2172
2173 // First pass, send EXEC and abort on failure
2174 fi = c->multi_head;
2175 while (fi) {
2176 if (SLOT_SOCK(c, fi->slot)->mode == MULTI) {
2177 if ( cluster_send_exec(c, fi->slot) < 0) {
2178 cluster_abort_exec(c);
2179 CLUSTER_THROW_EXCEPTION("Error processing EXEC across the cluster", 0);
2180
2181 // Free our queue, reset MULTI state
2182 CLUSTER_FREE_QUEUE(c);
2183 CLUSTER_RESET_MULTI(c);
2184
2185 RETURN_FALSE;
2186 }
2187 SLOT_SOCK(c, fi->slot)->mode = ATOMIC;
2188 SLOT_SOCK(c, fi->slot)->watching = 0;
2189 }
2190 fi = fi->next;
2191 }
2192
2193 // MULTI multi-bulk response handler
2194 cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2195
2196 // Free our callback queue, any enqueued distributed command context items
2197 // and reset our MULTI state.
2198 CLUSTER_FREE_QUEUE(c);
2199 CLUSTER_RESET_MULTI(c);
2200 }
2201
2202 /* {{{ proto bool RedisCluster::discard() */
PHP_METHOD(RedisCluster,discard)2203 PHP_METHOD(RedisCluster, discard) {
2204 redisCluster *c = GET_CONTEXT();
2205
2206 if (CLUSTER_IS_ATOMIC(c)) {
2207 php_error_docref(NULL, E_WARNING, "Cluster is not in MULTI mode");
2208 RETURN_FALSE;
2209 }
2210
2211 if (cluster_abort_exec(c) < 0) {
2212 CLUSTER_RESET_MULTI(c);
2213 }
2214
2215 CLUSTER_FREE_QUEUE(c);
2216
2217 RETURN_TRUE;
2218 }
2219
2220 /* Get a slot either by key (string) or host/port array */
2221 static short
cluster_cmd_get_slot(redisCluster * c,zval * z_arg)2222 cluster_cmd_get_slot(redisCluster *c, zval *z_arg)
2223 {
2224 size_t key_len;
2225 int key_free;
2226 zval *z_host, *z_port;
2227 short slot;
2228 char *key;
2229 zend_string *zstr;
2230
2231 /* If it's a string, treat it as a key. Otherwise, look for a two
2232 * element array */
2233 if (Z_TYPE_P(z_arg) ==IS_STRING || Z_TYPE_P(z_arg) ==IS_LONG ||
2234 Z_TYPE_P(z_arg) ==IS_DOUBLE)
2235 {
2236 /* Allow for any scalar here */
2237 zstr = zval_get_string(z_arg);
2238 key = ZSTR_VAL(zstr);
2239 key_len = ZSTR_LEN(zstr);
2240
2241 /* Hash it */
2242 key_free = redis_key_prefix(c->flags, &key, &key_len);
2243 slot = cluster_hash_key(key, key_len);
2244 zend_string_release(zstr);
2245 if (key_free) efree(key);
2246 } else if (Z_TYPE_P(z_arg) == IS_ARRAY &&
2247 (z_host = zend_hash_index_find(Z_ARRVAL_P(z_arg), 0)) != NULL &&
2248 (z_port = zend_hash_index_find(Z_ARRVAL_P(z_arg), 1)) != NULL &&
2249 Z_TYPE_P(z_host) == IS_STRING && Z_TYPE_P(z_port) == IS_LONG
2250 ) {
2251 /* Attempt to find this specific node by host:port */
2252 slot = cluster_find_slot(c,(const char *)Z_STRVAL_P(z_host),
2253 (unsigned short)Z_LVAL_P(z_port));
2254
2255 /* Inform the caller if they've passed bad data */
2256 if (slot < 0) {
2257 php_error_docref(0, E_WARNING, "Unknown node %s:" ZEND_LONG_FMT,
2258 Z_STRVAL_P(z_host), Z_LVAL_P(z_port));
2259 }
2260 } else {
2261 php_error_docref(0, E_WARNING,
2262 "Directed commands must be passed a key or [host,port] array");
2263 return -1;
2264 }
2265
2266 return slot;
2267 }
2268
2269 /* Generic handler for things we want directed at a given node, like SAVE,
2270 * BGSAVE, FLUSHDB, FLUSHALL, etc */
2271 static void
cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS,char * kw,REDIS_REPLY_TYPE reply_type,cluster_cb cb)2272 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw,
2273 REDIS_REPLY_TYPE reply_type, cluster_cb cb)
2274 {
2275 redisCluster *c = GET_CONTEXT();
2276 char *cmd;
2277 int cmd_len;
2278 zval *z_arg;
2279 short slot;
2280
2281 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_arg) == FAILURE) {
2282 RETURN_FALSE;
2283 }
2284
2285 // One argument means find the node (treated like a key), and two means
2286 // send the command to a specific host and port
2287 slot = cluster_cmd_get_slot(c, z_arg);
2288 if (slot < 0) {
2289 RETURN_FALSE;
2290 }
2291
2292 // Construct our command
2293 cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, "");
2294
2295 // Kick off our command
2296 if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) {
2297 CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0);
2298 efree(cmd);
2299 RETURN_FALSE;
2300 }
2301
2302 // Our response callback
2303 cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2304
2305 // Free our command
2306 efree(cmd);
2307 }
2308
2309 static void
cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS,char * kw,REDIS_REPLY_TYPE reply_type,cluster_cb cb)2310 cluster_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, REDIS_REPLY_TYPE reply_type, cluster_cb cb)
2311 {
2312 redisCluster *c = GET_CONTEXT();
2313 char *cmd;
2314 int cmd_len;
2315 zval *z_arg;
2316 zend_bool async = 0;
2317 short slot;
2318
2319 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &z_arg, &async) == FAILURE) {
2320 RETURN_FALSE;
2321 }
2322
2323 // One argument means find the node (treated like a key), and two means
2324 // send the command to a specific host and port
2325 slot = cluster_cmd_get_slot(c, z_arg);
2326 if (slot < 0) {
2327 RETURN_FALSE;
2328 }
2329
2330 // Construct our command
2331 if (async) {
2332 cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, "s", "ASYNC", sizeof("ASYNC") - 1);
2333 } else {
2334 cmd_len = redis_spprintf(NULL, NULL, &cmd, kw, "");
2335 }
2336
2337
2338 // Kick off our command
2339 if (cluster_send_slot(c, slot, cmd, cmd_len, reply_type) < 0) {
2340 CLUSTER_THROW_EXCEPTION("Unable to send command at a specific node", 0);
2341 efree(cmd);
2342 RETURN_FALSE;
2343 }
2344
2345 // Our response callback
2346 cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2347
2348 // Free our command
2349 efree(cmd);
2350 }
2351
2352 /* Generic routine for handling various commands which need to be directed at
2353 * a node, but have complex syntax. We simply parse out the arguments and send
2354 * the command as constructed by the caller */
cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS,char * kw,int kw_len)2355 static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len)
2356 {
2357 redisCluster *c = GET_CONTEXT();
2358 smart_string cmd = {0};
2359 zval *z_args;
2360 short slot;
2361 int i, argc = ZEND_NUM_ARGS();
2362
2363 /* Commands using this pass-thru don't need to be enabled in MULTI mode */
2364 if (!CLUSTER_IS_ATOMIC(c)) {
2365 php_error_docref(0, E_WARNING,
2366 "Command can't be issued in MULTI mode");
2367 RETURN_FALSE;
2368 }
2369
2370 /* We at least need the key or [host,port] argument */
2371 if (argc < 1) {
2372 php_error_docref(0, E_WARNING,
2373 "Command requires at least an argument to direct to a node");
2374 RETURN_FALSE;
2375 }
2376
2377 /* Allocate an array to process arguments */
2378 z_args = emalloc(argc * sizeof(zval));
2379
2380 /* Grab args */
2381 if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
2382 efree(z_args);
2383 RETURN_FALSE;
2384 }
2385
2386 /* First argument needs to be the "where" */
2387 if ((slot = cluster_cmd_get_slot(c, &z_args[0])) < 0) {
2388 efree(z_args);
2389 RETURN_FALSE;
2390 }
2391
2392 /* Initialize our command */
2393 redis_cmd_init_sstr(&cmd, argc-1, kw, kw_len);
2394
2395 /* Iterate, appending args */
2396 for(i = 1; i < argc; i++) {
2397 zend_string *zstr = zval_get_string(&z_args[i]);
2398 redis_cmd_append_sstr(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr));
2399 zend_string_release(zstr);
2400 }
2401
2402 /* Send it off */
2403 if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) {
2404 CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0);
2405 efree(cmd.c);
2406 efree(z_args);
2407 RETURN_FALSE;
2408 }
2409
2410 /* Read the response variant */
2411 cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2412
2413 efree(cmd.c);
2414 efree(z_args);
2415 }
2416
2417 /* Generic method for HSCAN, SSCAN, and ZSCAN */
cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,REDIS_SCAN_TYPE type)2418 static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
2419 REDIS_SCAN_TYPE type)
2420 {
2421 redisCluster *c = GET_CONTEXT();
2422 char *cmd, *pat = NULL, *key = NULL;
2423 size_t key_len = 0, pat_len = 0, pat_free = 0;
2424 int cmd_len, key_free = 0;
2425 short slot;
2426 zval *z_it;
2427 HashTable *hash;
2428 long it, num_ele;
2429 zend_long count = 0;
2430
2431 // Can't be in MULTI mode
2432 if (!CLUSTER_IS_ATOMIC(c)) {
2433 CLUSTER_THROW_EXCEPTION("SCAN type commands can't be called in MULTI mode!", 0);
2434 RETURN_FALSE;
2435 }
2436
2437 /* Parse arguments */
2438 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|s!l", &key,
2439 &key_len, &z_it, &pat, &pat_len, &count) == FAILURE)
2440 {
2441 RETURN_FALSE;
2442 }
2443
2444 /* Treat as readonly */
2445 c->readonly = 1;
2446
2447 // Convert iterator to long if it isn't, update our long iterator if it's
2448 // set and >0, and finish if it's back to zero
2449 if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) {
2450 convert_to_long(z_it);
2451 it = 0;
2452 } else if (Z_LVAL_P(z_it) != 0) {
2453 it = Z_LVAL_P(z_it);
2454 } else {
2455 RETURN_FALSE;
2456 }
2457
2458 // Apply any key prefix we have, get the slot
2459 key_free = redis_key_prefix(c->flags, &key, &key_len);
2460 slot = cluster_hash_key(key, key_len);
2461
2462 if (c->flags->scan & REDIS_SCAN_PREFIX) {
2463 pat_free = redis_key_prefix(c->flags, &pat, &pat_len);
2464 }
2465
2466 // If SCAN_RETRY is set, loop until we get a zero iterator or until
2467 // we get non-zero elements. Otherwise we just send the command once.
2468 do {
2469 /* Free our return value if we're back in the loop */
2470 if (Z_TYPE_P(return_value) == IS_ARRAY) {
2471 zval_dtor(return_value);
2472 ZVAL_NULL(return_value);
2473 }
2474
2475 // Create command
2476 cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, it, pat, pat_len,
2477 count);
2478
2479 // Send it off
2480 if (cluster_send_command(c, slot, cmd, cmd_len) == FAILURE)
2481 {
2482 CLUSTER_THROW_EXCEPTION("Couldn't send SCAN command", 0);
2483 if (key_free) efree(key);
2484 efree(cmd);
2485 RETURN_FALSE;
2486 }
2487
2488 // Read response
2489 if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type,
2490 &it) == FAILURE)
2491 {
2492 CLUSTER_THROW_EXCEPTION("Couldn't read SCAN response", 0);
2493 if (key_free) efree(key);
2494 efree(cmd);
2495 RETURN_FALSE;
2496 }
2497
2498 // Count the elements we got back
2499 hash = Z_ARRVAL_P(return_value);
2500 num_ele = zend_hash_num_elements(hash);
2501
2502 // Free our command
2503 efree(cmd);
2504 } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
2505
2506 // Free our pattern
2507 if (pat_free) efree(pat);
2508
2509 // Free our key
2510 if (key_free) efree(key);
2511
2512 // Update iterator reference
2513 Z_LVAL_P(z_it) = it;
2514 }
2515
redis_acl_op_readonly(zend_string * op)2516 static int redis_acl_op_readonly(zend_string *op) {
2517 /* Only return read-only for operations we know to be */
2518 if (ZSTR_STRICMP_STATIC(op, "LIST") ||
2519 ZSTR_STRICMP_STATIC(op, "USERS") ||
2520 ZSTR_STRICMP_STATIC(op, "GETUSER") ||
2521 ZSTR_STRICMP_STATIC(op, "CAT") ||
2522 ZSTR_STRICMP_STATIC(op, "GENPASS") ||
2523 ZSTR_STRICMP_STATIC(op, "WHOAMI") ||
2524 ZSTR_STRICMP_STATIC(op, "LOG")) return 1;
2525
2526 return 0;
2527 }
2528
PHP_METHOD(RedisCluster,acl)2529 PHP_METHOD(RedisCluster, acl) {
2530 redisCluster *c = GET_CONTEXT();
2531 smart_string cmdstr = {0};
2532 int argc = ZEND_NUM_ARGS(), i, readonly;
2533 cluster_cb cb;
2534 zend_string *zs;
2535 zval *zargs;
2536 void *ctx = NULL;
2537 short slot;
2538
2539 /* ACL in cluster needs a slot argument, and then at least the op */
2540 if (argc < 2) {
2541 WRONG_PARAM_COUNT;
2542 RETURN_FALSE;
2543 }
2544
2545 /* Grab all our arguments and determine the command slot */
2546 zargs = emalloc(argc * sizeof(*zargs));
2547 if (zend_get_parameters_array(ht, argc, zargs) == FAILURE ||
2548 (slot = cluster_cmd_get_slot(c, &zargs[0]) < 0))
2549 {
2550 efree(zargs);
2551 RETURN_FALSE;
2552 }
2553
2554 REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1, "ACL");
2555
2556 /* Read the op, determin if it's readonly, and add it */
2557 zs = zval_get_string(&zargs[1]);
2558 readonly = redis_acl_op_readonly(zs);
2559 redis_cmd_append_sstr_zstr(&cmdstr, zs);
2560
2561 /* We have specialized handlers for GETUSER and LOG, whereas every
2562 * other ACL command can be handled generically */
2563 if (zend_string_equals_literal_ci(zs, "GETUSER")) {
2564 cb = cluster_acl_getuser_resp;
2565 } else if (zend_string_equals_literal_ci(zs, "LOG")) {
2566 cb = cluster_acl_log_resp;
2567 } else {
2568 cb = cluster_variant_resp;
2569 }
2570
2571 zend_string_release(zs);
2572
2573 /* Process remaining args */
2574 for (i = 2; i < argc; i++) {
2575 zs = zval_get_string(&zargs[i]);
2576 redis_cmd_append_sstr_zstr(&cmdstr, zs);
2577 zend_string_release(zs);
2578 }
2579
2580 /* Can we use replicas? */
2581 c->readonly = readonly && CLUSTER_IS_ATOMIC(c);
2582
2583 /* Kick off our command */
2584 if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, TYPE_EOF) < 0) {
2585 CLUSTER_THROW_EXCEPTION("Unabler to send ACL command", 0);
2586 efree(zargs);
2587 RETURN_FALSE;
2588 }
2589
2590 if (CLUSTER_IS_ATOMIC(c)) {
2591 cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2592 } else {
2593 CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx);
2594 }
2595
2596 efree(cmdstr.c);
2597 efree(zargs);
2598 }
2599
2600 /* {{{ proto RedisCluster::scan(string master, long it [, string pat, long cnt]) */
PHP_METHOD(RedisCluster,scan)2601 PHP_METHOD(RedisCluster, scan) {
2602 redisCluster *c = GET_CONTEXT();
2603 char *cmd, *pat = NULL;
2604 size_t pat_len = 0;
2605 int cmd_len;
2606 short slot;
2607 zval *z_it, *z_node;
2608 long it, num_ele, pat_free = 0;
2609 zend_long count = 0;
2610
2611 /* Treat as read-only */
2612 c->readonly = CLUSTER_IS_ATOMIC(c);
2613
2614 /* Can't be in MULTI mode */
2615 if (!CLUSTER_IS_ATOMIC(c)) {
2616 CLUSTER_THROW_EXCEPTION("SCAN type commands can't be called in MULTI mode", 0);
2617 RETURN_FALSE;
2618 }
2619
2620 /* Parse arguments */
2621 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z|s!l", &z_it,
2622 &z_node, &pat, &pat_len, &count) == FAILURE)
2623 {
2624 RETURN_FALSE;
2625 }
2626
2627 /* Convert or update iterator */
2628 if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) {
2629 convert_to_long(z_it);
2630 it = 0;
2631 } else if (Z_LVAL_P(z_it) != 0) {
2632 it = Z_LVAL_P(z_it);
2633 } else {
2634 RETURN_FALSE;
2635 }
2636
2637 if (c->flags->scan & REDIS_SCAN_PREFIX) {
2638 pat_free = redis_key_prefix(c->flags, &pat, &pat_len);
2639 }
2640
2641 /* With SCAN_RETRY on, loop until we get some keys, otherwise just return
2642 * what Redis does, as it does */
2643 do {
2644 /* Free our return value if we're back in the loop */
2645 if (Z_TYPE_P(return_value) == IS_ARRAY) {
2646 zval_dtor(return_value);
2647 ZVAL_NULL(return_value);
2648 }
2649
2650 /* Construct our command */
2651 cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len,
2652 count);
2653
2654 if ((slot = cluster_cmd_get_slot(c, z_node)) < 0) {
2655 RETURN_FALSE;
2656 }
2657
2658 // Send it to the node in question
2659 if (cluster_send_command(c, slot, cmd, cmd_len) < 0)
2660 {
2661 CLUSTER_THROW_EXCEPTION("Couldn't send SCAN to node", 0);
2662 efree(cmd);
2663 RETURN_FALSE;
2664 }
2665
2666 if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN,
2667 &it) == FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY)
2668 {
2669 CLUSTER_THROW_EXCEPTION("Couldn't process SCAN response from node", 0);
2670 efree(cmd);
2671 RETURN_FALSE;
2672 }
2673
2674 efree(cmd);
2675
2676 num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value));
2677 } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
2678
2679 if (pat_free) efree(pat);
2680
2681 Z_LVAL_P(z_it) = it;
2682 }
2683 /* }}} */
2684
2685 /* {{{ proto RedisCluster::sscan(string key, long it [string pat, long cnt]) */
PHP_METHOD(RedisCluster,sscan)2686 PHP_METHOD(RedisCluster, sscan) {
2687 cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN);
2688 }
2689 /* }}} */
2690
2691 /* {{{ proto RedisCluster::zscan(string key, long it [string pat, long cnt]) */
PHP_METHOD(RedisCluster,zscan)2692 PHP_METHOD(RedisCluster, zscan) {
2693 cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
2694 }
2695 /* }}} */
2696
2697 /* {{{ proto RedisCluster::hscan(string key, long it [string pat, long cnt]) */
PHP_METHOD(RedisCluster,hscan)2698 PHP_METHOD(RedisCluster, hscan) {
2699 cluster_kscan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN);
2700 }
2701 /* }}} */
2702
2703 /* {{{ proto RedisCluster::save(string key)
2704 * proto RedisCluster::save(array host_port) */
PHP_METHOD(RedisCluster,save)2705 PHP_METHOD(RedisCluster, save) {
2706 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SAVE", TYPE_LINE,
2707 cluster_bool_resp);
2708 }
2709 /* }}} */
2710
2711 /* {{{ proto RedisCluster::bgsave(string key)
2712 * proto RedisCluster::bgsave(array host_port) */
PHP_METHOD(RedisCluster,bgsave)2713 PHP_METHOD(RedisCluster, bgsave) {
2714 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGSAVE",
2715 TYPE_LINE, cluster_bool_resp);
2716 }
2717 /* }}} */
2718
2719 /* {{{ proto RedisCluster::flushdb(string key, [bool async])
2720 * proto RedisCluster::flushdb(array host_port, [bool async]) */
PHP_METHOD(RedisCluster,flushdb)2721 PHP_METHOD(RedisCluster, flushdb) {
2722 cluster_flush_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHDB",
2723 TYPE_LINE, cluster_bool_resp);
2724 }
2725 /* }}} */
2726
2727 /* {{{ proto RedisCluster::flushall(string key, [bool async])
2728 * proto RedisCluster::flushall(array host_port, [bool async]) */
PHP_METHOD(RedisCluster,flushall)2729 PHP_METHOD(RedisCluster, flushall) {
2730 cluster_flush_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "FLUSHALL",
2731 TYPE_LINE, cluster_bool_resp);
2732 }
2733 /* }}} */
2734
2735 /* {{{ proto RedisCluster::dbsize(string key)
2736 * proto RedisCluster::dbsize(array host_port) */
PHP_METHOD(RedisCluster,dbsize)2737 PHP_METHOD(RedisCluster, dbsize) {
2738 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DBSIZE",
2739 TYPE_INT, cluster_long_resp);
2740 }
2741 /* }}} */
2742
2743 /* {{{ proto RedisCluster::bgrewriteaof(string key)
2744 * proto RedisCluster::bgrewriteaof(array host_port) */
PHP_METHOD(RedisCluster,bgrewriteaof)2745 PHP_METHOD(RedisCluster, bgrewriteaof) {
2746 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BGREWRITEAOF",
2747 TYPE_LINE, cluster_bool_resp);
2748 }
2749 /* }}} */
2750
2751 /* {{{ proto RedisCluster::lastsave(string key)
2752 * proto RedisCluster::lastsave(array $host_port) */
PHP_METHOD(RedisCluster,lastsave)2753 PHP_METHOD(RedisCluster, lastsave) {
2754 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "LASTSAVE",
2755 TYPE_INT, cluster_long_resp);
2756 }
2757 /* }}} */
2758
2759 /* {{{ proto array RedisCluster::info(string key, [string $arg])
2760 * proto array RedisCluster::info(array host_port, [string $arg]) */
PHP_METHOD(RedisCluster,info)2761 PHP_METHOD(RedisCluster, info) {
2762 redisCluster *c = GET_CONTEXT();
2763 REDIS_REPLY_TYPE rtype;
2764 char *cmd, *opt = NULL;
2765 int cmd_len;
2766 size_t opt_len = 0;
2767 void *ctx = NULL;
2768
2769 zval *z_arg;
2770 short slot;
2771
2772 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s", &z_arg, &opt,
2773 &opt_len) == FAILURE)
2774 {
2775 RETURN_FALSE;
2776 }
2777
2778 /* Treat INFO as non read-only, as we probably want the master */
2779 c->readonly = 0;
2780
2781 slot = cluster_cmd_get_slot(c, z_arg);
2782 if (slot < 0) {
2783 RETURN_FALSE;
2784 }
2785
2786 if (opt != NULL) {
2787 cmd_len = redis_spprintf(NULL, NULL, &cmd, "INFO", "s", opt, opt_len);
2788 } else {
2789 cmd_len = redis_spprintf(NULL, NULL, &cmd, "INFO", "");
2790 }
2791
2792 rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE;
2793 if (cluster_send_slot(c, slot, cmd, cmd_len, rtype) < 0) {
2794 CLUSTER_THROW_EXCEPTION("Unable to send INFO command to specific node", 0);
2795 efree(cmd);
2796 RETURN_FALSE;
2797 }
2798
2799 if (CLUSTER_IS_ATOMIC(c)) {
2800 cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2801 } else {
2802 CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_info_resp, ctx);
2803 }
2804
2805 efree(cmd);
2806 }
2807 /* }}} */
2808
2809 /* {{{ proto array RedisCluster::client('list')
2810 * proto bool RedisCluster::client('kill', $ipport)
2811 * proto bool RedisCluster::client('setname', $name)
2812 * proto string RedisCluster::client('getname')
2813 */
PHP_METHOD(RedisCluster,client)2814 PHP_METHOD(RedisCluster, client) {
2815 redisCluster *c = GET_CONTEXT();
2816 char *cmd, *opt = NULL, *arg = NULL;
2817 int cmd_len;
2818 size_t opt_len, arg_len = 0;
2819 REDIS_REPLY_TYPE rtype;
2820 zval *z_node;
2821 short slot;
2822 cluster_cb cb;
2823
2824 /* Parse args */
2825 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs|s", &z_node, &opt,
2826 &opt_len, &arg, &arg_len) == FAILURE)
2827 {
2828 RETURN_FALSE;
2829 }
2830
2831 /* Make sure we can properly resolve the slot */
2832 slot = cluster_cmd_get_slot(c, z_node);
2833 if (slot < 0) RETURN_FALSE;
2834
2835 /* Our return type and reply callback is different for all subcommands */
2836 if (opt_len == 4 && !strncasecmp(opt, "list", 4)) {
2837 rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE;
2838 cb = cluster_client_list_resp;
2839 } else if ((opt_len == 4 && !strncasecmp(opt, "kill", 4)) ||
2840 (opt_len == 7 && !strncasecmp(opt, "setname", 7)))
2841 {
2842 rtype = TYPE_LINE;
2843 cb = cluster_bool_resp;
2844 } else if (opt_len == 7 && !strncasecmp(opt, "getname", 7)) {
2845 rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE;
2846 cb = cluster_bulk_resp;
2847 } else {
2848 php_error_docref(NULL, E_WARNING,
2849 "Invalid CLIENT subcommand (LIST, KILL, GETNAME, and SETNAME are valid");
2850 RETURN_FALSE;
2851 }
2852
2853 /* Construct the command */
2854 if (ZEND_NUM_ARGS() == 3) {
2855 cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "ss",
2856 opt, opt_len, arg, arg_len);
2857 } else if (ZEND_NUM_ARGS() == 2) {
2858 cmd_len = redis_spprintf(NULL, NULL, &cmd, "CLIENT", "s",
2859 opt, opt_len);
2860 } else {
2861 zend_wrong_param_count();
2862 RETURN_FALSE;
2863 }
2864
2865 /* Attempt to write our command */
2866 if (cluster_send_slot(c, slot, cmd, cmd_len, rtype) < 0) {
2867 CLUSTER_THROW_EXCEPTION("Unable to send CLIENT command to specific node", 0);
2868 efree(cmd);
2869 RETURN_FALSE;
2870 }
2871
2872 /* Now enqueue or process response */
2873 if (CLUSTER_IS_ATOMIC(c)) {
2874 cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2875 } else {
2876 void *ctx = NULL;
2877 CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx);
2878 }
2879
2880 efree(cmd);
2881 }
2882
2883 /* {{{ proto mixed RedisCluster::cluster(variant) */
PHP_METHOD(RedisCluster,cluster)2884 PHP_METHOD(RedisCluster, cluster) {
2885 cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CLUSTER",
2886 sizeof("CLUSTER")-1);
2887 }
2888 /* }}} */
2889
2890 /* }}} */
2891
2892 /* {{{ proto mixed RedisCluster::config(string key, ...)
2893 * proto mixed RedisCluster::config(array host_port, ...) */
PHP_METHOD(RedisCluster,config)2894 PHP_METHOD(RedisCluster, config) {
2895 cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "CONFIG",
2896 sizeof("CONFIG")-1);
2897 }
2898 /* }}} */
2899
2900 /* {{{ proto mixed RedisCluster::pubsub(string key, ...)
2901 * proto mixed RedisCluster::pubsub(array host_port, ...) */
PHP_METHOD(RedisCluster,pubsub)2902 PHP_METHOD(RedisCluster, pubsub) {
2903 cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUBSUB",
2904 sizeof("PUBSUB")-1);
2905 }
2906 /* }}} */
2907
2908 /* {{{ proto mixed RedisCluster::script(string key, ...)
2909 * proto mixed RedisCluster::script(array host_port, ...) */
PHP_METHOD(RedisCluster,script)2910 PHP_METHOD(RedisCluster, script) {
2911 redisCluster *c = GET_CONTEXT();
2912 smart_string cmd = {0};
2913 zval *z_args;
2914 short slot;
2915 int argc = ZEND_NUM_ARGS();
2916
2917 /* Commands using this pass-thru don't need to be enabled in MULTI mode */
2918 if (!CLUSTER_IS_ATOMIC(c)) {
2919 php_error_docref(0, E_WARNING,
2920 "Command can't be issued in MULTI mode");
2921 RETURN_FALSE;
2922 }
2923
2924 /* We at least need the key or [host,port] argument */
2925 if (argc < 2) {
2926 php_error_docref(0, E_WARNING,
2927 "Command requires at least an argument to direct to a node");
2928 RETURN_FALSE;
2929 }
2930
2931 /* Allocate an array to process arguments */
2932 z_args = ecalloc(argc, sizeof(zval));
2933
2934 /* Grab args */
2935 if (zend_get_parameters_array(ht, argc, z_args) == FAILURE ||
2936 (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0 ||
2937 redis_build_script_cmd(&cmd, argc - 1, &z_args[1]) == NULL
2938 ) {
2939 efree(z_args);
2940 RETURN_FALSE;
2941 }
2942
2943 /* Send it off */
2944 if (cluster_send_slot(c, slot, cmd.c, cmd.len, TYPE_EOF) < 0) {
2945 CLUSTER_THROW_EXCEPTION("Couldn't send command to node", 0);
2946 efree(cmd.c);
2947 efree(z_args);
2948 RETURN_FALSE;
2949 }
2950
2951 /* Read the response variant */
2952 cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
2953
2954 efree(cmd.c);
2955 efree(z_args);
2956 }
2957 /* }}} */
2958
2959 /* {{{ proto mixed RedisCluster::slowlog(string key, ...)
2960 * proto mixed RedisCluster::slowlog(array host_port, ...) */
PHP_METHOD(RedisCluster,slowlog)2961 PHP_METHOD(RedisCluster, slowlog) {
2962 cluster_raw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SLOWLOG",
2963 sizeof("SLOWLOG")-1);
2964 }
2965 /* }}} */
2966
2967 /* {{{ proto int RedisCluster::geoadd(string key, float long float lat string mem, ...) */
PHP_METHOD(RedisCluster,geoadd)2968 PHP_METHOD(RedisCluster, geoadd) {
2969 CLUSTER_PROCESS_KW_CMD("GEOADD", redis_key_varval_cmd, cluster_long_resp, 0);
2970 }
2971
2972 /* {{{ proto array RedisCluster::geohash(string key, string mem1, [string mem2...]) */
PHP_METHOD(RedisCluster,geohash)2973 PHP_METHOD(RedisCluster, geohash) {
2974 CLUSTER_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, cluster_mbulk_raw_resp, 1);
2975 }
2976
2977 /* {{{ proto array RedisCluster::geopos(string key, string mem1, [string mem2...]) */
PHP_METHOD(RedisCluster,geopos)2978 PHP_METHOD(RedisCluster, geopos) {
2979 CLUSTER_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, cluster_variant_resp, 1);
2980 }
2981
2982 /* {{{ proto array RedisCluster::geodist(string key, string mem1, string mem2 [string unit]) */
PHP_METHOD(RedisCluster,geodist)2983 PHP_METHOD(RedisCluster, geodist) {
2984 CLUSTER_PROCESS_CMD(geodist, cluster_dbl_resp, 1);
2985 }
2986
2987 /* {{{ proto array RedisCluster::georadius() }}} */
PHP_METHOD(RedisCluster,georadius)2988 PHP_METHOD(RedisCluster, georadius) {
2989 CLUSTER_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, cluster_variant_resp, 1);
2990 }
2991
2992 /* {{{ proto array RedisCluster::georadius() }}} */
PHP_METHOD(RedisCluster,georadius_ro)2993 PHP_METHOD(RedisCluster, georadius_ro) {
2994 CLUSTER_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, cluster_variant_resp, 1);
2995 }
2996
2997 /* {{{ proto array RedisCluster::georadiusbymember() }}} */
PHP_METHOD(RedisCluster,georadiusbymember)2998 PHP_METHOD(RedisCluster, georadiusbymember) {
2999 CLUSTER_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, cluster_variant_resp, 1);
3000 }
3001
3002 /* {{{ proto array RedisCluster::georadiusbymember() }}} */
PHP_METHOD(RedisCluster,georadiusbymember_ro)3003 PHP_METHOD(RedisCluster, georadiusbymember_ro) {
3004 CLUSTER_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, cluster_variant_resp, 1);
3005 }
3006
3007 /* {{{ proto array RedisCluster::role(string key)
3008 * proto array RedisCluster::role(array host_port) */
PHP_METHOD(RedisCluster,role)3009 PHP_METHOD(RedisCluster, role) {
3010 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ROLE",
3011 TYPE_MULTIBULK, cluster_variant_resp);
3012 }
3013
3014 /* {{{ proto array RedisCluster::time(string key)
3015 * proto array RedisCluster::time(array host_port) */
PHP_METHOD(RedisCluster,time)3016 PHP_METHOD(RedisCluster, time) {
3017 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "TIME",
3018 TYPE_MULTIBULK, cluster_variant_resp);
3019 }
3020 /* }}} */
3021
3022 /* {{{ proto string RedisCluster::randomkey(string key)
3023 * proto string RedisCluster::randomkey(array host_port) */
PHP_METHOD(RedisCluster,randomkey)3024 PHP_METHOD(RedisCluster, randomkey) {
3025 cluster_empty_node_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "RANDOMKEY",
3026 TYPE_BULK, cluster_bulk_resp);
3027 }
3028 /* }}} */
3029
3030 /* {{{ proto bool RedisCluster::ping(string key| string msg)
3031 * proto bool RedisCluster::ping(array host_port| string msg) */
PHP_METHOD(RedisCluster,ping)3032 PHP_METHOD(RedisCluster, ping) {
3033 redisCluster *c = GET_CONTEXT();
3034 REDIS_REPLY_TYPE rtype;
3035 void *ctx = NULL;
3036 zval *z_node;
3037 char *cmd, *arg = NULL;
3038 int cmdlen;
3039 size_t arglen;
3040 short slot;
3041
3042 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!", &z_node, &arg,
3043 &arglen) == FAILURE)
3044 {
3045 RETURN_FALSE;
3046 }
3047
3048 /* Treat this as a readonly command */
3049 c->readonly = CLUSTER_IS_ATOMIC(c);
3050
3051 /* Grab slot either by key or host/port */
3052 slot = cluster_cmd_get_slot(c, z_node);
3053 if (slot < 0) {
3054 RETURN_FALSE;
3055 }
3056
3057 /* Construct our command */
3058 if (arg != NULL) {
3059 cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", "s", arg, arglen);
3060 } else {
3061 cmdlen = redis_spprintf(NULL, NULL, &cmd, "PING", "");
3062 }
3063
3064 /* Send it off */
3065 rtype = CLUSTER_IS_ATOMIC(c) && arg != NULL ? TYPE_BULK : TYPE_LINE;
3066 if (cluster_send_slot(c, slot, cmd, cmdlen, rtype) < 0) {
3067 CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0);
3068 efree(cmd);
3069 RETURN_FALSE;
3070 }
3071
3072 /* We're done with our command */
3073 efree(cmd);
3074
3075 /* Process response */
3076 if (CLUSTER_IS_ATOMIC(c)) {
3077 if (arg != NULL) {
3078 cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
3079 } else {
3080 /* If we're atomic and didn't send an argument then we have already
3081 * processed the reply (which must have been successful. */
3082 RETURN_TRUE;
3083 }
3084 } else {
3085 if (arg != NULL) {
3086 CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx);
3087 } else {
3088 CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx);
3089 }
3090
3091 RETURN_ZVAL(getThis(), 1, 0);
3092 }
3093 }
3094 /* }}} */
3095
3096 /* {{{ proto long RedisCluster::xack(string key, string group, array ids) }}} */
PHP_METHOD(RedisCluster,xack)3097 PHP_METHOD(RedisCluster, xack) {
3098 CLUSTER_PROCESS_CMD(xack, cluster_long_resp, 0);
3099 }
3100
3101 /* {{{ proto string RedisCluster::xadd(string key, string id, array field_values) }}} */
PHP_METHOD(RedisCluster,xadd)3102 PHP_METHOD(RedisCluster, xadd) {
3103 CLUSTER_PROCESS_CMD(xadd, cluster_bulk_raw_resp, 0);
3104 }
3105
3106 /* {{{ proto array RedisCluster::xclaim(string key, string group, string consumer,
3107 * long min_idle_time, array ids, array options) */
PHP_METHOD(RedisCluster,xclaim)3108 PHP_METHOD(RedisCluster, xclaim) {
3109 CLUSTER_PROCESS_CMD(xclaim, cluster_xclaim_resp, 0);
3110 }
3111
PHP_METHOD(RedisCluster,xdel)3112 PHP_METHOD(RedisCluster, xdel) {
3113 CLUSTER_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, cluster_long_resp, 0);
3114 }
3115
3116 /* {{{ proto variant RedisCluster::xgroup(string op, [string key, string arg1, string arg2]) }}} */
PHP_METHOD(RedisCluster,xgroup)3117 PHP_METHOD(RedisCluster, xgroup) {
3118 CLUSTER_PROCESS_CMD(xgroup, cluster_variant_resp, 0);
3119 }
3120
3121 /* {{{ proto variant RedisCluster::xinfo(string op, [string arg1, string arg2]); */
PHP_METHOD(RedisCluster,xinfo)3122 PHP_METHOD(RedisCluster, xinfo) {
3123 CLUSTER_PROCESS_CMD(xinfo, cluster_xinfo_resp, 0);
3124 }
3125
3126 /* {{{ proto string RedisCluster::xlen(string key) }}} */
PHP_METHOD(RedisCluster,xlen)3127 PHP_METHOD(RedisCluster, xlen) {
3128 CLUSTER_PROCESS_KW_CMD("XLEN", redis_key_cmd, cluster_long_resp, 1);
3129 }
3130
PHP_METHOD(RedisCluster,xpending)3131 PHP_METHOD(RedisCluster, xpending) {
3132 CLUSTER_PROCESS_CMD(xpending, cluster_variant_resp_strings, 1);
3133 }
3134
PHP_METHOD(RedisCluster,xrange)3135 PHP_METHOD(RedisCluster, xrange) {
3136 CLUSTER_PROCESS_KW_CMD("XRANGE", redis_xrange_cmd, cluster_xrange_resp, 1);
3137 }
3138
PHP_METHOD(RedisCluster,xrevrange)3139 PHP_METHOD(RedisCluster, xrevrange) {
3140 CLUSTER_PROCESS_KW_CMD("XREVRANGE", redis_xrange_cmd, cluster_xrange_resp, 1);
3141 }
3142
PHP_METHOD(RedisCluster,xread)3143 PHP_METHOD(RedisCluster, xread) {
3144 CLUSTER_PROCESS_CMD(xread, cluster_xread_resp, 1);
3145 }
3146
PHP_METHOD(RedisCluster,xreadgroup)3147 PHP_METHOD(RedisCluster, xreadgroup) {
3148 CLUSTER_PROCESS_CMD(xreadgroup, cluster_xread_resp, 0);
3149 }
3150
PHP_METHOD(RedisCluster,xtrim)3151 PHP_METHOD(RedisCluster, xtrim) {
3152 CLUSTER_PROCESS_CMD(xtrim, cluster_long_resp, 0);
3153 }
3154
3155
3156
3157 /* {{{ proto string RedisCluster::echo(string key, string msg)
3158 * proto string RedisCluster::echo(array host_port, string msg) */
PHP_METHOD(RedisCluster,echo)3159 PHP_METHOD(RedisCluster, echo) {
3160 redisCluster *c = GET_CONTEXT();
3161 REDIS_REPLY_TYPE rtype;
3162 zval *z_arg;
3163 char *cmd, *msg;
3164 int cmd_len;
3165 size_t msg_len;
3166 short slot;
3167
3168 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &z_arg, &msg,
3169 &msg_len) == FAILURE)
3170 {
3171 RETURN_FALSE;
3172 }
3173
3174 /* Treat this as a readonly command */
3175 c->readonly = CLUSTER_IS_ATOMIC(c);
3176
3177 /* Grab slot either by key or host/port */
3178 slot = cluster_cmd_get_slot(c, z_arg);
3179 if (slot < 0) {
3180 RETURN_FALSE;
3181 }
3182
3183 /* Construct our command */
3184 cmd_len = redis_spprintf(NULL, NULL, &cmd, "ECHO", "s", msg, msg_len);
3185
3186 /* Send it off */
3187 rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE;
3188 if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) {
3189 CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0);
3190 efree(cmd);
3191 RETURN_FALSE;
3192 }
3193
3194 /* Process bulk response */
3195 if (CLUSTER_IS_ATOMIC(c)) {
3196 cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
3197 } else {
3198 void *ctx = NULL;
3199 CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx);
3200 }
3201
3202 efree(cmd);
3203 }
3204 /* }}} */
3205
3206 /* {{{ proto mixed RedisCluster::rawcommand(string $key, string $cmd, [ $argv1 .. $argvN])
3207 * proto mixed RedisCluster::rawcommand(array $host_port, string $cmd, [ $argv1 .. $argvN]) */
PHP_METHOD(RedisCluster,rawcommand)3208 PHP_METHOD(RedisCluster, rawcommand) {
3209 REDIS_REPLY_TYPE rtype;
3210 int argc = ZEND_NUM_ARGS(), cmd_len;
3211 redisCluster *c = GET_CONTEXT();
3212 char *cmd = NULL;
3213 zval *z_args;
3214 short slot;
3215
3216 /* Sanity check on our arguments */
3217 if (argc < 2) {
3218 php_error_docref(NULL, E_WARNING,
3219 "You must pass at least node information as well as at least a command.");
3220 RETURN_FALSE;
3221 }
3222 z_args = emalloc(argc * sizeof(zval));
3223 if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
3224 php_error_docref(NULL, E_WARNING,
3225 "Internal PHP error parsing method parameters.");
3226 efree(z_args);
3227 RETURN_FALSE;
3228 } else if (redis_build_raw_cmd(&z_args[1], argc-1, &cmd, &cmd_len) ||
3229 (slot = cluster_cmd_get_slot(c, &z_args[0])) < 0)
3230 {
3231 if (cmd) efree(cmd);
3232 efree(z_args);
3233 RETURN_FALSE;
3234 }
3235
3236 /* Free argument array */
3237 efree(z_args);
3238
3239 /* Direct the command */
3240 rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE;
3241 if (cluster_send_slot(c,slot,cmd,cmd_len,rtype) < 0) {
3242 CLUSTER_THROW_EXCEPTION("Unable to send command to the specified node", 0);
3243 efree(cmd);
3244 RETURN_FALSE;
3245 }
3246
3247 /* Process variant response */
3248 if (CLUSTER_IS_ATOMIC(c)) {
3249 cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
3250 } else {
3251 void *ctx = NULL;
3252 CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_raw_resp, ctx);
3253 }
3254
3255 efree(cmd);
3256 }
3257 /* }}} */
3258
3259 /* {{{ proto array RedisCluster::command()
3260 * proto array RedisCluster::command('INFO', string cmd)
3261 * proto array RedisCluster::command('GETKEYS', array cmd_args) */
PHP_METHOD(RedisCluster,command)3262 PHP_METHOD(RedisCluster, command) {
3263 CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0);
3264 }
3265
3266 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
3267
3268