1 /* -*- Mode: C; tab-width: 4 -*- */
2 /*
3 +----------------------------------------------------------------------+
4 | Copyright (c) 1997-2009 The PHP Group |
5 +----------------------------------------------------------------------+
6 | This source file is subject to version 3.01 of the PHP license, |
7 | that is bundled with this package in the file LICENSE, and is |
8 | available through the world-wide-web at the following url: |
9 | http://www.php.net/license/3_01.txt |
10 | If you did not receive a copy of the PHP license and are unable to |
11 | obtain it through the world-wide-web, please send a note to |
12 | license@php.net so we can mail you a copy immediately. |
13 +----------------------------------------------------------------------+
14 | Original author: Alfonso Jimenez <yo@alfonsojimenez.com> |
15 | Maintainer: Nicolas Favre-Felix <n.favre-felix@owlient.eu> |
16 | Maintainer: Nasreddine Bouafif <n.bouafif@owlient.eu> |
17 | Maintainer: Michael Grunder <michael.grunder@gmail.com> |
18 +----------------------------------------------------------------------+
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php_redis.h"
26 #include "redis_array.h"
27 #include "redis_cluster.h"
28 #include "redis_commands.h"
29 #include "redis_sentinel.h"
30 #include <standard/php_random.h>
31 #include <zend_exceptions.h>
32 #include <ext/standard/info.h>
33 #include <ext/hash/php_hash.h>
34
35
36 #ifdef PHP_SESSION
37 #include <ext/session/php_session.h>
38 #endif
39
40 #include "library.h"
41
42 #ifdef HAVE_REDIS_ZSTD
43 #include <zstd.h>
44 #endif
45
46 #ifdef HAVE_REDIS_LZ4
47 #include <lz4.h>
48 #endif
49
50 #ifdef PHP_SESSION
51 extern ps_module ps_mod_redis;
52 extern ps_module ps_mod_redis_cluster;
53 #endif
54
55 extern zend_class_entry *redis_array_ce;
56 extern zend_class_entry *redis_cluster_ce;
57 extern zend_class_entry *redis_cluster_exception_ce;
58 extern zend_class_entry *redis_sentinel_ce;
59
60 zend_class_entry *redis_ce;
61 zend_class_entry *redis_exception_ce;
62
63 extern int le_cluster_slot_cache;
64
65 extern zend_function_entry redis_array_functions[];
66 extern zend_function_entry redis_cluster_functions[];
67 extern zend_function_entry redis_sentinel_functions[];
68
69 int le_redis_pconnect;
70
71 PHP_INI_BEGIN()
72 /* redis arrays */
73 PHP_INI_ENTRY("redis.arrays.algorithm", "", PHP_INI_ALL, NULL)
74 PHP_INI_ENTRY("redis.arrays.auth", "", PHP_INI_ALL, NULL)
75 PHP_INI_ENTRY("redis.arrays.autorehash", "0", PHP_INI_ALL, NULL)
76 PHP_INI_ENTRY("redis.arrays.connecttimeout", "0", PHP_INI_ALL, NULL)
77 PHP_INI_ENTRY("redis.arrays.distributor", "", PHP_INI_ALL, NULL)
78 PHP_INI_ENTRY("redis.arrays.functions", "", PHP_INI_ALL, NULL)
79 PHP_INI_ENTRY("redis.arrays.hosts", "", PHP_INI_ALL, NULL)
80 PHP_INI_ENTRY("redis.arrays.index", "0", PHP_INI_ALL, NULL)
81 PHP_INI_ENTRY("redis.arrays.lazyconnect", "0", PHP_INI_ALL, NULL)
82 PHP_INI_ENTRY("redis.arrays.names", "", PHP_INI_ALL, NULL)
83 PHP_INI_ENTRY("redis.arrays.pconnect", "0", PHP_INI_ALL, NULL)
84 PHP_INI_ENTRY("redis.arrays.previous", "", PHP_INI_ALL, NULL)
85 PHP_INI_ENTRY("redis.arrays.readtimeout", "0", PHP_INI_ALL, NULL)
86 PHP_INI_ENTRY("redis.arrays.retryinterval", "0", PHP_INI_ALL, NULL)
87 PHP_INI_ENTRY("redis.arrays.consistent", "0", PHP_INI_ALL, NULL)
88
89 /* redis cluster */
90 PHP_INI_ENTRY("redis.clusters.cache_slots", "0", PHP_INI_ALL, NULL)
91 PHP_INI_ENTRY("redis.clusters.auth", "", PHP_INI_ALL, NULL)
92 PHP_INI_ENTRY("redis.clusters.persistent", "0", PHP_INI_ALL, NULL)
93 PHP_INI_ENTRY("redis.clusters.read_timeout", "0", PHP_INI_ALL, NULL)
94 PHP_INI_ENTRY("redis.clusters.seeds", "", PHP_INI_ALL, NULL)
95 PHP_INI_ENTRY("redis.clusters.timeout", "0", PHP_INI_ALL, NULL)
96
97 /* redis pconnect */
98 PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL)
99 PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL)
100 PHP_INI_ENTRY("redis.pconnect.echo_check_liveness", "1", PHP_INI_ALL, NULL)
101 PHP_INI_ENTRY("redis.pconnect.pool_detect_dirty", "0", PHP_INI_ALL, NULL)
102 PHP_INI_ENTRY("redis.pconnect.pool_poll_timeout", "0", PHP_INI_ALL, NULL)
103 PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL)
104
105 /* redis session */
106 PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL)
107 PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL)
108 PHP_INI_ENTRY("redis.session.lock_retries", "10", PHP_INI_ALL, NULL)
109 PHP_INI_ENTRY("redis.session.lock_wait_time", "2000", PHP_INI_ALL, NULL)
110 PHP_INI_END()
111
112 /** {{{ Argument info for commands in redis 1.0 */
113 ZEND_BEGIN_ARG_INFO_EX(arginfo_connect, 0, 0, 1)
114 ZEND_ARG_INFO(0, host)
115 ZEND_ARG_INFO(0, port)
116 ZEND_ARG_INFO(0, timeout)
117 ZEND_ARG_INFO(0, retry_interval)
118 ZEND_END_ARG_INFO()
119
120 ZEND_BEGIN_ARG_INFO_EX(arginfo_info, 0, 0, 0)
121 ZEND_ARG_INFO(0, option)
122 ZEND_END_ARG_INFO()
123
124 ZEND_BEGIN_ARG_INFO_EX(arginfo_multi, 0, 0, 0)
125 ZEND_ARG_INFO(0, mode)
126 ZEND_END_ARG_INFO()
127
128 ZEND_BEGIN_ARG_INFO_EX(arginfo_client, 0, 0, 1)
129 ZEND_ARG_INFO(0, cmd)
130 ZEND_ARG_VARIADIC_INFO(0, args)
131 ZEND_END_ARG_INFO()
132
133 ZEND_BEGIN_ARG_INFO_EX(arginfo_config, 0, 0, 2)
134 ZEND_ARG_INFO(0, cmd)
135 ZEND_ARG_INFO(0, key)
136 ZEND_ARG_INFO(0, value)
137 ZEND_END_ARG_INFO()
138
139 ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0)
140 ZEND_ARG_INFO(0, async)
141 ZEND_END_ARG_INFO()
142
143 ZEND_BEGIN_ARG_INFO_EX(arginfo_pubsub, 0, 0, 1)
144 ZEND_ARG_INFO(0, cmd)
145 ZEND_ARG_VARIADIC_INFO(0, args)
146 ZEND_END_ARG_INFO()
147
148 ZEND_BEGIN_ARG_INFO_EX(arginfo_slowlog, 0, 0, 1)
149 ZEND_ARG_INFO(0, arg)
150 ZEND_ARG_INFO(0, option)
151 ZEND_END_ARG_INFO()
152
153 ZEND_BEGIN_ARG_INFO_EX(arginfo_pconnect, 0, 0, 1)
154 ZEND_ARG_INFO(0, host)
155 ZEND_ARG_INFO(0, port)
156 ZEND_ARG_INFO(0, timeout)
157 ZEND_END_ARG_INFO()
158
159 ZEND_BEGIN_ARG_INFO_EX(arginfo_mget, 0, 0, 1)
160 ZEND_ARG_ARRAY_INFO(0, keys, 0)
161 ZEND_END_ARG_INFO()
162
163 ZEND_BEGIN_ARG_INFO_EX(arginfo_exists, 0, 0, 1)
164 ZEND_ARG_INFO(0, key)
165 ZEND_ARG_VARIADIC_INFO(0, other_keys)
166 ZEND_END_ARG_INFO()
167
168 ZEND_BEGIN_ARG_INFO_EX(arginfo_del, 0, 0, 1)
169 ZEND_ARG_INFO(0, key)
170 ZEND_ARG_VARIADIC_INFO(0, other_keys)
171 ZEND_END_ARG_INFO()
172
173 ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 1)
174 ZEND_ARG_INFO(0, pattern)
175 ZEND_END_ARG_INFO()
176
177 ZEND_BEGIN_ARG_INFO_EX(arginfo_generic_sort, 0, 0, 1)
178 ZEND_ARG_INFO(0, key)
179 ZEND_ARG_INFO(0, pattern)
180 ZEND_ARG_INFO(0, get)
181 ZEND_ARG_INFO(0, start)
182 ZEND_ARG_INFO(0, end)
183 ZEND_ARG_INFO(0, getList)
184 ZEND_END_ARG_INFO()
185
186 ZEND_BEGIN_ARG_INFO_EX(arginfo_lrem, 0, 0, 3)
187 ZEND_ARG_INFO(0, key)
188 ZEND_ARG_INFO(0, value)
189 ZEND_ARG_INFO(0, count)
190 ZEND_END_ARG_INFO()
191
192 ZEND_BEGIN_ARG_INFO_EX(arginfo_auth, 0, 0, 1)
193 ZEND_ARG_INFO(0, auth)
194 ZEND_END_ARG_INFO()
195
196 ZEND_BEGIN_ARG_INFO_EX(arginfo_select, 0, 0, 1)
197 ZEND_ARG_INFO(0, dbindex)
198 ZEND_END_ARG_INFO()
199
200 ZEND_BEGIN_ARG_INFO_EX(arginfo_move, 0, 0, 2)
201 ZEND_ARG_INFO(0, key)
202 ZEND_ARG_INFO(0, dbindex)
203 ZEND_END_ARG_INFO()
204
205 ZEND_BEGIN_ARG_INFO_EX(arginfo_slaveof, 0, 0, 0)
206 ZEND_ARG_INFO(0, host)
207 ZEND_ARG_INFO(0, port)
208 ZEND_END_ARG_INFO()
209
210 ZEND_BEGIN_ARG_INFO_EX(arginfo_acl, 0, 0, 1)
211 ZEND_ARG_INFO(0, subcmd)
212 ZEND_ARG_VARIADIC_INFO(0, args)
213 ZEND_END_ARG_INFO()
214
215 /* }}} */
216
217 ZEND_BEGIN_ARG_INFO_EX(arginfo_migrate, 0, 0, 5)
218 ZEND_ARG_INFO(0, host)
219 ZEND_ARG_INFO(0, port)
220 ZEND_ARG_INFO(0, key)
221 ZEND_ARG_INFO(0, db)
222 ZEND_ARG_INFO(0, timeout)
223 ZEND_ARG_INFO(0, copy)
224 ZEND_ARG_INFO(0, replace)
225 ZEND_END_ARG_INFO()
226
227 ZEND_BEGIN_ARG_INFO_EX(arginfo_wait, 0, 0, 2)
228 ZEND_ARG_INFO(0, numslaves)
229 ZEND_ARG_INFO(0, timeout)
230 ZEND_END_ARG_INFO()
231
232 ZEND_BEGIN_ARG_INFO_EX(arginfo_script, 0, 0, 1)
233 ZEND_ARG_INFO(0, cmd)
234 ZEND_ARG_VARIADIC_INFO(0, args)
235 ZEND_END_ARG_INFO()
236
237 /**
238 * Argument info for the SCAN proper
239 */
240 ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 1)
241 ZEND_ARG_INFO(1, i_iterator)
242 ZEND_ARG_INFO(0, str_pattern)
243 ZEND_ARG_INFO(0, i_count)
244 ZEND_END_ARG_INFO()
245
246 /**
247 * Argument info for key scanning
248 */
249 ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2)
250 ZEND_ARG_INFO(0, str_key)
251 ZEND_ARG_INFO(1, i_iterator)
252 ZEND_ARG_INFO(0, str_pattern)
253 ZEND_ARG_INFO(0, i_count)
254 ZEND_END_ARG_INFO()
255
256 static zend_function_entry redis_functions[] = {
257 PHP_ME(Redis, __construct, arginfo_void, ZEND_ACC_PUBLIC)
258 PHP_ME(Redis, __destruct, arginfo_void, ZEND_ACC_PUBLIC)
259 PHP_ME(Redis, _prefix, arginfo_key, ZEND_ACC_PUBLIC)
260 PHP_ME(Redis, _serialize, arginfo_value, ZEND_ACC_PUBLIC)
261 PHP_ME(Redis, _unserialize, arginfo_value, ZEND_ACC_PUBLIC)
262 PHP_ME(Redis, _pack, arginfo_value, ZEND_ACC_PUBLIC)
263 PHP_ME(Redis, _unpack, arginfo_value, ZEND_ACC_PUBLIC)
264 PHP_ME(Redis, _compress, arginfo_value, ZEND_ACC_PUBLIC)
265 PHP_ME(Redis, _uncompress, arginfo_value, ZEND_ACC_PUBLIC)
266 PHP_ME(Redis, acl, arginfo_acl, ZEND_ACC_PUBLIC)
267 PHP_ME(Redis, append, arginfo_key_value, ZEND_ACC_PUBLIC)
268 PHP_ME(Redis, auth, arginfo_auth, ZEND_ACC_PUBLIC)
269 PHP_ME(Redis, bgSave, arginfo_void, ZEND_ACC_PUBLIC)
270 PHP_ME(Redis, bgrewriteaof, arginfo_void, ZEND_ACC_PUBLIC)
271 PHP_ME(Redis, bitcount, arginfo_key, ZEND_ACC_PUBLIC)
272 PHP_ME(Redis, bitop, arginfo_bitop, ZEND_ACC_PUBLIC)
273 PHP_ME(Redis, bitpos, arginfo_bitpos, ZEND_ACC_PUBLIC)
274 PHP_ME(Redis, blPop, arginfo_blrpop, ZEND_ACC_PUBLIC)
275 PHP_ME(Redis, brPop, arginfo_blrpop, ZEND_ACC_PUBLIC)
276 PHP_ME(Redis, brpoplpush, arginfo_brpoplpush, ZEND_ACC_PUBLIC)
277 PHP_ME(Redis, bzPopMax, arginfo_blrpop, ZEND_ACC_PUBLIC)
278 PHP_ME(Redis, bzPopMin, arginfo_blrpop, ZEND_ACC_PUBLIC)
279 PHP_ME(Redis, clearLastError, arginfo_void, ZEND_ACC_PUBLIC)
280 PHP_ME(Redis, client, arginfo_client, ZEND_ACC_PUBLIC)
281 PHP_ME(Redis, close, arginfo_void, ZEND_ACC_PUBLIC)
282 PHP_ME(Redis, command, arginfo_command, ZEND_ACC_PUBLIC)
283 PHP_ME(Redis, config, arginfo_config, ZEND_ACC_PUBLIC)
284 PHP_ME(Redis, connect, arginfo_connect, ZEND_ACC_PUBLIC)
285 PHP_ME(Redis, dbSize, arginfo_void, ZEND_ACC_PUBLIC)
286 PHP_ME(Redis, debug, arginfo_key, ZEND_ACC_PUBLIC)
287 PHP_ME(Redis, decr, arginfo_key, ZEND_ACC_PUBLIC)
288 PHP_ME(Redis, decrBy, arginfo_key_value, ZEND_ACC_PUBLIC)
289 PHP_ME(Redis, del, arginfo_del, ZEND_ACC_PUBLIC)
290 PHP_ME(Redis, discard, arginfo_void, ZEND_ACC_PUBLIC)
291 PHP_ME(Redis, dump, arginfo_key, ZEND_ACC_PUBLIC)
292 PHP_ME(Redis, echo, arginfo_echo, ZEND_ACC_PUBLIC)
293 PHP_ME(Redis, eval, arginfo_eval, ZEND_ACC_PUBLIC)
294 PHP_ME(Redis, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC)
295 PHP_ME(Redis, exec, arginfo_void, ZEND_ACC_PUBLIC)
296 PHP_ME(Redis, exists, arginfo_exists, ZEND_ACC_PUBLIC)
297 PHP_ME(Redis, expire, arginfo_expire, ZEND_ACC_PUBLIC)
298 PHP_ME(Redis, expireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
299 PHP_ME(Redis, flushAll, arginfo_flush, ZEND_ACC_PUBLIC)
300 PHP_ME(Redis, flushDB, arginfo_flush, ZEND_ACC_PUBLIC)
301 PHP_ME(Redis, geoadd, arginfo_geoadd, ZEND_ACC_PUBLIC)
302 PHP_ME(Redis, geodist, arginfo_geodist, ZEND_ACC_PUBLIC)
303 PHP_ME(Redis, geohash, arginfo_key_members, ZEND_ACC_PUBLIC)
304 PHP_ME(Redis, geopos, arginfo_key_members, ZEND_ACC_PUBLIC)
305 PHP_ME(Redis, georadius, arginfo_georadius, ZEND_ACC_PUBLIC)
306 PHP_ME(Redis, georadius_ro, arginfo_georadius, ZEND_ACC_PUBLIC)
307 PHP_ME(Redis, georadiusbymember, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
308 PHP_ME(Redis, georadiusbymember_ro, arginfo_georadiusbymember, ZEND_ACC_PUBLIC)
309 PHP_ME(Redis, get, arginfo_key, ZEND_ACC_PUBLIC)
310 PHP_ME(Redis, getAuth, arginfo_void, ZEND_ACC_PUBLIC)
311 PHP_ME(Redis, getBit, arginfo_key_offset, ZEND_ACC_PUBLIC)
312 PHP_ME(Redis, getDBNum, arginfo_void, ZEND_ACC_PUBLIC)
313 PHP_ME(Redis, getHost, arginfo_void, ZEND_ACC_PUBLIC)
314 PHP_ME(Redis, getLastError, arginfo_void, ZEND_ACC_PUBLIC)
315 PHP_ME(Redis, getMode, arginfo_void, ZEND_ACC_PUBLIC)
316 PHP_ME(Redis, getOption, arginfo_getoption, ZEND_ACC_PUBLIC)
317 PHP_ME(Redis, getPersistentID, arginfo_void, ZEND_ACC_PUBLIC)
318 PHP_ME(Redis, getPort, arginfo_void, ZEND_ACC_PUBLIC)
319 PHP_ME(Redis, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
320 PHP_ME(Redis, getReadTimeout, arginfo_void, ZEND_ACC_PUBLIC)
321 PHP_ME(Redis, getSet, arginfo_key_value, ZEND_ACC_PUBLIC)
322 PHP_ME(Redis, getTimeout, arginfo_void, ZEND_ACC_PUBLIC)
323 PHP_ME(Redis, hDel, arginfo_key_members, ZEND_ACC_PUBLIC)
324 PHP_ME(Redis, hExists, arginfo_key_member, ZEND_ACC_PUBLIC)
325 PHP_ME(Redis, hGet, arginfo_key_member, ZEND_ACC_PUBLIC)
326 PHP_ME(Redis, hGetAll, arginfo_key, ZEND_ACC_PUBLIC)
327 PHP_ME(Redis, hIncrBy, arginfo_key_member_value, ZEND_ACC_PUBLIC)
328 PHP_ME(Redis, hIncrByFloat, arginfo_key_member_value, ZEND_ACC_PUBLIC)
329 PHP_ME(Redis, hKeys, arginfo_key, ZEND_ACC_PUBLIC)
330 PHP_ME(Redis, hLen, arginfo_key, ZEND_ACC_PUBLIC)
331 PHP_ME(Redis, hMget, arginfo_hmget, ZEND_ACC_PUBLIC)
332 PHP_ME(Redis, hMset, arginfo_hmset, ZEND_ACC_PUBLIC)
333 PHP_ME(Redis, hSet, arginfo_key_member_value, ZEND_ACC_PUBLIC)
334 PHP_ME(Redis, hSetNx, arginfo_key_member_value, ZEND_ACC_PUBLIC)
335 PHP_ME(Redis, hStrLen, arginfo_key_member, ZEND_ACC_PUBLIC)
336 PHP_ME(Redis, hVals, arginfo_key, ZEND_ACC_PUBLIC)
337 PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC)
338 PHP_ME(Redis, incr, arginfo_key, ZEND_ACC_PUBLIC)
339 PHP_ME(Redis, incrBy, arginfo_key_value, ZEND_ACC_PUBLIC)
340 PHP_ME(Redis, incrByFloat, arginfo_key_value, ZEND_ACC_PUBLIC)
341 PHP_ME(Redis, info, arginfo_info, ZEND_ACC_PUBLIC)
342 PHP_ME(Redis, isConnected, arginfo_void, ZEND_ACC_PUBLIC)
343 PHP_ME(Redis, keys, arginfo_keys, ZEND_ACC_PUBLIC)
344 PHP_ME(Redis, lInsert, arginfo_linsert, ZEND_ACC_PUBLIC)
345 PHP_ME(Redis, lLen, arginfo_key, ZEND_ACC_PUBLIC)
346 PHP_ME(Redis, lPop, arginfo_key, ZEND_ACC_PUBLIC)
347 PHP_ME(Redis, lPush, arginfo_key_value, ZEND_ACC_PUBLIC)
348 PHP_ME(Redis, lPushx, arginfo_key_value, ZEND_ACC_PUBLIC)
349 PHP_ME(Redis, lSet, arginfo_lset, ZEND_ACC_PUBLIC)
350 PHP_ME(Redis, lastSave, arginfo_void, ZEND_ACC_PUBLIC)
351 PHP_ME(Redis, lindex, arginfo_lindex, ZEND_ACC_PUBLIC)
352 PHP_ME(Redis, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC)
353 PHP_ME(Redis, lrem, arginfo_lrem, ZEND_ACC_PUBLIC)
354 PHP_ME(Redis, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC)
355 PHP_ME(Redis, mget, arginfo_mget, ZEND_ACC_PUBLIC)
356 PHP_ME(Redis, migrate, arginfo_migrate, ZEND_ACC_PUBLIC)
357 PHP_ME(Redis, move, arginfo_move, ZEND_ACC_PUBLIC)
358 PHP_ME(Redis, mset, arginfo_pairs, ZEND_ACC_PUBLIC)
359 PHP_ME(Redis, msetnx, arginfo_pairs, ZEND_ACC_PUBLIC)
360 PHP_ME(Redis, multi, arginfo_multi, ZEND_ACC_PUBLIC)
361 PHP_ME(Redis, object, arginfo_object, ZEND_ACC_PUBLIC)
362 PHP_ME(Redis, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC)
363 PHP_ME(Redis, persist, arginfo_key, ZEND_ACC_PUBLIC)
364 PHP_ME(Redis, pexpire, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
365 PHP_ME(Redis, pexpireAt, arginfo_key_timestamp, ZEND_ACC_PUBLIC)
366 PHP_ME(Redis, pfadd, arginfo_pfadd, ZEND_ACC_PUBLIC)
367 PHP_ME(Redis, pfcount, arginfo_key, ZEND_ACC_PUBLIC)
368 PHP_ME(Redis, pfmerge, arginfo_pfmerge, ZEND_ACC_PUBLIC)
369 PHP_ME(Redis, ping, arginfo_void, ZEND_ACC_PUBLIC)
370 PHP_ME(Redis, pipeline, arginfo_void, ZEND_ACC_PUBLIC)
371 PHP_ME(Redis, psetex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
372 PHP_ME(Redis, psubscribe, arginfo_psubscribe, ZEND_ACC_PUBLIC)
373 PHP_ME(Redis, pttl, arginfo_key, ZEND_ACC_PUBLIC)
374 PHP_ME(Redis, publish, arginfo_publish, ZEND_ACC_PUBLIC)
375 PHP_ME(Redis, pubsub, arginfo_pubsub, ZEND_ACC_PUBLIC)
376 PHP_ME(Redis, punsubscribe, arginfo_punsubscribe, ZEND_ACC_PUBLIC)
377 PHP_ME(Redis, rPop, arginfo_key, ZEND_ACC_PUBLIC)
378 PHP_ME(Redis, rPush, arginfo_key_value, ZEND_ACC_PUBLIC)
379 PHP_ME(Redis, rPushx, arginfo_key_value, ZEND_ACC_PUBLIC)
380 PHP_ME(Redis, randomKey, arginfo_void, ZEND_ACC_PUBLIC)
381 PHP_ME(Redis, rawcommand, arginfo_rawcommand, ZEND_ACC_PUBLIC)
382 PHP_ME(Redis, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC)
383 PHP_ME(Redis, renameNx, arginfo_key_newkey, ZEND_ACC_PUBLIC)
384 PHP_ME(Redis, restore, arginfo_restore, ZEND_ACC_PUBLIC)
385 PHP_ME(Redis, role, arginfo_void, ZEND_ACC_PUBLIC)
386 PHP_ME(Redis, rpoplpush, arginfo_rpoplpush, ZEND_ACC_PUBLIC)
387 PHP_ME(Redis, sAdd, arginfo_key_value, ZEND_ACC_PUBLIC)
388 PHP_ME(Redis, sAddArray, arginfo_sadd_array, ZEND_ACC_PUBLIC)
389 PHP_ME(Redis, sDiff, arginfo_nkeys, ZEND_ACC_PUBLIC)
390 PHP_ME(Redis, sDiffStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
391 PHP_ME(Redis, sInter, arginfo_nkeys, ZEND_ACC_PUBLIC)
392 PHP_ME(Redis, sInterStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
393 PHP_ME(Redis, sMembers, arginfo_key, ZEND_ACC_PUBLIC)
394 PHP_ME(Redis, sMisMember, arginfo_key_members, ZEND_ACC_PUBLIC)
395 PHP_ME(Redis, sMove, arginfo_smove, ZEND_ACC_PUBLIC)
396 PHP_ME(Redis, sPop, arginfo_key, ZEND_ACC_PUBLIC)
397 PHP_ME(Redis, sRandMember, arginfo_srand_member, ZEND_ACC_PUBLIC)
398 PHP_ME(Redis, sUnion, arginfo_nkeys, ZEND_ACC_PUBLIC)
399 PHP_ME(Redis, sUnionStore, arginfo_dst_nkeys, ZEND_ACC_PUBLIC)
400 PHP_ME(Redis, save, arginfo_void, ZEND_ACC_PUBLIC)
401 PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC)
402 PHP_ME(Redis, scard, arginfo_key, ZEND_ACC_PUBLIC)
403 PHP_ME(Redis, script, arginfo_script, ZEND_ACC_PUBLIC)
404 PHP_ME(Redis, select, arginfo_select, ZEND_ACC_PUBLIC)
405 PHP_ME(Redis, set, arginfo_set, ZEND_ACC_PUBLIC)
406 PHP_ME(Redis, setBit, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
407 PHP_ME(Redis, setOption, arginfo_setoption, ZEND_ACC_PUBLIC)
408 PHP_ME(Redis, setRange, arginfo_key_offset_value, ZEND_ACC_PUBLIC)
409 PHP_ME(Redis, setex, arginfo_key_expire_value, ZEND_ACC_PUBLIC)
410 PHP_ME(Redis, setnx, arginfo_key_value, ZEND_ACC_PUBLIC)
411 PHP_ME(Redis, sismember, arginfo_key_value, ZEND_ACC_PUBLIC)
412 PHP_ME(Redis, slaveof, arginfo_slaveof, ZEND_ACC_PUBLIC)
413 PHP_ME(Redis, slowlog, arginfo_slowlog, ZEND_ACC_PUBLIC)
414 PHP_ME(Redis, sort, arginfo_sort, ZEND_ACC_PUBLIC)
415 PHP_ME(Redis, sortAsc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
416 PHP_ME(Redis, sortAscAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
417 PHP_ME(Redis, sortDesc, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
418 PHP_ME(Redis, sortDescAlpha, arginfo_generic_sort, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
419 PHP_ME(Redis, srem, arginfo_key_members, ZEND_ACC_PUBLIC)
420 PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
421 PHP_ME(Redis, strlen, arginfo_key, ZEND_ACC_PUBLIC)
422 PHP_ME(Redis, subscribe, arginfo_subscribe, ZEND_ACC_PUBLIC)
423 PHP_ME(Redis, swapdb, arginfo_swapdb, ZEND_ACC_PUBLIC)
424 PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC)
425 PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC)
426 PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC)
427 PHP_ME(Redis, unlink, arginfo_nkeys, ZEND_ACC_PUBLIC)
428 PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
429 PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
430 PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC)
431 PHP_ME(Redis, watch, arginfo_watch, ZEND_ACC_PUBLIC)
432 PHP_ME(Redis, xack, arginfo_xack, ZEND_ACC_PUBLIC)
433 PHP_ME(Redis, xadd, arginfo_xadd, ZEND_ACC_PUBLIC)
434 PHP_ME(Redis, xclaim, arginfo_xclaim, ZEND_ACC_PUBLIC)
435 PHP_ME(Redis, xdel, arginfo_xdel, ZEND_ACC_PUBLIC)
436 PHP_ME(Redis, xgroup, arginfo_xgroup, ZEND_ACC_PUBLIC)
437 PHP_ME(Redis, xinfo, arginfo_xinfo, ZEND_ACC_PUBLIC)
438 PHP_ME(Redis, xlen, arginfo_key, ZEND_ACC_PUBLIC)
439 PHP_ME(Redis, xpending, arginfo_xpending, ZEND_ACC_PUBLIC)
440 PHP_ME(Redis, xrange, arginfo_xrange, ZEND_ACC_PUBLIC)
441 PHP_ME(Redis, xread, arginfo_xread, ZEND_ACC_PUBLIC)
442 PHP_ME(Redis, xreadgroup, arginfo_xreadgroup, ZEND_ACC_PUBLIC)
443 PHP_ME(Redis, xrevrange, arginfo_xrange, ZEND_ACC_PUBLIC)
444 PHP_ME(Redis, xtrim, arginfo_xtrim, ZEND_ACC_PUBLIC)
445 PHP_ME(Redis, zAdd, arginfo_zadd, ZEND_ACC_PUBLIC)
446 PHP_ME(Redis, zCard, arginfo_key, ZEND_ACC_PUBLIC)
447 PHP_ME(Redis, zCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
448 PHP_ME(Redis, zIncrBy, arginfo_zincrby, ZEND_ACC_PUBLIC)
449 PHP_ME(Redis, zLexCount, arginfo_key_min_max, ZEND_ACC_PUBLIC)
450 PHP_ME(Redis, zPopMax, arginfo_key, ZEND_ACC_PUBLIC)
451 PHP_ME(Redis, zPopMin, arginfo_key, ZEND_ACC_PUBLIC)
452 PHP_ME(Redis, zRange, arginfo_zrange, ZEND_ACC_PUBLIC)
453 PHP_ME(Redis, zRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
454 PHP_ME(Redis, zRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
455 PHP_ME(Redis, zRank, arginfo_key_member, ZEND_ACC_PUBLIC)
456 PHP_ME(Redis, zRem, arginfo_key_members, ZEND_ACC_PUBLIC)
457 PHP_ME(Redis, zRemRangeByLex, arginfo_key_min_max, ZEND_ACC_PUBLIC)
458 PHP_ME(Redis, zRemRangeByRank, arginfo_key_start_end, ZEND_ACC_PUBLIC)
459 PHP_ME(Redis, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC)
460 PHP_ME(Redis, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC)
461 PHP_ME(Redis, zRevRangeByLex, arginfo_zrangebylex, ZEND_ACC_PUBLIC)
462 PHP_ME(Redis, zRevRangeByScore, arginfo_zrangebyscore, ZEND_ACC_PUBLIC)
463 PHP_ME(Redis, zRevRank, arginfo_key_member, ZEND_ACC_PUBLIC)
464 PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC)
465 PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC)
466 PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
467 PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC)
468
469 /* Mark all of these aliases deprecated. They aren't actual Redis commands. */
470 PHP_MALIAS(Redis, delete, del, arginfo_del, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
471 PHP_MALIAS(Redis, evaluate, eval, arginfo_eval, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
472 PHP_MALIAS(Redis, evaluateSha, evalsha, arginfo_evalsha, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
473 PHP_MALIAS(Redis, getKeys, keys, arginfo_keys, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
474 PHP_MALIAS(Redis, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
475 PHP_MALIAS(Redis, lGet, lindex, arginfo_lindex, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
476 PHP_MALIAS(Redis, lGetRange, lrange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
477 PHP_MALIAS(Redis, lRemove, lrem, arginfo_lrem, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
478 PHP_MALIAS(Redis, lSize, lLen, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
479 PHP_MALIAS(Redis, listTrim, ltrim, arginfo_ltrim, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
480 PHP_MALIAS(Redis, open, connect, arginfo_connect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
481 PHP_MALIAS(Redis, popen, pconnect, arginfo_pconnect, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
482 PHP_MALIAS(Redis, renameKey, rename, arginfo_key_newkey, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
483 PHP_MALIAS(Redis, sContains, sismember, arginfo_key_value, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
484 PHP_MALIAS(Redis, sGetMembers, sMembers, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
485 PHP_MALIAS(Redis, sRemove, srem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
486 PHP_MALIAS(Redis, sSize, scard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
487 PHP_MALIAS(Redis, sendEcho, echo, arginfo_echo, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
488 PHP_MALIAS(Redis, setTimeout, expire, arginfo_expire, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
489 PHP_MALIAS(Redis, substr, getRange, arginfo_key_start_end, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
490 PHP_MALIAS(Redis, zDelete, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED | ZEND_ACC_DEPRECATED)
491 PHP_MALIAS(Redis, zDeleteRangeByRank, zRemRangeByRank, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
492 PHP_MALIAS(Redis, zDeleteRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
493 PHP_MALIAS(Redis, zInter, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
494 PHP_MALIAS(Redis, zRemove, zRem, arginfo_key_members, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
495 PHP_MALIAS(Redis, zRemoveRangeByScore, zRemRangeByScore, arginfo_key_min_max, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
496 PHP_MALIAS(Redis, zReverseRange, zRevRange, arginfo_zrange, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
497 PHP_MALIAS(Redis, zSize, zCard, arginfo_key, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
498 PHP_MALIAS(Redis, zUnion, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED)
499 PHP_FE_END
500 };
501
502 static const zend_module_dep redis_deps[] = {
503 #ifdef HAVE_REDIS_IGBINARY
504 ZEND_MOD_REQUIRED("igbinary")
505 #endif
506 #ifdef HAVE_REDIS_MSGPACK
507 ZEND_MOD_REQUIRED("msgpack")
508 #endif
509 #ifdef HAVE_REDIS_JSON
510 ZEND_MOD_REQUIRED("json")
511 #endif
512 #ifdef PHP_SESSION
513 ZEND_MOD_REQUIRED("session")
514 #endif
515 ZEND_MOD_END
516 };
517
518 ZEND_DECLARE_MODULE_GLOBALS(redis)
519 static PHP_GINIT_FUNCTION(redis);
520
521 zend_module_entry redis_module_entry = {
522 STANDARD_MODULE_HEADER_EX,
523 NULL,
524 redis_deps,
525 "redis",
526 NULL,
527 PHP_MINIT(redis),
528 NULL,
529 NULL,
530 NULL,
531 PHP_MINFO(redis),
532 PHP_REDIS_VERSION,
533 PHP_MODULE_GLOBALS(redis),
534 PHP_GINIT(redis),
535 NULL,
536 NULL,
537 STANDARD_MODULE_PROPERTIES_EX
538 };
539
540 #ifdef COMPILE_DL_REDIS
ZEND_GET_MODULE(redis)541 ZEND_GET_MODULE(redis)
542 #endif
543
544 zend_object_handlers redis_object_handlers;
545
546 /* Send a static DISCARD in case we're in MULTI mode. */
547 static int
548 redis_send_discard(RedisSock *redis_sock)
549 {
550 int result = FAILURE;
551 char *cmd, *resp;
552 int resp_len, cmd_len;
553
554 /* format our discard command */
555 cmd_len = REDIS_SPPRINTF(&cmd, "DISCARD", "");
556
557 /* send our DISCARD command */
558 if (redis_sock_write(redis_sock, cmd, cmd_len) >= 0 &&
559 (resp = redis_sock_read(redis_sock,&resp_len)) != NULL)
560 {
561 /* success if we get OK */
562 result = (resp_len == 3 && strncmp(resp,"+OK", 3) == 0) ? SUCCESS:FAILURE;
563
564 /* free our response */
565 efree(resp);
566 }
567
568 /* free our command */
569 efree(cmd);
570
571 /* return success/failure */
572 return result;
573 }
574
575 static void
free_reply_callbacks(RedisSock * redis_sock)576 free_reply_callbacks(RedisSock *redis_sock)
577 {
578 fold_item *fi;
579
580 for (fi = redis_sock->head; fi; ) {
581 fold_item *fi_next = fi->next;
582 free(fi);
583 fi = fi_next;
584 }
585 redis_sock->head = NULL;
586 redis_sock->current = NULL;
587 }
588
589 /* Passthru for destroying cluster cache */
cluster_cache_dtor(zend_resource * rsrc)590 static void cluster_cache_dtor(zend_resource *rsrc) {
591 if (rsrc->ptr) {
592 cluster_cache_free(rsrc->ptr);
593 }
594 }
595
596 void
free_redis_object(zend_object * object)597 free_redis_object(zend_object *object)
598 {
599 redis_object *redis = PHPREDIS_GET_OBJECT(redis_object, object);
600
601 zend_object_std_dtor(&redis->std);
602 if (redis->sock) {
603 redis_sock_disconnect(redis->sock, 0);
604 redis_free_socket(redis->sock);
605 }
606 }
607
608 zend_object *
create_redis_object(zend_class_entry * ce)609 create_redis_object(zend_class_entry *ce)
610 {
611 redis_object *redis = ecalloc(1, sizeof(redis_object) + zend_object_properties_size(ce));
612
613 redis->sock = NULL;
614
615 zend_object_std_init(&redis->std, ce);
616 object_properties_init(&redis->std, ce);
617
618 memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers));
619 redis_object_handlers.offset = XtOffsetOf(redis_object, std);
620 redis_object_handlers.free_obj = free_redis_object;
621 redis_object_handlers.clone_obj = NULL;
622 redis->std.handlers = &redis_object_handlers;
623
624 return &redis->std;
625 }
626
627 static zend_always_inline RedisSock *
redis_sock_get_instance(zval * id,int no_throw)628 redis_sock_get_instance(zval *id, int no_throw)
629 {
630 redis_object *redis;
631
632 if (Z_TYPE_P(id) == IS_OBJECT) {
633 redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, id);
634 if (redis->sock) {
635 return redis->sock;
636 }
637 }
638 // Throw an exception unless we've been requested not to
639 if (!no_throw) {
640 REDIS_THROW_EXCEPTION("Redis server went away", 0);
641 }
642 return NULL;
643 }
644
645 /**
646 * redis_sock_get
647 */
648 PHP_REDIS_API RedisSock *
redis_sock_get(zval * id,int no_throw)649 redis_sock_get(zval *id, int no_throw)
650 {
651 RedisSock *redis_sock;
652
653 if ((redis_sock = redis_sock_get_instance(id, no_throw)) == NULL) {
654 return NULL;
655 }
656
657 if (redis_sock_server_open(redis_sock) < 0) {
658 if (!no_throw) {
659 char *errmsg = NULL;
660 if (redis_sock->port < 0) {
661 spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host));
662 } else {
663 spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port);
664 }
665 REDIS_THROW_EXCEPTION(errmsg, 0);
666 efree(errmsg);
667 }
668 return NULL;
669 }
670
671 return redis_sock;
672 }
673
674 /**
675 * redis_sock_get_direct
676 * Returns our attached RedisSock pointer if we're connected
677 */
redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS)678 PHP_REDIS_API RedisSock *redis_sock_get_connected(INTERNAL_FUNCTION_PARAMETERS) {
679 zval *object;
680 RedisSock *redis_sock;
681
682 // If we can't grab our object, or get a socket, or we're not connected,
683 // return NULL
684 if((zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
685 &object, redis_ce) == FAILURE) ||
686 (redis_sock = redis_sock_get(object, 1)) == NULL ||
687 redis_sock->status < REDIS_SOCK_STATUS_CONNECTED)
688 {
689 return NULL;
690 }
691
692 /* Return our socket */
693 return redis_sock;
694 }
695
696 /* Redis and RedisCluster objects share serialization/prefixing settings so
697 * this is a generic function to add class constants to either */
add_class_constants(zend_class_entry * ce,int is_cluster)698 static void add_class_constants(zend_class_entry *ce, int is_cluster) {
699 zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_NOT_FOUND"), REDIS_NOT_FOUND);
700 zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STRING"), REDIS_STRING);
701 zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_SET"), REDIS_SET);
702 zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_LIST"), REDIS_LIST);
703 zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_ZSET"), REDIS_ZSET);
704 zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_HASH"), REDIS_HASH);
705 zend_declare_class_constant_long(ce, ZEND_STRL("REDIS_STREAM"), REDIS_STREAM);
706
707 /* Cluster doesn't support pipelining at this time */
708 if(!is_cluster) {
709 zend_declare_class_constant_long(ce, ZEND_STRL("PIPELINE"), PIPELINE);
710 }
711
712 /* Add common mode constants */
713 zend_declare_class_constant_long(ce, ZEND_STRL("ATOMIC"), ATOMIC);
714 zend_declare_class_constant_long(ce, ZEND_STRL("MULTI"), MULTI);
715
716 /* options */
717 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SERIALIZER"), REDIS_OPT_SERIALIZER);
718 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_PREFIX"), REDIS_OPT_PREFIX);
719 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_READ_TIMEOUT"), REDIS_OPT_READ_TIMEOUT);
720 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_TCP_KEEPALIVE"), REDIS_OPT_TCP_KEEPALIVE);
721 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION"), REDIS_OPT_COMPRESSION);
722 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_REPLY_LITERAL"), REDIS_OPT_REPLY_LITERAL);
723 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_COMPRESSION_LEVEL"), REDIS_OPT_COMPRESSION_LEVEL);
724 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_NULL_MULTIBULK_AS_NULL"), REDIS_OPT_NULL_MBULK_AS_NULL);
725
726 /* serializer */
727 zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_NONE"), REDIS_SERIALIZER_NONE);
728 zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_PHP"), REDIS_SERIALIZER_PHP);
729 #ifdef HAVE_REDIS_IGBINARY
730 zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_IGBINARY"), REDIS_SERIALIZER_IGBINARY);
731 #endif
732 #ifdef HAVE_REDIS_MSGPACK
733 zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_MSGPACK"), REDIS_SERIALIZER_MSGPACK);
734 #endif
735 zend_declare_class_constant_long(ce, ZEND_STRL("SERIALIZER_JSON"), REDIS_SERIALIZER_JSON);
736
737 /* compression */
738 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_NONE"), REDIS_COMPRESSION_NONE);
739 #ifdef HAVE_REDIS_LZF
740 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZF"), REDIS_COMPRESSION_LZF);
741 #endif
742 #ifdef HAVE_REDIS_ZSTD
743 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD"), REDIS_COMPRESSION_ZSTD);
744 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MIN"), 1);
745 #ifdef ZSTD_CLEVEL_DEFAULT
746 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), ZSTD_CLEVEL_DEFAULT);
747 #else
748 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_DEFAULT"), 3);
749 #endif
750 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_ZSTD_MAX"), ZSTD_maxCLevel());
751 #endif
752
753 #ifdef HAVE_REDIS_LZ4
754 zend_declare_class_constant_long(ce, ZEND_STRL("COMPRESSION_LZ4"), REDIS_COMPRESSION_LZ4);
755 #endif
756
757 /* scan options*/
758 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN);
759 zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY);
760 zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY);
761 zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_PREFIX"), REDIS_SCAN_PREFIX);
762 zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NOPREFIX"), REDIS_SCAN_NOPREFIX);
763
764 zend_declare_class_constant_stringl(ce, "AFTER", 5, "after", 5);
765 zend_declare_class_constant_stringl(ce, "BEFORE", 6, "before", 6);
766
767 /* Cluster option to allow for slave failover */
768 if (is_cluster) {
769 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SLAVE_FAILOVER"), REDIS_OPT_FAILOVER);
770 zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_NONE"), REDIS_FAILOVER_NONE);
771 zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_ERROR"), REDIS_FAILOVER_ERROR);
772 zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE"), REDIS_FAILOVER_DISTRIBUTE);
773 zend_declare_class_constant_long(ce, ZEND_STRL("FAILOVER_DISTRIBUTE_SLAVES"), REDIS_FAILOVER_DISTRIBUTE_SLAVES);
774 }
775
776 /* retry/backoff options*/
777 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_MAX_RETRIES"), REDIS_OPT_MAX_RETRIES);
778
779 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_ALGORITHM"), REDIS_OPT_BACKOFF_ALGORITHM);
780 zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_DEFAULT"), REDIS_BACKOFF_ALGORITHM_DEFAULT);
781 zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_CONSTANT"), REDIS_BACKOFF_ALGORITHM_CONSTANT);
782 zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_UNIFORM"), REDIS_BACKOFF_ALGORITHM_UNIFORM);
783 zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_EXPONENTIAL"), REDIS_BACKOFF_ALGORITHM_EXPONENTIAL);
784 zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_FULL_JITTER"), REDIS_BACKOFF_ALGORITHM_FULL_JITTER);
785 zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_EQUAL_JITTER"), REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER);
786 zend_declare_class_constant_long(ce, ZEND_STRL("BACKOFF_ALGORITHM_DECORRELATED_JITTER"), REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER);
787
788 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_BASE"), REDIS_OPT_BACKOFF_BASE);
789
790 zend_declare_class_constant_long(ce, ZEND_STRL("OPT_BACKOFF_CAP"), REDIS_OPT_BACKOFF_CAP);
791 }
792
ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor)793 static ZEND_RSRC_DTOR_FUNC(redis_connections_pool_dtor)
794 {
795 if (res->ptr) {
796 ConnectionPool *p = res->ptr;
797 zend_llist_destroy(&p->list);
798 pefree(res->ptr, 1);
799 }
800 }
801
redis_random_hex_bytes(char * dst,size_t dstsize)802 static void redis_random_hex_bytes(char *dst, size_t dstsize) {
803 char chunk[9], *ptr = dst;
804 ssize_t rem = dstsize, len, clen;
805 size_t bytes;
806
807 /* We need two characters per hex byte */
808 bytes = dstsize / 2;
809 zend_string *s = zend_string_alloc(bytes, 0);
810
811 /* First try to have PHP generate the bytes */
812 if (php_random_bytes_silent(ZSTR_VAL(s), bytes) == SUCCESS) {
813 php_hash_bin2hex(dst, (unsigned char *)ZSTR_VAL(s), bytes);
814 zend_string_release(s);
815 return;
816 }
817
818 /* PHP shouldn't have failed, but generate manually if it did. */
819 while (rem > 0) {
820 clen = snprintf(chunk, sizeof(chunk), "%08x", rand());
821 len = rem >= clen ? clen : rem;
822 memcpy(ptr, chunk, len);
823 ptr += len; rem -= len;
824 }
825
826 zend_string_release(s);
827 }
828
PHP_GINIT_FUNCTION(redis)829 static PHP_GINIT_FUNCTION(redis)
830 {
831 redis_random_hex_bytes(redis_globals->salt, sizeof(redis_globals->salt) - 1);
832 redis_globals->salt[sizeof(redis_globals->salt)-1] = '\0';
833 }
834
835 /**
836 * PHP_MINIT_FUNCTION
837 */
PHP_MINIT_FUNCTION(redis)838 PHP_MINIT_FUNCTION(redis)
839 {
840 struct timeval tv;
841
842 zend_class_entry redis_class_entry;
843 zend_class_entry redis_array_class_entry;
844 zend_class_entry redis_cluster_class_entry;
845 zend_class_entry redis_sentinel_class_entry;
846 zend_class_entry redis_exception_class_entry;
847 zend_class_entry redis_cluster_exception_class_entry;
848
849 zend_class_entry *exception_ce = NULL;
850
851 /* Seed random generator (for RedisCluster failover) */
852 gettimeofday(&tv, NULL);
853 srand(tv.tv_usec * tv.tv_sec);
854
855 REGISTER_INI_ENTRIES();
856
857 /* Redis class */
858 INIT_CLASS_ENTRY(redis_class_entry, "Redis", redis_functions);
859 redis_ce = zend_register_internal_class(&redis_class_entry);
860 redis_ce->create_object = create_redis_object;
861
862 /* RedisArray class */
863 INIT_CLASS_ENTRY(redis_array_class_entry, "RedisArray", redis_array_functions);
864 redis_array_ce = zend_register_internal_class(&redis_array_class_entry);
865 redis_array_ce->create_object = create_redis_array_object;
866
867 /* RedisCluster class */
868 INIT_CLASS_ENTRY(redis_cluster_class_entry, "RedisCluster", redis_cluster_functions);
869 redis_cluster_ce = zend_register_internal_class(&redis_cluster_class_entry);
870 redis_cluster_ce->create_object = create_cluster_context;
871
872 /* RedisSentinel class */
873 INIT_CLASS_ENTRY(redis_sentinel_class_entry, "RedisSentinel", redis_sentinel_functions);
874 redis_sentinel_ce = zend_register_internal_class(&redis_sentinel_class_entry);
875 redis_sentinel_ce->create_object = create_sentinel_object;
876
877 /* Register our cluster cache list item */
878 le_cluster_slot_cache = zend_register_list_destructors_ex(NULL, cluster_cache_dtor,
879 "Redis cluster slot cache",
880 module_number);
881
882 /* Base Exception class */
883 exception_ce = zend_hash_str_find_ptr(CG(class_table), "RuntimeException", sizeof("RuntimeException") - 1);
884 if (exception_ce == NULL) {
885 exception_ce = zend_exception_get_default();
886 }
887
888 /* RedisException class */
889 INIT_CLASS_ENTRY(redis_exception_class_entry, "RedisException", NULL);
890 redis_exception_ce = zend_register_internal_class_ex(
891 &redis_exception_class_entry,
892 exception_ce);
893
894 /* RedisClusterException class */
895 INIT_CLASS_ENTRY(redis_cluster_exception_class_entry,
896 "RedisClusterException", NULL);
897 redis_cluster_exception_ce = zend_register_internal_class_ex(
898 &redis_cluster_exception_class_entry, exception_ce);
899
900 /* Add shared class constants to Redis and RedisCluster objects */
901 add_class_constants(redis_ce, 0);
902 add_class_constants(redis_cluster_ce, 1);
903
904 #ifdef PHP_SESSION
905 php_session_register_module(&ps_mod_redis);
906 php_session_register_module(&ps_mod_redis_cluster);
907 #endif
908
909 /* Register resource destructors */
910 le_redis_pconnect = zend_register_list_destructors_ex(NULL, redis_connections_pool_dtor,
911 "phpredis persistent connections pool", module_number);
912
913 return SUCCESS;
914 }
915
916 static const char *
get_available_serializers(void)917 get_available_serializers(void)
918 {
919 #ifdef HAVE_REDIS_JSON
920 #ifdef HAVE_REDIS_IGBINARY
921 #ifdef HAVE_REDIS_MSGPACK
922 return "php, json, igbinary, msgpack";
923 #else
924 return "php, json, igbinary";
925 #endif
926 #else
927 #ifdef HAVE_REDIS_MSGPACK
928 return "php, json, msgpack";
929 #else
930 return "php, json";
931 #endif
932 #endif
933 #else
934 #ifdef HAVE_REDIS_IGBINARY
935 #ifdef HAVE_REDIS_MSGPACK
936 return "php, igbinary, msgpack";
937 #else
938 return "php, igbinary";
939 #endif
940 #else
941 #ifdef HAVE_REDIS_MSGPACK
942 return "php, msgpack";
943 #else
944 return "php";
945 #endif
946 #endif
947 #endif
948 }
949
950 /**
951 * PHP_MINFO_FUNCTION
952 */
PHP_MINFO_FUNCTION(redis)953 PHP_MINFO_FUNCTION(redis)
954 {
955 smart_str names = {0,};
956
957 php_info_print_table_start();
958 php_info_print_table_header(2, "Redis Support", "enabled");
959 php_info_print_table_row(2, "Redis Version", PHP_REDIS_VERSION);
960 php_info_print_table_row(2, "Redis Sentinel Version", PHP_REDIS_SENTINEL_VERSION);
961 #ifdef GIT_REVISION
962 php_info_print_table_row(2, "Git revision", "$Id: " GIT_REVISION " $");
963 #endif
964 php_info_print_table_row(2, "Available serializers", get_available_serializers());
965 #ifdef HAVE_REDIS_LZF
966 smart_str_appends(&names, "lzf");
967 #endif
968 #ifdef HAVE_REDIS_ZSTD
969 if (names.s) {
970 smart_str_appends(&names, ", ");
971 }
972 smart_str_appends(&names, "zstd");
973 #endif
974 #ifdef HAVE_REDIS_LZ4
975 if (names.s) {
976 smart_str_appends(&names, ", ");
977 }
978 smart_str_appends(&names, "lz4");
979 #endif
980 if (names.s) {
981 smart_str_0(&names);
982 php_info_print_table_row(2, "Available compression", ZSTR_VAL(names.s));
983 }
984 smart_str_free(&names);
985 php_info_print_table_end();
986
987 DISPLAY_INI_ENTRIES();
988 }
989
990 /* {{{ proto Redis Redis::__construct()
991 Public constructor */
PHP_METHOD(Redis,__construct)992 PHP_METHOD(Redis, __construct)
993 {
994 if (zend_parse_parameters_none() == FAILURE) {
995 RETURN_FALSE;
996 }
997 }
998 /* }}} */
999
1000 /* {{{ proto Redis Redis::__destruct()
1001 Public Destructor
1002 */
PHP_METHOD(Redis,__destruct)1003 PHP_METHOD(Redis,__destruct) {
1004 if (zend_parse_parameters_none() == FAILURE) {
1005 RETURN_FALSE;
1006 }
1007
1008 // Grab our socket
1009 RedisSock *redis_sock;
1010 if ((redis_sock = redis_sock_get_instance(getThis(), 1)) == NULL) {
1011 RETURN_FALSE;
1012 }
1013
1014 // If we think we're in MULTI mode, send a discard
1015 if (IS_MULTI(redis_sock)) {
1016 if (!IS_PIPELINE(redis_sock) && redis_sock->stream) {
1017 // Discard any multi commands, and free any callbacks that have been
1018 // queued
1019 redis_send_discard(redis_sock);
1020 }
1021 free_reply_callbacks(redis_sock);
1022 }
1023 }
1024
1025 /* {{{ proto boolean Redis::connect(string host, int port [, double timeout [, long retry_interval]])
1026 */
PHP_METHOD(Redis,connect)1027 PHP_METHOD(Redis, connect)
1028 {
1029 if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0) == FAILURE) {
1030 RETURN_FALSE;
1031 } else {
1032 RETURN_TRUE;
1033 }
1034 }
1035 /* }}} */
1036
1037 /* {{{ proto boolean Redis::pconnect(string host, int port [, double timeout])
1038 */
PHP_METHOD(Redis,pconnect)1039 PHP_METHOD(Redis, pconnect)
1040 {
1041 if (redis_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) {
1042 RETURN_FALSE;
1043 } else {
1044 RETURN_TRUE;
1045 }
1046 }
1047 /* }}} */
1048
1049 PHP_REDIS_API int
redis_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent)1050 redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
1051 {
1052 zval *object, *context = NULL, *ele;
1053 char *host = NULL, *persistent_id = NULL;
1054 zend_long port = -1, retry_interval = 0;
1055 size_t host_len, persistent_id_len;
1056 double timeout = 0.0, read_timeout = 0.0;
1057 redis_object *redis;
1058
1059 #ifdef ZTS
1060 /* not sure how in threaded mode this works so disabled persistence at
1061 * first */
1062 persistent = 0;
1063 #endif
1064
1065 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1066 "Os|lds!lda", &object, redis_ce, &host,
1067 &host_len, &port, &timeout, &persistent_id,
1068 &persistent_id_len, &retry_interval,
1069 &read_timeout, &context) == FAILURE)
1070 {
1071 return FAILURE;
1072 }
1073
1074 /* Disregard persistent_id if we're not opening a persistent connection */
1075 if (!persistent) {
1076 persistent_id = NULL;
1077 }
1078
1079 if (timeout < 0L || timeout > INT_MAX) {
1080 REDIS_THROW_EXCEPTION("Invalid connect timeout", 0);
1081 return FAILURE;
1082 }
1083
1084 if (read_timeout < 0L || read_timeout > INT_MAX) {
1085 REDIS_THROW_EXCEPTION("Invalid read timeout", 0);
1086 return FAILURE;
1087 }
1088
1089 if (retry_interval < 0L || retry_interval > INT_MAX) {
1090 REDIS_THROW_EXCEPTION("Invalid retry interval", 0);
1091 return FAILURE;
1092 }
1093
1094 /* If it's not a unix socket, set to default */
1095 if(port == -1 && host_len && host[0] != '/') {
1096 port = 6379;
1097 }
1098
1099 redis = PHPREDIS_ZVAL_GET_OBJECT(redis_object, object);
1100
1101 /* if there is a redis sock already we have to remove it */
1102 if (redis->sock) {
1103 redis_sock_disconnect(redis->sock, 0);
1104 redis_free_socket(redis->sock);
1105 }
1106
1107 redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent,
1108 persistent_id, retry_interval);
1109
1110 if (context) {
1111 /* Stream context (e.g. TLS) */
1112 if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "stream"))) {
1113 redis_sock_set_stream_context(redis->sock, ele);
1114 }
1115
1116 /* AUTH */
1117 if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "auth"))) {
1118 redis_sock_set_auth_zval(redis->sock, ele);
1119 }
1120 }
1121
1122 if (redis_sock_server_open(redis->sock) < 0) {
1123 if (redis->sock->err) {
1124 REDIS_THROW_EXCEPTION(ZSTR_VAL(redis->sock->err), 0);
1125 }
1126 redis_free_socket(redis->sock);
1127 redis->sock = NULL;
1128 return FAILURE;
1129 }
1130
1131 return SUCCESS;
1132 }
1133
1134 /* {{{ proto long Redis::bitop(string op, string key, ...) */
PHP_METHOD(Redis,bitop)1135 PHP_METHOD(Redis, bitop) {
1136 REDIS_PROCESS_CMD(bitop, redis_long_response);
1137 }
1138
1139 /* }}} */
1140
1141 /* {{{ proto long Redis::bitcount(string key, [int start], [int end])
1142 */
PHP_METHOD(Redis,bitcount)1143 PHP_METHOD(Redis, bitcount)
1144 {
1145 REDIS_PROCESS_CMD(bitcount, redis_long_response);
1146 }
1147 /* }}} */
1148
1149 /* {{{ proto integer Redis::bitpos(string key, int bit, [int start, int end]) */
PHP_METHOD(Redis,bitpos)1150 PHP_METHOD(Redis, bitpos)
1151 {
1152 REDIS_PROCESS_CMD(bitpos, redis_long_response);
1153 }
1154 /* }}} */
1155
1156 /* {{{ proto boolean Redis::close()
1157 */
PHP_METHOD(Redis,close)1158 PHP_METHOD(Redis, close)
1159 {
1160 RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
1161
1162 if (redis_sock_disconnect(redis_sock, 1) == SUCCESS) {
1163 RETURN_TRUE;
1164 }
1165 RETURN_FALSE;
1166 }
1167 /* }}} */
1168
1169 /* {{{ proto boolean Redis::set(string key, mixed val, long timeout,
1170 * [array opt) */
PHP_METHOD(Redis,set)1171 PHP_METHOD(Redis, set) {
1172 REDIS_PROCESS_CMD(set, redis_boolean_response);
1173 }
1174
1175 /* {{{ proto boolean Redis::setex(string key, long expire, string value)
1176 */
PHP_METHOD(Redis,setex)1177 PHP_METHOD(Redis, setex)
1178 {
1179 REDIS_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, redis_boolean_response);
1180 }
1181
1182 /* {{{ proto boolean Redis::psetex(string key, long expire, string value)
1183 */
PHP_METHOD(Redis,psetex)1184 PHP_METHOD(Redis, psetex)
1185 {
1186 REDIS_PROCESS_KW_CMD("PSETEX", redis_key_long_val_cmd, redis_boolean_response);
1187 }
1188
1189 /* {{{ proto boolean Redis::setnx(string key, string value)
1190 */
PHP_METHOD(Redis,setnx)1191 PHP_METHOD(Redis, setnx)
1192 {
1193 REDIS_PROCESS_KW_CMD("SETNX", redis_kv_cmd, redis_1_response);
1194 }
1195
1196 /* }}} */
1197
1198 /* {{{ proto string Redis::getSet(string key, string value)
1199 */
PHP_METHOD(Redis,getSet)1200 PHP_METHOD(Redis, getSet)
1201 {
1202 REDIS_PROCESS_KW_CMD("GETSET", redis_kv_cmd, redis_string_response);
1203 }
1204 /* }}} */
1205
1206 /* {{{ proto string Redis::randomKey()
1207 */
PHP_METHOD(Redis,randomKey)1208 PHP_METHOD(Redis, randomKey)
1209 {
1210 REDIS_PROCESS_KW_CMD("RANDOMKEY", redis_empty_cmd, redis_ping_response);
1211 }
1212 /* }}} */
1213
1214 /* {{{ proto string Redis::echo(string msg)
1215 */
PHP_METHOD(Redis,echo)1216 PHP_METHOD(Redis, echo)
1217 {
1218 REDIS_PROCESS_KW_CMD("ECHO", redis_str_cmd, redis_string_response);
1219 }
1220 /* }}} */
1221
1222 /* {{{ proto string Redis::rename(string key_src, string key_dst)
1223 */
PHP_METHOD(Redis,rename)1224 PHP_METHOD(Redis, rename)
1225 {
1226 REDIS_PROCESS_KW_CMD("RENAME", redis_key_key_cmd, redis_boolean_response);
1227 }
1228 /* }}} */
1229
1230 /* {{{ proto string Redis::renameNx(string key_src, string key_dst)
1231 */
PHP_METHOD(Redis,renameNx)1232 PHP_METHOD(Redis, renameNx)
1233 {
1234 REDIS_PROCESS_KW_CMD("RENAMENX", redis_key_key_cmd, redis_1_response);
1235 }
1236 /* }}} */
1237
1238 /* }}} */
1239
1240 /* {{{ proto string Redis::get(string key)
1241 */
PHP_METHOD(Redis,get)1242 PHP_METHOD(Redis, get)
1243 {
1244 REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_string_response);
1245 }
1246 /* }}} */
1247
1248
1249 /* {{{ proto string Redis::ping()
1250 */
PHP_METHOD(Redis,ping)1251 PHP_METHOD(Redis, ping)
1252 {
1253 REDIS_PROCESS_KW_CMD("PING", redis_opt_str_cmd, redis_read_variant_reply);
1254 }
1255 /* }}} */
1256
1257 /* {{{ proto boolean Redis::incr(string key [,int value])
1258 */
PHP_METHOD(Redis,incr)1259 PHP_METHOD(Redis, incr){
1260 REDIS_PROCESS_CMD(incr, redis_long_response);
1261 }
1262 /* }}} */
1263
1264 /* {{{ proto boolean Redis::incrBy(string key ,int value)
1265 */
PHP_METHOD(Redis,incrBy)1266 PHP_METHOD(Redis, incrBy){
1267 REDIS_PROCESS_KW_CMD("INCRBY", redis_key_long_cmd, redis_long_response);
1268 }
1269 /* }}} */
1270
1271 /* {{{ proto float Redis::incrByFloat(string key, float value)
1272 */
PHP_METHOD(Redis,incrByFloat)1273 PHP_METHOD(Redis, incrByFloat) {
1274 REDIS_PROCESS_KW_CMD("INCRBYFLOAT", redis_key_dbl_cmd, redis_bulk_double_response);
1275 }
1276 /* }}} */
1277
1278 /* {{{ proto boolean Redis::decr(string key) */
PHP_METHOD(Redis,decr)1279 PHP_METHOD(Redis, decr)
1280 {
1281 REDIS_PROCESS_CMD(decr, redis_long_response);
1282 }
1283 /* }}} */
1284
1285 /* {{{ proto boolean Redis::decrBy(string key ,int value)
1286 */
PHP_METHOD(Redis,decrBy)1287 PHP_METHOD(Redis, decrBy){
1288 REDIS_PROCESS_KW_CMD("DECRBY", redis_key_long_cmd, redis_long_response);
1289 }
1290 /* }}} */
1291
1292 /* {{{ proto array Redis::mget(array keys)
1293 */
PHP_METHOD(Redis,mget)1294 PHP_METHOD(Redis, mget)
1295 {
1296 zval *object, *z_args, *z_ele;
1297 HashTable *hash;
1298 RedisSock *redis_sock;
1299 smart_string cmd = {0};
1300 int arg_count;
1301
1302 /* Make sure we have proper arguments */
1303 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa",
1304 &object, redis_ce, &z_args) == FAILURE) {
1305 RETURN_FALSE;
1306 }
1307
1308 /* We'll need the socket */
1309 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
1310 RETURN_FALSE;
1311 }
1312
1313 /* Grab our array */
1314 hash = Z_ARRVAL_P(z_args);
1315
1316 /* We don't need to do anything if there aren't any keys */
1317 if((arg_count = zend_hash_num_elements(hash)) == 0) {
1318 RETURN_FALSE;
1319 }
1320
1321 /* Build our command header */
1322 redis_cmd_init_sstr(&cmd, arg_count, "MGET", 4);
1323
1324 /* Iterate through and grab our keys */
1325 ZEND_HASH_FOREACH_VAL(hash, z_ele) {
1326 zend_string *zstr = zval_get_string(z_ele);
1327 redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL);
1328 zend_string_release(zstr);
1329 } ZEND_HASH_FOREACH_END();
1330
1331 /* Kick off our command */
1332 REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
1333 if (IS_ATOMIC(redis_sock)) {
1334 if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1335 redis_sock, NULL, NULL) < 0) {
1336 RETURN_FALSE;
1337 }
1338 }
1339 REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
1340 }
1341
1342 /* {{{ proto boolean Redis::exists(string key)
1343 */
PHP_METHOD(Redis,exists)1344 PHP_METHOD(Redis, exists)
1345 {
1346 REDIS_PROCESS_CMD(exists, redis_long_response);
1347 }
1348 /* }}} */
1349
1350 /* {{{ proto boolean Redis::del(string key)
1351 */
PHP_METHOD(Redis,del)1352 PHP_METHOD(Redis, del)
1353 {
1354 REDIS_PROCESS_CMD(del, redis_long_response);
1355 }
1356 /* }}} */
1357
1358 /* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}}
1359 * {{{ proto long Redis::unlink(array $keys) */
PHP_METHOD(Redis,unlink)1360 PHP_METHOD(Redis, unlink)
1361 {
1362 REDIS_PROCESS_CMD(unlink, redis_long_response);
1363 }
1364
redis_set_watch(RedisSock * redis_sock)1365 PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
1366 {
1367 redis_sock->watching = 1;
1368 }
1369
redis_watch_response(INTERNAL_FUNCTION_PARAMETERS,RedisSock * redis_sock,zval * z_tab,void * ctx)1370 PHP_REDIS_API int redis_watch_response(INTERNAL_FUNCTION_PARAMETERS,
1371 RedisSock *redis_sock, zval *z_tab, void *ctx)
1372 {
1373 return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
1374 z_tab, ctx, redis_set_watch);
1375 }
1376
1377 /* {{{ proto boolean Redis::watch(string key1, string key2...)
1378 */
PHP_METHOD(Redis,watch)1379 PHP_METHOD(Redis, watch)
1380 {
1381 REDIS_PROCESS_CMD(watch, redis_watch_response);
1382 }
1383 /* }}} */
1384
redis_clear_watch(RedisSock * redis_sock)1385 PHP_REDIS_API void redis_clear_watch(RedisSock *redis_sock)
1386 {
1387 redis_sock->watching = 0;
1388 }
1389
redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS,RedisSock * redis_sock,zval * z_tab,void * ctx)1390 PHP_REDIS_API int redis_unwatch_response(INTERNAL_FUNCTION_PARAMETERS,
1391 RedisSock *redis_sock, zval *z_tab,
1392 void *ctx)
1393 {
1394 return redis_boolean_response_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
1395 z_tab, ctx, redis_clear_watch);
1396 }
1397
1398 /* {{{ proto boolean Redis::unwatch()
1399 */
PHP_METHOD(Redis,unwatch)1400 PHP_METHOD(Redis, unwatch)
1401 {
1402 REDIS_PROCESS_KW_CMD("UNWATCH", redis_empty_cmd, redis_unwatch_response);
1403 }
1404 /* }}} */
1405
1406 /* {{{ proto array Redis::keys(string pattern)
1407 */
PHP_METHOD(Redis,keys)1408 PHP_METHOD(Redis, keys)
1409 {
1410 REDIS_PROCESS_KW_CMD("KEYS", redis_key_cmd, redis_mbulk_reply_raw);
1411 }
1412 /* }}} */
1413
1414 /* {{{ proto int Redis::type(string key)
1415 */
PHP_METHOD(Redis,type)1416 PHP_METHOD(Redis, type)
1417 {
1418 REDIS_PROCESS_KW_CMD("TYPE", redis_key_cmd, redis_type_response);
1419 }
1420 /* }}} */
1421
1422 /* {{{ proto mixed Redis::acl(string $op, ...) }}} */
PHP_METHOD(Redis,acl)1423 PHP_METHOD(Redis, acl) {
1424 RedisSock *redis_sock;
1425 FailableResultCallback cb;
1426 zval *zargs;
1427 zend_string *op;
1428 char *cmd;
1429 int cmdlen, argc = ZEND_NUM_ARGS();
1430
1431 if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
1432 if (argc < 1) {
1433 php_error_docref(NULL, E_WARNING, "ACL command requires at least one argument");
1434 }
1435 RETURN_FALSE;
1436 }
1437
1438 zargs = emalloc(argc * sizeof(*zargs));
1439 if (zend_get_parameters_array(ht, argc, zargs) == FAILURE) {
1440 efree(zargs);
1441 RETURN_FALSE;
1442 }
1443
1444 /* Read the subcommand and set response callback */
1445 op = zval_get_string(&zargs[0]);
1446 if (zend_string_equals_literal_ci(op, "GETUSER")) {
1447 cb = redis_acl_getuser_reply;
1448 } else if (zend_string_equals_literal_ci(op, "LOG")) {
1449 cb = redis_acl_log_reply;
1450 } else {
1451 cb = redis_read_variant_reply;
1452 }
1453
1454 /* Make our command and free args */
1455 cmd = redis_variadic_str_cmd("ACL", zargs, argc, &cmdlen);
1456
1457 zend_string_release(op);
1458 efree(zargs);
1459
1460 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmdlen);
1461 if (IS_ATOMIC(redis_sock)) {
1462 if (cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) {
1463 RETURN_FALSE;
1464 }
1465 }
1466 REDIS_PROCESS_RESPONSE(cb);
1467 }
1468
1469 /* {{{ proto long Redis::append(string key, string val) */
PHP_METHOD(Redis,append)1470 PHP_METHOD(Redis, append)
1471 {
1472 REDIS_PROCESS_KW_CMD("APPEND", redis_kv_cmd, redis_long_response);
1473 }
1474 /* }}} */
1475
1476 /* {{{ proto string Redis::GetRange(string key, long start, long end) */
PHP_METHOD(Redis,getRange)1477 PHP_METHOD(Redis, getRange)
1478 {
1479 REDIS_PROCESS_KW_CMD("GETRANGE", redis_key_long_long_cmd,
1480 redis_string_response);
1481 }
1482 /* }}} */
1483
1484 /* {{{ proto string Redis::setRange(string key, long start, string value) */
PHP_METHOD(Redis,setRange)1485 PHP_METHOD(Redis, setRange)
1486 {
1487 REDIS_PROCESS_KW_CMD("SETRANGE", redis_key_long_str_cmd,
1488 redis_long_response);
1489 }
1490 /* }}} */
1491
1492 /* {{{ proto long Redis::getbit(string key, long idx) */
PHP_METHOD(Redis,getBit)1493 PHP_METHOD(Redis, getBit)
1494 {
1495 REDIS_PROCESS_KW_CMD("GETBIT", redis_key_long_cmd, redis_long_response);
1496 }
1497 /* }}} */
1498
1499 /* {{{ proto long Redis::setbit(string key, long idx, bool|int value) */
PHP_METHOD(Redis,setBit)1500 PHP_METHOD(Redis, setBit)
1501 {
1502 REDIS_PROCESS_CMD(setbit, redis_long_response);
1503 }
1504 /* }}} */
1505
1506 /* {{{ proto long Redis::strlen(string key) */
PHP_METHOD(Redis,strlen)1507 PHP_METHOD(Redis, strlen)
1508 {
1509 REDIS_PROCESS_KW_CMD("STRLEN", redis_key_cmd, redis_long_response);
1510 }
1511 /* }}} */
1512
1513 /* {{{ proto boolean Redis::lPush(string key , string value)
1514 */
PHP_METHOD(Redis,lPush)1515 PHP_METHOD(Redis, lPush)
1516 {
1517 REDIS_PROCESS_KW_CMD("LPUSH", redis_key_varval_cmd, redis_long_response);
1518 }
1519 /* }}} */
1520
1521 /* {{{ proto boolean Redis::rPush(string key , string value)
1522 */
PHP_METHOD(Redis,rPush)1523 PHP_METHOD(Redis, rPush)
1524 {
1525 REDIS_PROCESS_KW_CMD("RPUSH", redis_key_varval_cmd, redis_long_response);
1526 }
1527 /* }}} */
1528
PHP_METHOD(Redis,lInsert)1529 PHP_METHOD(Redis, lInsert)
1530 {
1531 REDIS_PROCESS_CMD(linsert, redis_long_response);
1532 }
1533
1534 /* {{{ proto long Redis::lPushx(string key, mixed value) */
PHP_METHOD(Redis,lPushx)1535 PHP_METHOD(Redis, lPushx)
1536 {
1537 REDIS_PROCESS_KW_CMD("LPUSHX", redis_kv_cmd, redis_long_response);
1538 }
1539 /* }}} */
1540
1541 /* {{{ proto long Redis::rPushx(string key, mixed value) */
PHP_METHOD(Redis,rPushx)1542 PHP_METHOD(Redis, rPushx)
1543 {
1544 REDIS_PROCESS_KW_CMD("RPUSHX", redis_kv_cmd, redis_long_response);
1545 }
1546 /* }}} */
1547
1548 /* {{{ proto string Redis::lPOP(string key) */
PHP_METHOD(Redis,lPop)1549 PHP_METHOD(Redis, lPop)
1550 {
1551 REDIS_PROCESS_KW_CMD("LPOP", redis_key_cmd, redis_string_response);
1552 }
1553 /* }}} */
1554
1555 /* {{{ proto string Redis::rPOP(string key) */
PHP_METHOD(Redis,rPop)1556 PHP_METHOD(Redis, rPop)
1557 {
1558 REDIS_PROCESS_KW_CMD("RPOP", redis_key_cmd, redis_string_response);
1559 }
1560 /* }}} */
1561
1562 /* {{{ proto string Redis::blPop(string key1, string key2, ..., int timeout) */
PHP_METHOD(Redis,blPop)1563 PHP_METHOD(Redis, blPop)
1564 {
1565 REDIS_PROCESS_KW_CMD("BLPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
1566 }
1567 /* }}} */
1568
1569 /* {{{ proto string Redis::brPop(string key1, string key2, ..., int timeout) */
PHP_METHOD(Redis,brPop)1570 PHP_METHOD(Redis, brPop)
1571 {
1572 REDIS_PROCESS_KW_CMD("BRPOP", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
1573 }
1574 /* }}} */
1575
1576
1577 /* {{{ proto int Redis::lLen(string key) */
PHP_METHOD(Redis,lLen)1578 PHP_METHOD(Redis, lLen)
1579 {
1580 REDIS_PROCESS_KW_CMD("LLEN", redis_key_cmd, redis_long_response);
1581 }
1582 /* }}} */
1583
1584 /* {{{ proto boolean Redis::lrem(string list, string value, int count = 0) */
PHP_METHOD(Redis,lrem)1585 PHP_METHOD(Redis, lrem)
1586 {
1587 REDIS_PROCESS_CMD(lrem, redis_long_response);
1588 }
1589 /* }}} */
1590
1591 /* {{{ proto boolean Redis::ltrim(string key , int start , int end) */
PHP_METHOD(Redis,ltrim)1592 PHP_METHOD(Redis, ltrim)
1593 {
1594 REDIS_PROCESS_KW_CMD("LTRIM", redis_key_long_long_cmd,
1595 redis_boolean_response);
1596 }
1597 /* }}} */
1598
1599 /* {{{ proto string Redis::lindex(string key , int index) */
PHP_METHOD(Redis,lindex)1600 PHP_METHOD(Redis, lindex)
1601 {
1602 REDIS_PROCESS_KW_CMD("LINDEX", redis_key_long_cmd, redis_string_response);
1603 }
1604 /* }}} */
1605
1606 /* {{{ proto array Redis::lrange(string key, int start , int end) */
PHP_METHOD(Redis,lrange)1607 PHP_METHOD(Redis, lrange)
1608 {
1609 REDIS_PROCESS_KW_CMD("LRANGE", redis_key_long_long_cmd,
1610 redis_sock_read_multibulk_reply);
1611 }
1612 /* }}} */
1613
1614 /* {{{ proto long Redis::sAdd(string key , mixed value) */
PHP_METHOD(Redis,sAdd)1615 PHP_METHOD(Redis, sAdd)
1616 {
1617 REDIS_PROCESS_KW_CMD("SADD", redis_key_varval_cmd, redis_long_response);
1618 }
1619 /* }}} */
1620
1621 /* {{{ proto boolean Redis::sAddArray(string key, array $values) */
PHP_METHOD(Redis,sAddArray)1622 PHP_METHOD(Redis, sAddArray) {
1623 REDIS_PROCESS_KW_CMD("SADD", redis_key_val_arr_cmd, redis_long_response);
1624 } /* }}} */
1625
1626 /* {{{ proto int Redis::scard(string key) */
PHP_METHOD(Redis,scard)1627 PHP_METHOD(Redis, scard)
1628 {
1629 REDIS_PROCESS_KW_CMD("SCARD", redis_key_cmd, redis_long_response);
1630 }
1631 /* }}} */
1632
1633 /* {{{ proto boolean Redis::srem(string set, string value) */
PHP_METHOD(Redis,srem)1634 PHP_METHOD(Redis, srem)
1635 {
1636 REDIS_PROCESS_KW_CMD("SREM", redis_key_varval_cmd, redis_long_response);
1637 }
1638 /* }}} */
1639
1640 /* {{{ proto boolean Redis::sMove(string src, string dst, mixed value) */
PHP_METHOD(Redis,sMove)1641 PHP_METHOD(Redis, sMove)
1642 {
1643 REDIS_PROCESS_CMD(smove, redis_1_response);
1644 }
1645 /* }}} */
1646
1647 /* {{{ proto string Redis::sPop(string key) */
PHP_METHOD(Redis,sPop)1648 PHP_METHOD(Redis, sPop)
1649 {
1650 if (ZEND_NUM_ARGS() == 1) {
1651 REDIS_PROCESS_KW_CMD("SPOP", redis_key_cmd, redis_string_response);
1652 } else if (ZEND_NUM_ARGS() == 2) {
1653 REDIS_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, redis_sock_read_multibulk_reply);
1654 } else {
1655 ZEND_WRONG_PARAM_COUNT();
1656 }
1657
1658 }
1659 /* }}} */
1660
1661 /* {{{ proto string Redis::sRandMember(string key [int count]) */
PHP_METHOD(Redis,sRandMember)1662 PHP_METHOD(Redis, sRandMember)
1663 {
1664 char *cmd;
1665 int cmd_len;
1666 short have_count;
1667 RedisSock *redis_sock;
1668
1669 // Grab our socket, validate call
1670 if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL ||
1671 redis_srandmember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
1672 &cmd, &cmd_len, NULL, NULL, &have_count) == FAILURE)
1673 {
1674 RETURN_FALSE;
1675 }
1676
1677 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
1678 if(have_count) {
1679 if (IS_ATOMIC(redis_sock)) {
1680 if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1681 redis_sock, NULL, NULL) < 0)
1682 {
1683 RETURN_FALSE;
1684 }
1685 }
1686 REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
1687 } else {
1688 if (IS_ATOMIC(redis_sock)) {
1689 redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
1690 NULL, NULL);
1691 }
1692 REDIS_PROCESS_RESPONSE(redis_string_response);
1693 }
1694 }
1695 /* }}} */
1696
1697 /* {{{ proto boolean Redis::sismember(string set, string value) */
PHP_METHOD(Redis,sismember)1698 PHP_METHOD(Redis, sismember)
1699 {
1700 REDIS_PROCESS_KW_CMD("SISMEMBER", redis_kv_cmd, redis_1_response);
1701 }
1702 /* }}} */
1703
1704 /* {{{ proto array Redis::sMembers(string set) */
PHP_METHOD(Redis,sMembers)1705 PHP_METHOD(Redis, sMembers)
1706 {
1707 REDIS_PROCESS_KW_CMD("SMEMBERS", redis_key_cmd,
1708 redis_sock_read_multibulk_reply);
1709 }
1710
1711 /* {{{ proto array Redis::sMisMember(string key, string member0, ...memberN) */
PHP_METHOD(Redis,sMisMember)1712 PHP_METHOD(Redis, sMisMember)
1713 {
1714 REDIS_PROCESS_KW_CMD("SMISMEMBER", redis_key_varval_cmd, redis_read_variant_reply);
1715 }
1716 /* }}} */
1717
1718 /* {{{ proto array Redis::sInter(string key0, ... string keyN) */
PHP_METHOD(Redis,sInter)1719 PHP_METHOD(Redis, sInter) {
1720 REDIS_PROCESS_CMD(sinter, redis_sock_read_multibulk_reply);
1721 }
1722 /* }}} */
1723
1724 /* {{{ proto array Redis::sInterStore(string dst, string key0,...string keyN) */
PHP_METHOD(Redis,sInterStore)1725 PHP_METHOD(Redis, sInterStore) {
1726 REDIS_PROCESS_CMD(sinterstore, redis_long_response);
1727 }
1728 /* }}} */
1729
1730 /* {{{ proto array Redis::sUnion(string key0, ... string keyN) */
PHP_METHOD(Redis,sUnion)1731 PHP_METHOD(Redis, sUnion) {
1732 REDIS_PROCESS_CMD(sunion, redis_sock_read_multibulk_reply);
1733 }
1734 /* }}} */
1735
1736 /* {{{ proto array Redis::sUnionStore(string dst, string key0, ... keyN) */
PHP_METHOD(Redis,sUnionStore)1737 PHP_METHOD(Redis, sUnionStore) {
1738 REDIS_PROCESS_CMD(sunionstore, redis_long_response);
1739 }
1740 /* }}} */
1741
1742 /* {{{ proto array Redis::sDiff(string key0, ... string keyN) */
PHP_METHOD(Redis,sDiff)1743 PHP_METHOD(Redis, sDiff) {
1744 REDIS_PROCESS_CMD(sdiff, redis_sock_read_multibulk_reply);
1745 }
1746 /* }}} */
1747
1748 /* {{{ proto array Redis::sDiffStore(string dst, string key0, ... keyN) */
PHP_METHOD(Redis,sDiffStore)1749 PHP_METHOD(Redis, sDiffStore) {
1750 REDIS_PROCESS_CMD(sdiffstore, redis_long_response);
1751 }
1752 /* }}} */
1753
1754
1755 /* {{{ proto array Redis::sort(string key, array options) */
PHP_METHOD(Redis,sort)1756 PHP_METHOD(Redis, sort) {
1757 char *cmd;
1758 int cmd_len, have_store;
1759 RedisSock *redis_sock;
1760
1761 // Grab socket, handle command construction
1762 if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL ||
1763 redis_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &have_store,
1764 &cmd, &cmd_len, NULL, NULL) == FAILURE)
1765 {
1766 RETURN_FALSE;
1767 }
1768
1769 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
1770 if (IS_ATOMIC(redis_sock)) {
1771 if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1772 redis_sock, NULL, NULL) < 0)
1773 {
1774 RETURN_FALSE;
1775 }
1776 }
1777 REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
1778 }
1779
1780 static void
generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS,int desc,int alpha)1781 generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha)
1782 {
1783 zval *object, *zele, *zget = NULL;
1784 RedisSock *redis_sock;
1785 zend_string *zpattern;
1786 char *key = NULL, *pattern = NULL, *store = NULL;
1787 size_t keylen, patternlen, storelen;
1788 zend_long offset = -1, count = -1;
1789 int argc = 1; /* SORT key is the simplest SORT command */
1790 smart_string cmd = {0};
1791
1792 /* Parse myriad of sort arguments */
1793 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1794 "Os|s!z!lls", &object, redis_ce, &key,
1795 &keylen, &pattern, &patternlen, &zget,
1796 &offset, &count, &store, &storelen)
1797 == FAILURE)
1798 {
1799 RETURN_FALSE;
1800 }
1801
1802 /* Ensure we're sorting something, and we can get context */
1803 if (keylen == 0 || !(redis_sock = redis_sock_get(object, 0)))
1804 RETURN_FALSE;
1805
1806 /* Start calculating argc depending on input arguments */
1807 if (pattern && patternlen) argc += 2; /* BY pattern */
1808 if (offset >= 0 && count >= 0) argc += 3; /* LIMIT offset count */
1809 if (alpha) argc += 1; /* ALPHA */
1810 if (store) argc += 2; /* STORE destination */
1811 if (desc) argc += 1; /* DESC (ASC is the default) */
1812
1813 /* GET is special. It can be 0 .. N arguments depending what we have */
1814 if (zget) {
1815 if (Z_TYPE_P(zget) == IS_ARRAY)
1816 argc += zend_hash_num_elements(Z_ARRVAL_P(zget));
1817 else if (Z_STRLEN_P(zget) > 0) {
1818 argc += 2; /* GET pattern */
1819 }
1820 }
1821
1822 /* Start constructing final command and append key */
1823 redis_cmd_init_sstr(&cmd, argc, "SORT", 4);
1824 redis_cmd_append_sstr_key(&cmd, key, keylen, redis_sock, NULL);
1825
1826 /* BY pattern */
1827 if (pattern && patternlen) {
1828 redis_cmd_append_sstr(&cmd, "BY", sizeof("BY") - 1);
1829 redis_cmd_append_sstr(&cmd, pattern, patternlen);
1830 }
1831
1832 /* LIMIT offset count */
1833 if (offset >= 0 && count >= 0) {
1834 redis_cmd_append_sstr(&cmd, "LIMIT", sizeof("LIMIT") - 1);
1835 redis_cmd_append_sstr_long(&cmd, offset);
1836 redis_cmd_append_sstr_long(&cmd, count);
1837 }
1838
1839 /* Handle any number of GET pattern arguments we've been passed */
1840 if (zget != NULL) {
1841 if (Z_TYPE_P(zget) == IS_ARRAY) {
1842 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) {
1843 zpattern = zval_get_string(zele);
1844 redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1);
1845 redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern));
1846 zend_string_release(zpattern);
1847 } ZEND_HASH_FOREACH_END();
1848 } else {
1849 zpattern = zval_get_string(zget);
1850 redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1);
1851 redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern));
1852 zend_string_release(zpattern);
1853 }
1854 }
1855
1856 /* Append optional DESC and ALPHA modifiers */
1857 if (desc) redis_cmd_append_sstr(&cmd, "DESC", sizeof("DESC") - 1);
1858 if (alpha) redis_cmd_append_sstr(&cmd, "ALPHA", sizeof("ALPHA") - 1);
1859
1860 /* Finally append STORE if we've got it */
1861 if (store && storelen) {
1862 redis_cmd_append_sstr(&cmd, "STORE", sizeof("STORE") - 1);
1863 redis_cmd_append_sstr_key(&cmd, store, storelen, redis_sock, NULL);
1864 }
1865
1866 REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
1867 if (IS_ATOMIC(redis_sock)) {
1868 if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1869 redis_sock, NULL, NULL) < 0)
1870 {
1871 RETURN_FALSE;
1872 }
1873 }
1874 REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
1875 }
1876
1877 /* {{{ proto array Redis::sortAsc(string key, string pattern, string get,
1878 * int start, int end, bool getList]) */
PHP_METHOD(Redis,sortAsc)1879 PHP_METHOD(Redis, sortAsc)
1880 {
1881 generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
1882 }
1883 /* }}} */
1884
1885 /* {{{ proto array Redis::sortAscAlpha(string key, string pattern, string get,
1886 * int start, int end, bool getList]) */
PHP_METHOD(Redis,sortAscAlpha)1887 PHP_METHOD(Redis, sortAscAlpha)
1888 {
1889 generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
1890 }
1891 /* }}} */
1892
1893 /* {{{ proto array Redis::sortDesc(string key, string pattern, string get,
1894 * int start, int end, bool getList]) */
PHP_METHOD(Redis,sortDesc)1895 PHP_METHOD(Redis, sortDesc)
1896 {
1897 generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
1898 }
1899 /* }}} */
1900
1901 /* {{{ proto array Redis::sortDescAlpha(string key, string pattern, string get,
1902 * int start, int end, bool getList]) */
PHP_METHOD(Redis,sortDescAlpha)1903 PHP_METHOD(Redis, sortDescAlpha)
1904 {
1905 generic_sort_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
1906 }
1907 /* }}} */
1908
1909 /* {{{ proto array Redis::expire(string key, int timeout) */
PHP_METHOD(Redis,expire)1910 PHP_METHOD(Redis, expire) {
1911 REDIS_PROCESS_KW_CMD("EXPIRE", redis_key_long_cmd, redis_1_response);
1912 }
1913 /* }}} */
1914
1915 /* {{{ proto bool Redis::pexpire(string key, long ms) */
PHP_METHOD(Redis,pexpire)1916 PHP_METHOD(Redis, pexpire) {
1917 REDIS_PROCESS_KW_CMD("PEXPIRE", redis_key_long_cmd, redis_1_response);
1918 }
1919 /* }}} */
1920
1921 /* {{{ proto array Redis::expireAt(string key, int timestamp) */
PHP_METHOD(Redis,expireAt)1922 PHP_METHOD(Redis, expireAt) {
1923 REDIS_PROCESS_KW_CMD("EXPIREAT", redis_key_long_cmd, redis_1_response);
1924 }
1925 /* }}} */
1926
1927 /* {{{ proto array Redis::pexpireAt(string key, int timestamp) */
PHP_METHOD(Redis,pexpireAt)1928 PHP_METHOD(Redis, pexpireAt) {
1929 REDIS_PROCESS_KW_CMD("PEXPIREAT", redis_key_long_cmd, redis_1_response);
1930 }
1931 /* }}} */
1932
1933 /* {{{ proto array Redis::lSet(string key, int index, string value) */
PHP_METHOD(Redis,lSet)1934 PHP_METHOD(Redis, lSet) {
1935 REDIS_PROCESS_KW_CMD("LSET", redis_key_long_val_cmd,
1936 redis_boolean_response);
1937 }
1938 /* }}} */
1939
1940 /* {{{ proto string Redis::save() */
PHP_METHOD(Redis,save)1941 PHP_METHOD(Redis, save)
1942 {
1943 REDIS_PROCESS_KW_CMD("SAVE", redis_empty_cmd, redis_boolean_response);
1944 }
1945 /* }}} */
1946
1947 /* {{{ proto string Redis::bgSave() */
PHP_METHOD(Redis,bgSave)1948 PHP_METHOD(Redis, bgSave)
1949 {
1950 REDIS_PROCESS_KW_CMD("BGSAVE", redis_empty_cmd, redis_boolean_response);
1951 }
1952 /* }}} */
1953
1954 /* {{{ proto integer Redis::lastSave() */
PHP_METHOD(Redis,lastSave)1955 PHP_METHOD(Redis, lastSave)
1956 {
1957 REDIS_PROCESS_KW_CMD("LASTSAVE", redis_empty_cmd, redis_long_response);
1958 }
1959 /* }}} */
1960
1961 /* {{{ proto bool Redis::flushDB([bool async]) */
PHP_METHOD(Redis,flushDB)1962 PHP_METHOD(Redis, flushDB)
1963 {
1964 REDIS_PROCESS_KW_CMD("FLUSHDB", redis_flush_cmd, redis_boolean_response);
1965 }
1966 /* }}} */
1967
1968 /* {{{ proto bool Redis::flushAll([bool async]) */
PHP_METHOD(Redis,flushAll)1969 PHP_METHOD(Redis, flushAll)
1970 {
1971 REDIS_PROCESS_KW_CMD("FLUSHALL", redis_flush_cmd, redis_boolean_response);
1972 }
1973 /* }}} */
1974
1975 /* {{{ proto int Redis::dbSize() */
PHP_METHOD(Redis,dbSize)1976 PHP_METHOD(Redis, dbSize)
1977 {
1978 REDIS_PROCESS_KW_CMD("DBSIZE", redis_empty_cmd, redis_long_response);
1979 }
1980 /* }}} */
1981
1982 /* {{{ proto bool Redis::auth(string passwd) */
PHP_METHOD(Redis,auth)1983 PHP_METHOD(Redis, auth) {
1984 REDIS_PROCESS_CMD(auth, redis_boolean_response);
1985 }
1986 /* }}} */
1987
1988 /* {{{ proto long Redis::persist(string key) */
PHP_METHOD(Redis,persist)1989 PHP_METHOD(Redis, persist) {
1990 REDIS_PROCESS_KW_CMD("PERSIST", redis_key_cmd, redis_1_response);
1991 }
1992 /* }}} */
1993
1994
1995 /* {{{ proto long Redis::ttl(string key) */
PHP_METHOD(Redis,ttl)1996 PHP_METHOD(Redis, ttl) {
1997 REDIS_PROCESS_KW_CMD("TTL", redis_key_cmd, redis_long_response);
1998 }
1999 /* }}} */
2000
2001 /* {{{ proto long Redis::pttl(string key) */
PHP_METHOD(Redis,pttl)2002 PHP_METHOD(Redis, pttl) {
2003 REDIS_PROCESS_KW_CMD("PTTL", redis_key_cmd, redis_long_response);
2004 }
2005 /* }}} */
2006
2007 /* {{{ proto array Redis::info() */
PHP_METHOD(Redis,info)2008 PHP_METHOD(Redis, info) {
2009
2010 zval *object;
2011 RedisSock *redis_sock;
2012 char *cmd, *opt = NULL;
2013 size_t opt_len;
2014 int cmd_len;
2015
2016 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
2017 "O|s", &object, redis_ce, &opt, &opt_len)
2018 == FAILURE)
2019 {
2020 RETURN_FALSE;
2021 }
2022
2023 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
2024 RETURN_FALSE;
2025 }
2026
2027 /* Build a standalone INFO command or one with an option */
2028 if (opt != NULL) {
2029 cmd_len = REDIS_SPPRINTF(&cmd, "INFO", "s", opt, opt_len);
2030 } else {
2031 cmd_len = REDIS_SPPRINTF(&cmd, "INFO", "");
2032 }
2033
2034 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
2035 if (IS_ATOMIC(redis_sock)) {
2036 redis_info_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL,
2037 NULL);
2038 }
2039 REDIS_PROCESS_RESPONSE(redis_info_response);
2040
2041 }
2042 /* }}} */
2043
2044 /* {{{ proto bool Redis::select(long dbNumber) */
PHP_METHOD(Redis,select)2045 PHP_METHOD(Redis, select) {
2046
2047 zval *object;
2048 RedisSock *redis_sock;
2049
2050 char *cmd;
2051 int cmd_len;
2052 zend_long dbNumber;
2053
2054 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol",
2055 &object, redis_ce, &dbNumber) == FAILURE) {
2056 RETURN_FALSE;
2057 }
2058
2059 if (dbNumber < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) {
2060 RETURN_FALSE;
2061 }
2062
2063 redis_sock->dbNumber = dbNumber;
2064 cmd_len = REDIS_SPPRINTF(&cmd, "SELECT", "d", dbNumber);
2065
2066 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
2067 if (IS_ATOMIC(redis_sock)) {
2068 redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
2069 NULL, NULL);
2070 }
2071 REDIS_PROCESS_RESPONSE(redis_boolean_response);
2072 }
2073 /* }}} */
2074
2075 /* {{{ proto bool Redis::swapdb(long srcdb, long dstdb) */
PHP_METHOD(Redis,swapdb)2076 PHP_METHOD(Redis, swapdb) {
2077 REDIS_PROCESS_KW_CMD("SWAPDB", redis_long_long_cmd, redis_boolean_response);
2078 }
2079
2080 /* {{{ proto bool Redis::move(string key, long dbindex) */
PHP_METHOD(Redis,move)2081 PHP_METHOD(Redis, move) {
2082 REDIS_PROCESS_KW_CMD("MOVE", redis_key_long_cmd, redis_1_response);
2083 }
2084 /* }}} */
2085
2086 static
generic_mset(INTERNAL_FUNCTION_PARAMETERS,char * kw,FailableResultCallback fun)2087 void generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, FailableResultCallback fun)
2088 {
2089 RedisSock *redis_sock;
2090 smart_string cmd = {0};
2091 zval *object, *z_array;
2092 HashTable *htargs;
2093 zend_string *zkey;
2094 zval *zmem;
2095 char buf[64];
2096 size_t keylen;
2097 zend_ulong idx;
2098
2099 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oa",
2100 &object, redis_ce, &z_array) == FAILURE)
2101 {
2102 RETURN_FALSE;
2103 }
2104
2105 /* Make sure we can get our socket, and we were not passed an empty array */
2106 if ((redis_sock = redis_sock_get(object, 0)) == NULL ||
2107 zend_hash_num_elements(Z_ARRVAL_P(z_array)) == 0)
2108 {
2109 RETURN_FALSE;
2110 }
2111
2112 /* Initialize our command */
2113 htargs = Z_ARRVAL_P(z_array);
2114 redis_cmd_init_sstr(&cmd, zend_hash_num_elements(htargs) * 2, kw, strlen(kw));
2115
2116 ZEND_HASH_FOREACH_KEY_VAL(htargs, idx, zkey, zmem) {
2117 /* Handle string or numeric keys */
2118 if (zkey) {
2119 redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, NULL);
2120 } else {
2121 keylen = snprintf(buf, sizeof(buf), "%ld", (long)idx);
2122 redis_cmd_append_sstr_key(&cmd, buf, keylen, redis_sock, NULL);
2123 }
2124
2125 /* Append our value */
2126 redis_cmd_append_sstr_zval(&cmd, zmem, redis_sock);
2127 } ZEND_HASH_FOREACH_END();
2128
2129 REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
2130 if (IS_ATOMIC(redis_sock)) {
2131 fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
2132 }
2133
2134 REDIS_PROCESS_RESPONSE(fun);
2135 }
2136
2137 /* {{{ proto bool Redis::mset(array (key => value, ...)) */
PHP_METHOD(Redis,mset)2138 PHP_METHOD(Redis, mset) {
2139 generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSET", redis_boolean_response);
2140 }
2141 /* }}} */
2142
2143
2144 /* {{{ proto bool Redis::msetnx(array (key => value, ...)) */
PHP_METHOD(Redis,msetnx)2145 PHP_METHOD(Redis, msetnx) {
2146 generic_mset(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", redis_1_response);
2147 }
2148 /* }}} */
2149
2150 /* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) */
PHP_METHOD(Redis,rpoplpush)2151 PHP_METHOD(Redis, rpoplpush)
2152 {
2153 REDIS_PROCESS_KW_CMD("RPOPLPUSH", redis_key_key_cmd, redis_string_response);
2154 }
2155 /* }}} */
2156
2157 /* {{{ proto string Redis::brpoplpush(string src, string dst, int timeout) */
PHP_METHOD(Redis,brpoplpush)2158 PHP_METHOD(Redis, brpoplpush) {
2159 REDIS_PROCESS_CMD(brpoplpush, redis_string_response);
2160 }
2161 /* }}} */
2162
2163 /* {{{ proto long Redis::zAdd(string key, int score, string value) */
PHP_METHOD(Redis,zAdd)2164 PHP_METHOD(Redis, zAdd) {
2165 REDIS_PROCESS_CMD(zadd, redis_long_response);
2166 }
2167 /* }}} */
2168
2169 /* Handle ZRANGE and ZREVRANGE as they're the same except for keyword */
generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS,char * kw,zrange_cb fun)2170 static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw,
2171 zrange_cb fun)
2172 {
2173 char *cmd;
2174 int cmd_len;
2175 RedisSock *redis_sock;
2176 int withscores = 0;
2177
2178 if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
2179 RETURN_FALSE;
2180 }
2181
2182 if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd,
2183 &cmd_len, &withscores, NULL, NULL) == FAILURE)
2184 {
2185 RETURN_FALSE;
2186 }
2187
2188 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
2189 if(withscores) {
2190 if (IS_ATOMIC(redis_sock)) {
2191 redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
2192 }
2193 REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl);
2194 } else {
2195 if (IS_ATOMIC(redis_sock)) {
2196 if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2197 redis_sock, NULL, NULL) < 0)
2198 {
2199 RETURN_FALSE;
2200 }
2201 }
2202 REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
2203 }
2204 }
2205
2206 /* {{{ proto array Redis::zRange(string key,int start,int end,bool scores = 0) */
PHP_METHOD(Redis,zRange)2207 PHP_METHOD(Redis, zRange)
2208 {
2209 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE",
2210 redis_zrange_cmd);
2211 }
2212
2213 /* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */
PHP_METHOD(Redis,zRevRange)2214 PHP_METHOD(Redis, zRevRange) {
2215 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE",
2216 redis_zrange_cmd);
2217 }
2218 /* }}} */
2219
2220 /* {{{ proto array Redis::zRangeByScore(string k,string s,string e,array opt) */
PHP_METHOD(Redis,zRangeByScore)2221 PHP_METHOD(Redis, zRangeByScore) {
2222 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE",
2223 redis_zrangebyscore_cmd);
2224 }
2225 /* }}} */
2226
2227 /* {{{ proto array Redis::zRevRangeByScore(string key, string start, string end,
2228 * array options) */
PHP_METHOD(Redis,zRevRangeByScore)2229 PHP_METHOD(Redis, zRevRangeByScore) {
2230 generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE",
2231 redis_zrangebyscore_cmd);
2232 }
2233 /* }}} */
2234
2235 /* {{{ proto array Redis::zRangeByLex(string key, string min, string max, [
2236 * offset, limit]) */
PHP_METHOD(Redis,zRangeByLex)2237 PHP_METHOD(Redis, zRangeByLex) {
2238 REDIS_PROCESS_KW_CMD("ZRANGEBYLEX", redis_zrangebylex_cmd,
2239 redis_sock_read_multibulk_reply);
2240 }
2241 /* }}} */
2242
PHP_METHOD(Redis,zRevRangeByLex)2243 PHP_METHOD(Redis, zRevRangeByLex) {
2244 REDIS_PROCESS_KW_CMD("ZREVRANGEBYLEX", redis_zrangebylex_cmd,
2245 redis_sock_read_multibulk_reply);
2246 }
2247 /* }}} */
2248
2249 /* {{{ proto long Redis::zLexCount(string key, string min, string max) */
PHP_METHOD(Redis,zLexCount)2250 PHP_METHOD(Redis, zLexCount) {
2251 REDIS_PROCESS_KW_CMD("ZLEXCOUNT", redis_gen_zlex_cmd, redis_long_response);
2252 }
2253 /* }}} */
2254
2255 /* {{{ proto long Redis::zRemRangeByLex(string key, string min, string max) */
PHP_METHOD(Redis,zRemRangeByLex)2256 PHP_METHOD(Redis, zRemRangeByLex) {
2257 REDIS_PROCESS_KW_CMD("ZREMRANGEBYLEX", redis_gen_zlex_cmd,
2258 redis_long_response);
2259 }
2260 /* }}} */
2261
2262 /* {{{ proto long Redis::zRem(string key, string member) */
PHP_METHOD(Redis,zRem)2263 PHP_METHOD(Redis, zRem)
2264 {
2265 REDIS_PROCESS_KW_CMD("ZREM", redis_key_varval_cmd, redis_long_response);
2266 }
2267 /* }}} */
2268
2269 /* {{{ proto long Redis::zRemRangeByScore(string k, string s, string e) */
PHP_METHOD(Redis,zRemRangeByScore)2270 PHP_METHOD(Redis, zRemRangeByScore)
2271 {
2272 REDIS_PROCESS_KW_CMD("ZREMRANGEBYSCORE", redis_key_str_str_cmd,
2273 redis_long_response);
2274 }
2275 /* }}} */
2276
2277 /* {{{ proto long Redis::zRemRangeByRank(string key, long start, long end) */
PHP_METHOD(Redis,zRemRangeByRank)2278 PHP_METHOD(Redis, zRemRangeByRank)
2279 {
2280 REDIS_PROCESS_KW_CMD("ZREMRANGEBYRANK", redis_key_long_long_cmd,
2281 redis_long_response);
2282 }
2283 /* }}} */
2284
2285 /* {{{ proto array Redis::zCount(string key, string start , string end) */
PHP_METHOD(Redis,zCount)2286 PHP_METHOD(Redis, zCount)
2287 {
2288 REDIS_PROCESS_KW_CMD("ZCOUNT", redis_key_str_str_cmd, redis_long_response);
2289 }
2290 /* }}} */
2291
2292 /* {{{ proto long Redis::zCard(string key) */
PHP_METHOD(Redis,zCard)2293 PHP_METHOD(Redis, zCard)
2294 {
2295 REDIS_PROCESS_KW_CMD("ZCARD", redis_key_cmd, redis_long_response);
2296 }
2297 /* }}} */
2298
2299 /* {{{ proto double Redis::zScore(string key, mixed member) */
PHP_METHOD(Redis,zScore)2300 PHP_METHOD(Redis, zScore)
2301 {
2302 REDIS_PROCESS_KW_CMD("ZSCORE", redis_kv_cmd,
2303 redis_bulk_double_response);
2304 }
2305 /* }}} */
2306
2307 /* {{{ proto long Redis::zRank(string key, string member) */
PHP_METHOD(Redis,zRank)2308 PHP_METHOD(Redis, zRank) {
2309 REDIS_PROCESS_KW_CMD("ZRANK", redis_kv_cmd, redis_long_response);
2310 }
2311 /* }}} */
2312
2313 /* {{{ proto long Redis::zRevRank(string key, string member) */
PHP_METHOD(Redis,zRevRank)2314 PHP_METHOD(Redis, zRevRank) {
2315 REDIS_PROCESS_KW_CMD("ZREVRANK", redis_kv_cmd, redis_long_response);
2316 }
2317 /* }}} */
2318
2319 /* {{{ proto double Redis::zIncrBy(string key, double value, mixed member) */
PHP_METHOD(Redis,zIncrBy)2320 PHP_METHOD(Redis, zIncrBy)
2321 {
2322 REDIS_PROCESS_CMD(zincrby, redis_bulk_double_response);
2323 }
2324 /* }}} */
2325
2326 /* zinterstore */
PHP_METHOD(Redis,zinterstore)2327 PHP_METHOD(Redis, zinterstore) {
2328 REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response);
2329 }
2330
2331 /* zunionstore */
PHP_METHOD(Redis,zunionstore)2332 PHP_METHOD(Redis, zunionstore) {
2333 REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, redis_long_response);
2334 }
2335
2336 /* {{{ proto array Redis::zPopMax(string key) */
PHP_METHOD(Redis,zPopMax)2337 PHP_METHOD(Redis, zPopMax)
2338 {
2339 if (ZEND_NUM_ARGS() == 1) {
2340 REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl);
2341 } else if (ZEND_NUM_ARGS() == 2) {
2342 REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl);
2343 } else {
2344 ZEND_WRONG_PARAM_COUNT();
2345 }
2346 }
2347 /* }}} */
2348
2349 /* {{{ proto array Redis::zPopMin(string key) */
PHP_METHOD(Redis,zPopMin)2350 PHP_METHOD(Redis, zPopMin)
2351 {
2352 if (ZEND_NUM_ARGS() == 1) {
2353 REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_cmd, redis_mbulk_reply_zipped_keys_dbl);
2354 } else if (ZEND_NUM_ARGS() == 2) {
2355 REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl);
2356 } else {
2357 ZEND_WRONG_PARAM_COUNT();
2358 }
2359 }
2360 /* }}} */
2361
2362 /* {{{ proto Redis::bzPopMax(Array(keys) [, timeout]): Array */
PHP_METHOD(Redis,bzPopMax)2363 PHP_METHOD(Redis, bzPopMax) {
2364 REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
2365 }
2366 /* }}} */
2367
2368 /* {{{ proto Redis::bzPopMin(Array(keys) [, timeout]): Array */
PHP_METHOD(Redis,bzPopMin)2369 PHP_METHOD(Redis, bzPopMin) {
2370 REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply);
2371 }
2372 /* }}} */
2373
2374 /* hashes */
2375
2376 /* {{{ proto long Redis::hset(string key, string mem, string val) */
PHP_METHOD(Redis,hSet)2377 PHP_METHOD(Redis, hSet)
2378 {
2379 REDIS_PROCESS_CMD(hset, redis_long_response);
2380 }
2381 /* }}} */
2382
2383 /* {{{ proto bool Redis::hSetNx(string key, string mem, string val) */
PHP_METHOD(Redis,hSetNx)2384 PHP_METHOD(Redis, hSetNx)
2385 {
2386 REDIS_PROCESS_CMD(hsetnx, redis_1_response);
2387 }
2388 /* }}} */
2389
2390 /* {{{ proto string Redis::hget(string key, string mem) */
PHP_METHOD(Redis,hGet)2391 PHP_METHOD(Redis, hGet)
2392 {
2393 REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_string_response);
2394 }
2395 /* }}} */
2396
2397 /* {{{ proto long Redis::hLen(string key) */
PHP_METHOD(Redis,hLen)2398 PHP_METHOD(Redis, hLen)
2399 {
2400 REDIS_PROCESS_KW_CMD("HLEN", redis_key_cmd, redis_long_response);
2401 }
2402 /* }}} */
2403
2404 /* {{{ proto long Redis::hDel(string key, string mem1, ... memN) */
PHP_METHOD(Redis,hDel)2405 PHP_METHOD(Redis, hDel)
2406 {
2407 REDIS_PROCESS_CMD(hdel, redis_long_response);
2408 }
2409 /* }}} */
2410
2411 /* {{{ proto bool Redis::hExists(string key, string mem) */
PHP_METHOD(Redis,hExists)2412 PHP_METHOD(Redis, hExists)
2413 {
2414 REDIS_PROCESS_KW_CMD("HEXISTS", redis_key_str_cmd, redis_1_response);
2415 }
2416
2417 /* {{{ proto array Redis::hkeys(string key) */
PHP_METHOD(Redis,hKeys)2418 PHP_METHOD(Redis, hKeys)
2419 {
2420 REDIS_PROCESS_KW_CMD("HKEYS", redis_key_cmd, redis_mbulk_reply_raw);
2421 }
2422 /* }}} */
2423
2424 /* {{{ proto array Redis::hvals(string key) */
PHP_METHOD(Redis,hVals)2425 PHP_METHOD(Redis, hVals)
2426 {
2427 REDIS_PROCESS_KW_CMD("HVALS", redis_key_cmd,
2428 redis_sock_read_multibulk_reply);
2429 }
2430
2431 /* {{{ proto array Redis::hgetall(string key) */
PHP_METHOD(Redis,hGetAll)2432 PHP_METHOD(Redis, hGetAll) {
2433 REDIS_PROCESS_KW_CMD("HGETALL", redis_key_cmd, redis_mbulk_reply_zipped_vals);
2434 }
2435 /* }}} */
2436
2437 /* {{{ proto double Redis::hIncrByFloat(string k, string me, double v) */
PHP_METHOD(Redis,hIncrByFloat)2438 PHP_METHOD(Redis, hIncrByFloat)
2439 {
2440 REDIS_PROCESS_CMD(hincrbyfloat, redis_bulk_double_response);
2441 }
2442 /* }}} */
2443
2444 /* {{{ proto long Redis::hincrby(string key, string mem, long byval) */
PHP_METHOD(Redis,hIncrBy)2445 PHP_METHOD(Redis, hIncrBy)
2446 {
2447 REDIS_PROCESS_CMD(hincrby, redis_long_response);
2448 }
2449 /* }}} */
2450
2451 /* {{{ array Redis::hMget(string hash, array keys) */
PHP_METHOD(Redis,hMget)2452 PHP_METHOD(Redis, hMget) {
2453 REDIS_PROCESS_CMD(hmget, redis_mbulk_reply_assoc);
2454 }
2455 /* }}} */
2456
2457 /* {{{ proto bool Redis::hmset(string key, array keyvals) */
PHP_METHOD(Redis,hMset)2458 PHP_METHOD(Redis, hMset)
2459 {
2460 REDIS_PROCESS_CMD(hmset, redis_boolean_response);
2461 }
2462 /* }}} */
2463
2464 /* {{{ proto long Redis::hstrlen(string key, string field) */
PHP_METHOD(Redis,hStrLen)2465 PHP_METHOD(Redis, hStrLen) {
2466 REDIS_PROCESS_CMD(hstrlen, redis_long_response);
2467 }
2468 /* }}} */
2469
2470 /* flag : get, set {ATOMIC, MULTI, PIPELINE} */
2471
PHP_METHOD(Redis,multi)2472 PHP_METHOD(Redis, multi)
2473 {
2474
2475 RedisSock *redis_sock;
2476 char *resp, *cmd;
2477 int resp_len, cmd_len;
2478 zval *object;
2479 zend_long multi_value = MULTI;
2480
2481 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
2482 "O|l", &object, redis_ce, &multi_value)
2483 == FAILURE)
2484 {
2485 RETURN_FALSE;
2486 }
2487
2488 /* if the flag is activated, send the command, the reply will be "QUEUED"
2489 * or -ERR */
2490 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
2491 RETURN_FALSE;
2492 }
2493
2494 if (multi_value == PIPELINE) {
2495 /* Cannot enter pipeline mode in a MULTI block */
2496 if (IS_MULTI(redis_sock)) {
2497 php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!");
2498 RETURN_FALSE;
2499 }
2500
2501 /* Enable PIPELINE if we're not already in one */
2502 if (IS_ATOMIC(redis_sock)) {
2503 free_reply_callbacks(redis_sock);
2504 REDIS_ENABLE_MODE(redis_sock, PIPELINE);
2505 }
2506 } else if (multi_value == MULTI) {
2507 /* Don't want to do anything if we're already in MULTI mode */
2508 if (!IS_MULTI(redis_sock)) {
2509 cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", "");
2510 if (IS_PIPELINE(redis_sock)) {
2511 PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
2512 efree(cmd);
2513 REDIS_SAVE_CALLBACK(NULL, NULL);
2514 REDIS_ENABLE_MODE(redis_sock, MULTI);
2515 } else {
2516 SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len)
2517 efree(cmd);
2518 if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) {
2519 RETURN_FALSE;
2520 } else if (strncmp(resp, "+OK", 3) != 0) {
2521 efree(resp);
2522 RETURN_FALSE;
2523 }
2524 efree(resp);
2525 REDIS_ENABLE_MODE(redis_sock, MULTI);
2526 }
2527 }
2528 } else {
2529 php_error_docref(NULL, E_WARNING, "Unknown mode sent to Redis::multi");
2530 RETURN_FALSE;
2531 }
2532
2533 RETURN_ZVAL(getThis(), 1, 0);
2534 }
2535
2536 /* discard */
PHP_METHOD(Redis,discard)2537 PHP_METHOD(Redis, discard)
2538 {
2539 int ret = FAILURE;
2540 RedisSock *redis_sock;
2541 zval *object;
2542
2543 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
2544 &object, redis_ce) == FAILURE) {
2545 RETURN_FALSE;
2546 }
2547
2548 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
2549 RETURN_FALSE;
2550 }
2551
2552 if (IS_PIPELINE(redis_sock)) {
2553 ret = SUCCESS;
2554 if (redis_sock->pipeline_cmd) {
2555 zend_string_release(redis_sock->pipeline_cmd);
2556 redis_sock->pipeline_cmd = NULL;
2557 }
2558 } else if (IS_MULTI(redis_sock)) {
2559 ret = redis_send_discard(redis_sock);
2560 }
2561 if (ret == SUCCESS) {
2562 free_reply_callbacks(redis_sock);
2563 redis_sock->mode = ATOMIC;
2564 RETURN_TRUE;
2565 }
2566 RETURN_FALSE;
2567 }
2568
2569 /* redis_sock_read_multibulk_multi_reply */
redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,RedisSock * redis_sock)2570 PHP_REDIS_API int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
2571 RedisSock *redis_sock)
2572 {
2573
2574 char inbuf[4096];
2575 int numElems;
2576 size_t len;
2577
2578 if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
2579 return - 1;
2580 }
2581
2582 /* number of responses */
2583 numElems = atoi(inbuf+1);
2584
2585 if(numElems < 0) {
2586 return -1;
2587 }
2588
2589 array_init(return_value);
2590
2591 redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2592 redis_sock, return_value, numElems);
2593
2594 return 0;
2595 }
2596
2597
2598 /* exec */
PHP_METHOD(Redis,exec)2599 PHP_METHOD(Redis, exec)
2600 {
2601 RedisSock *redis_sock;
2602 char *cmd;
2603 int cmd_len, ret;
2604 zval *object;
2605
2606 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
2607 "O", &object, redis_ce) == FAILURE ||
2608 (redis_sock = redis_sock_get(object, 0)) == NULL
2609 ) {
2610 RETURN_FALSE;
2611 }
2612
2613 if (IS_MULTI(redis_sock)) {
2614 cmd_len = REDIS_SPPRINTF(&cmd, "EXEC", "");
2615 if (IS_PIPELINE(redis_sock)) {
2616 PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
2617 efree(cmd);
2618 REDIS_SAVE_CALLBACK(NULL, NULL);
2619 REDIS_DISABLE_MODE(redis_sock, MULTI);
2620 RETURN_ZVAL(getThis(), 1, 0);
2621 }
2622 SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len)
2623 efree(cmd);
2624
2625 ret = redis_sock_read_multibulk_multi_reply(
2626 INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
2627 free_reply_callbacks(redis_sock);
2628 REDIS_DISABLE_MODE(redis_sock, MULTI);
2629 redis_sock->watching = 0;
2630 if (ret < 0) {
2631 zval_dtor(return_value);
2632 RETURN_FALSE;
2633 }
2634 }
2635
2636 if (IS_PIPELINE(redis_sock)) {
2637 if (redis_sock->pipeline_cmd == NULL) {
2638 /* Empty array when no command was run. */
2639 array_init(return_value);
2640 } else {
2641 if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd),
2642 ZSTR_LEN(redis_sock->pipeline_cmd)) < 0) {
2643 ZVAL_FALSE(return_value);
2644 } else {
2645 array_init(return_value);
2646 redis_sock_read_multibulk_multi_reply_loop(
2647 INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, return_value, 0);
2648 }
2649 zend_string_release(redis_sock->pipeline_cmd);
2650 redis_sock->pipeline_cmd = NULL;
2651 }
2652 free_reply_callbacks(redis_sock);
2653 REDIS_DISABLE_MODE(redis_sock, PIPELINE);
2654 }
2655 }
2656
2657 PHP_REDIS_API int
redis_response_enqueued(RedisSock * redis_sock)2658 redis_response_enqueued(RedisSock *redis_sock)
2659 {
2660 char *resp;
2661 int resp_len, ret = FAILURE;
2662
2663 if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) {
2664 if (strncmp(resp, "+QUEUED", 7) == 0) {
2665 ret = SUCCESS;
2666 }
2667 efree(resp);
2668 }
2669 return ret;
2670 }
2671
2672 /* TODO: Investigate/fix the odd logic going on in here. Looks like previous abort
2673 * conditions that are now simply empty if { } { } blocks. */
2674 PHP_REDIS_API int
redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,RedisSock * redis_sock,zval * z_tab,int numElems)2675 redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
2676 RedisSock *redis_sock, zval *z_tab,
2677 int numElems)
2678 {
2679 fold_item *fi;
2680
2681 for (fi = redis_sock->head; fi; /* void */) {
2682 if (fi->fun) {
2683 fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx);
2684 fi = fi->next;
2685 continue;
2686 }
2687 size_t len;
2688 char inbuf[255];
2689
2690 if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
2691 } else if (strncmp(inbuf, "+OK", 3) != 0) {
2692 }
2693
2694 while ((fi = fi->next) && fi->fun) {
2695 if (redis_response_enqueued(redis_sock) == SUCCESS) {
2696 } else {
2697 }
2698 }
2699
2700 if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) {
2701 }
2702
2703 zval z_ret;
2704 array_init(&z_ret);
2705 add_next_index_zval(z_tab, &z_ret);
2706
2707 int num = atol(inbuf + 1);
2708
2709 if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) {
2710 }
2711
2712 if (fi) fi = fi->next;
2713 }
2714 redis_sock->current = fi;
2715 return 0;
2716 }
2717
PHP_METHOD(Redis,pipeline)2718 PHP_METHOD(Redis, pipeline)
2719 {
2720 RedisSock *redis_sock;
2721 zval *object;
2722
2723 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
2724 "O", &object, redis_ce) == FAILURE ||
2725 (redis_sock = redis_sock_get(object, 0)) == NULL
2726 ) {
2727 RETURN_FALSE;
2728 }
2729
2730 /* User cannot enter MULTI mode if already in a pipeline */
2731 if (IS_MULTI(redis_sock)) {
2732 php_error_docref(NULL, E_ERROR, "Can't activate pipeline in multi mode!");
2733 RETURN_FALSE;
2734 }
2735
2736 /* Enable pipeline mode unless we're already in that mode in which case this
2737 * is just a NO OP */
2738 if (IS_ATOMIC(redis_sock)) {
2739 /* NB : we keep the function fold, to detect the last function.
2740 * We need the response format of the n - 1 command. So, we can delete
2741 * when n > 2, the { 1 .. n - 2} commands */
2742 free_reply_callbacks(redis_sock);
2743 REDIS_ENABLE_MODE(redis_sock, PIPELINE);
2744 }
2745
2746 RETURN_ZVAL(getThis(), 1, 0);
2747 }
2748
2749 /* {{{ proto long Redis::publish(string channel, string msg) */
PHP_METHOD(Redis,publish)2750 PHP_METHOD(Redis, publish)
2751 {
2752 REDIS_PROCESS_KW_CMD("PUBLISH", redis_key_str_cmd, redis_long_response);
2753 }
2754 /* }}} */
2755
2756 /* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) */
PHP_METHOD(Redis,psubscribe)2757 PHP_METHOD(Redis, psubscribe)
2758 {
2759 REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd,
2760 redis_subscribe_response);
2761 }
2762
2763 /* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */
PHP_METHOD(Redis,subscribe)2764 PHP_METHOD(Redis, subscribe) {
2765 REDIS_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd,
2766 redis_subscribe_response);
2767 }
2768
2769 /**
2770 * [p]unsubscribe channel_0 channel_1 ... channel_n
2771 * [p]unsubscribe(array(channel_0, channel_1, ..., channel_n))
2772 * response format :
2773 * array(
2774 * channel_0 => TRUE|FALSE,
2775 * channel_1 => TRUE|FALSE,
2776 * ...
2777 * channel_n => TRUE|FALSE
2778 * );
2779 **/
2780
PHP_METHOD(Redis,unsubscribe)2781 PHP_METHOD(Redis, unsubscribe)
2782 {
2783 REDIS_PROCESS_KW_CMD("UNSUBSCRIBE", redis_unsubscribe_cmd,
2784 redis_unsubscribe_response);
2785 }
2786
PHP_METHOD(Redis,punsubscribe)2787 PHP_METHOD(Redis, punsubscribe)
2788 {
2789 REDIS_PROCESS_KW_CMD("PUNSUBSCRIBE", redis_unsubscribe_cmd,
2790 redis_unsubscribe_response);
2791 }
2792
2793 /* {{{ proto string Redis::bgrewriteaof() */
PHP_METHOD(Redis,bgrewriteaof)2794 PHP_METHOD(Redis, bgrewriteaof)
2795 {
2796 REDIS_PROCESS_KW_CMD("BGREWRITEAOF", redis_empty_cmd,
2797 redis_boolean_response);
2798 }
2799 /* }}} */
2800
2801 /* {{{ proto string Redis::slaveof([host, port]) */
PHP_METHOD(Redis,slaveof)2802 PHP_METHOD(Redis, slaveof)
2803 {
2804 zval *object;
2805 RedisSock *redis_sock;
2806 char *cmd = "", *host = NULL;
2807 size_t host_len;
2808 zend_long port = 6379;
2809 int cmd_len;
2810
2811 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
2812 "O|sl", &object, redis_ce, &host,
2813 &host_len, &port) == FAILURE)
2814 {
2815 RETURN_FALSE;
2816 }
2817 if (port < 0 || (redis_sock = redis_sock_get(object, 0)) == NULL) {
2818 RETURN_FALSE;
2819 }
2820
2821 if (host && host_len) {
2822 cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "sd", host, host_len, (int)port);
2823 } else {
2824 cmd_len = REDIS_SPPRINTF(&cmd, "SLAVEOF", "ss", "NO", 2, "ONE", 3);
2825 }
2826
2827 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
2828 if (IS_ATOMIC(redis_sock)) {
2829 redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
2830 NULL, NULL);
2831 }
2832 REDIS_PROCESS_RESPONSE(redis_boolean_response);
2833 }
2834 /* }}} */
2835
2836 /* {{{ proto string Redis::object(key) */
PHP_METHOD(Redis,object)2837 PHP_METHOD(Redis, object)
2838 {
2839 RedisSock *redis_sock;
2840 char *cmd; int cmd_len;
2841 REDIS_REPLY_TYPE rtype;
2842
2843 if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
2844 RETURN_FALSE;
2845 }
2846
2847 if(redis_object_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &rtype,
2848 &cmd, &cmd_len, NULL, NULL)==FAILURE)
2849 {
2850 RETURN_FALSE;
2851 }
2852
2853 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
2854
2855 if(rtype == TYPE_INT) {
2856 if (IS_ATOMIC(redis_sock)) {
2857 redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
2858 NULL, NULL);
2859 }
2860 REDIS_PROCESS_RESPONSE(redis_long_response);
2861 } else {
2862 if (IS_ATOMIC(redis_sock)) {
2863 redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
2864 NULL, NULL);
2865 }
2866 REDIS_PROCESS_RESPONSE(redis_string_response);
2867 }
2868 }
2869 /* }}} */
2870
2871 /* {{{ proto string Redis::getOption($option) */
PHP_METHOD(Redis,getOption)2872 PHP_METHOD(Redis, getOption)
2873 {
2874 RedisSock *redis_sock;
2875
2876 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
2877 RETURN_FALSE;
2878 }
2879
2880 redis_getoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL);
2881 }
2882 /* }}} */
2883
2884 /* {{{ proto string Redis::setOption(string $option, mixed $value) */
PHP_METHOD(Redis,setOption)2885 PHP_METHOD(Redis, setOption)
2886 {
2887 RedisSock *redis_sock;
2888
2889 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
2890 RETURN_FALSE;
2891 }
2892
2893 redis_setoption_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL);
2894 }
2895 /* }}} */
2896
2897 /* {{{ proto boolean Redis::config(string op, string key [, mixed value]) */
PHP_METHOD(Redis,config)2898 PHP_METHOD(Redis, config)
2899 {
2900 zval *object;
2901 RedisSock *redis_sock;
2902 char *key = NULL, *val = NULL, *cmd, *op = NULL;
2903 size_t key_len, val_len, op_len;
2904 enum {CFG_GET, CFG_SET} mode;
2905 int cmd_len;
2906
2907 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
2908 "Oss|s", &object, redis_ce, &op, &op_len,
2909 &key, &key_len, &val, &val_len) == FAILURE)
2910 {
2911 RETURN_FALSE;
2912 }
2913
2914 /* op must be GET or SET */
2915 if(strncasecmp(op, "GET", 3) == 0) {
2916 mode = CFG_GET;
2917 } else if(strncasecmp(op, "SET", 3) == 0) {
2918 mode = CFG_SET;
2919 } else {
2920 RETURN_FALSE;
2921 }
2922
2923 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
2924 RETURN_FALSE;
2925 }
2926
2927 if (mode == CFG_GET && val == NULL) {
2928 cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "ss", op, op_len, key, key_len);
2929
2930 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
2931 if (IS_ATOMIC(redis_sock)) {
2932 redis_mbulk_reply_zipped_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
2933 }
2934 REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_raw);
2935
2936 } else if(mode == CFG_SET && val != NULL) {
2937 cmd_len = REDIS_SPPRINTF(&cmd, "CONFIG", "sss", op, op_len, key, key_len, val, val_len);
2938
2939 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len)
2940 if (IS_ATOMIC(redis_sock)) {
2941 redis_boolean_response(
2942 INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
2943 }
2944 REDIS_PROCESS_RESPONSE(redis_boolean_response);
2945 } else {
2946 RETURN_FALSE;
2947 }
2948 }
2949 /* }}} */
2950
2951
2952 /* {{{ proto boolean Redis::slowlog(string arg, [int option]) */
PHP_METHOD(Redis,slowlog)2953 PHP_METHOD(Redis, slowlog) {
2954 zval *object;
2955 RedisSock *redis_sock;
2956 char *arg, *cmd;
2957 int cmd_len;
2958 size_t arg_len;
2959 zend_long option = 0;
2960 enum {SLOWLOG_GET, SLOWLOG_LEN, SLOWLOG_RESET} mode;
2961
2962 // Make sure we can get parameters
2963 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
2964 "Os|l", &object, redis_ce, &arg, &arg_len,
2965 &option) == FAILURE)
2966 {
2967 RETURN_FALSE;
2968 }
2969
2970 /* Figure out what kind of slowlog command we're executing */
2971 if(!strncasecmp(arg, "GET", 3)) {
2972 mode = SLOWLOG_GET;
2973 } else if(!strncasecmp(arg, "LEN", 3)) {
2974 mode = SLOWLOG_LEN;
2975 } else if(!strncasecmp(arg, "RESET", 5)) {
2976 mode = SLOWLOG_RESET;
2977 } else {
2978 /* This command is not valid */
2979 RETURN_FALSE;
2980 }
2981
2982 /* Make sure we can grab our redis socket */
2983 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
2984 RETURN_FALSE;
2985 }
2986
2987 // Create our command. For everything except SLOWLOG GET (with an arg) it's
2988 // just two parts
2989 if (mode == SLOWLOG_GET && ZEND_NUM_ARGS() == 2) {
2990 cmd_len = REDIS_SPPRINTF(&cmd, "SLOWLOG", "sl", arg, arg_len, option);
2991 } else {
2992 cmd_len = REDIS_SPPRINTF(&cmd, "SLOWLOG", "s", arg, arg_len);
2993 }
2994
2995 /* Kick off our command */
2996 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
2997 if (IS_ATOMIC(redis_sock)) {
2998 if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
2999 redis_sock, NULL, NULL) < 0)
3000 {
3001 RETURN_FALSE;
3002 }
3003 }
3004 REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
3005 }
3006
3007 /* {{{ proto Redis::wait(int num_slaves, int ms) }}} */
PHP_METHOD(Redis,wait)3008 PHP_METHOD(Redis, wait) {
3009 zval *object;
3010 RedisSock *redis_sock;
3011 zend_long num_slaves, timeout;
3012 char *cmd;
3013 int cmd_len;
3014
3015 /* Make sure arguments are valid */
3016 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll",
3017 &object, redis_ce, &num_slaves, &timeout)
3018 ==FAILURE)
3019 {
3020 RETURN_FALSE;
3021 }
3022
3023 /* Don't even send this to Redis if our args are negative */
3024 if(num_slaves < 0 || timeout < 0) {
3025 RETURN_FALSE;
3026 }
3027
3028 /* Grab our socket */
3029 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
3030 RETURN_FALSE;
3031 }
3032
3033 // Construct the command
3034 cmd_len = REDIS_SPPRINTF(&cmd, "WAIT", "ll", num_slaves, timeout);
3035
3036 /* Kick it off */
3037 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
3038 if (IS_ATOMIC(redis_sock)) {
3039 redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL,
3040 NULL);
3041 }
3042 REDIS_PROCESS_RESPONSE(redis_long_response);
3043 }
3044
3045 /* Construct a PUBSUB command */
3046 PHP_REDIS_API int
redis_build_pubsub_cmd(RedisSock * redis_sock,char ** ret,PUBSUB_TYPE type,zval * arg)3047 redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
3048 zval *arg)
3049 {
3050 HashTable *ht_chan;
3051 zval *z_ele;
3052 smart_string cmd = {0};
3053
3054 if (type == PUBSUB_CHANNELS) {
3055 if (arg) {
3056 /* With a pattern */
3057 return REDIS_SPPRINTF(ret, "PUBSUB", "sk", "CHANNELS", sizeof("CHANNELS") - 1,
3058 Z_STRVAL_P(arg), Z_STRLEN_P(arg));
3059 } else {
3060 /* No pattern */
3061 return REDIS_SPPRINTF(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS") - 1);
3062 }
3063 } else if (type == PUBSUB_NUMSUB) {
3064 ht_chan = Z_ARRVAL_P(arg);
3065
3066 // Add PUBSUB and NUMSUB bits
3067 redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
3068 redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
3069
3070 /* Iterate our elements */
3071 ZEND_HASH_FOREACH_VAL(ht_chan, z_ele) {
3072 zend_string *zstr = zval_get_string(z_ele);
3073 redis_cmd_append_sstr_key(&cmd, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, NULL);
3074 zend_string_release(zstr);
3075 } ZEND_HASH_FOREACH_END();
3076
3077 /* Set return */
3078 *ret = cmd.c;
3079 return cmd.len;
3080 } else if (type == PUBSUB_NUMPAT) {
3081 return REDIS_SPPRINTF(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT") - 1);
3082 }
3083
3084 /* Shouldn't ever happen */
3085 return -1;
3086 }
3087
3088 /*
3089 * {{{ proto Redis::pubsub("channels", pattern);
3090 * proto Redis::pubsub("numsub", Array channels);
3091 * proto Redis::pubsub("numpat"); }}}
3092 */
PHP_METHOD(Redis,pubsub)3093 PHP_METHOD(Redis, pubsub) {
3094 zval *object;
3095 RedisSock *redis_sock;
3096 char *keyword, *cmd;
3097 int cmd_len;
3098 size_t kw_len;
3099 PUBSUB_TYPE type;
3100 zval *arg = NULL;
3101
3102 // Parse arguments
3103 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
3104 "Os|z", &object, redis_ce, &keyword,
3105 &kw_len, &arg)==FAILURE)
3106 {
3107 RETURN_FALSE;
3108 }
3109
3110 /* Validate our sub command keyword, and that we've got proper arguments */
3111 if(!strncasecmp(keyword, "channels", sizeof("channels"))) {
3112 /* One (optional) string argument */
3113 if(arg && Z_TYPE_P(arg) != IS_STRING) {
3114 RETURN_FALSE;
3115 }
3116 type = PUBSUB_CHANNELS;
3117 } else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
3118 /* One array argument */
3119 if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
3120 zend_hash_num_elements(Z_ARRVAL_P(arg)) == 0)
3121 {
3122 RETURN_FALSE;
3123 }
3124 type = PUBSUB_NUMSUB;
3125 } else if(!strncasecmp(keyword, "numpat", sizeof("numpat"))) {
3126 type = PUBSUB_NUMPAT;
3127 } else {
3128 /* Invalid keyword */
3129 RETURN_FALSE;
3130 }
3131
3132 /* Grab our socket context object */
3133 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
3134 RETURN_FALSE;
3135 }
3136
3137 /* Construct our "PUBSUB" command */
3138 cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg);
3139
3140 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
3141
3142 if(type == PUBSUB_NUMSUB) {
3143 if (IS_ATOMIC(redis_sock)) {
3144 if(redis_mbulk_reply_zipped_keys_int(INTERNAL_FUNCTION_PARAM_PASSTHRU,
3145 redis_sock, NULL, NULL) < 0)
3146 {
3147 RETURN_FALSE;
3148 }
3149 }
3150 REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_int);
3151 } else {
3152 if (IS_ATOMIC(redis_sock)) {
3153 if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
3154 redis_sock, NULL, NULL) < 0)
3155 {
3156 RETURN_FALSE;
3157 }
3158 }
3159 REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
3160 }
3161 }
3162
3163 /* {{{ proto variant Redis::eval(string script, [array keys, long num_keys]) */
PHP_METHOD(Redis,eval)3164 PHP_METHOD(Redis, eval)
3165 {
3166 REDIS_PROCESS_KW_CMD("EVAL", redis_eval_cmd, redis_read_raw_variant_reply);
3167 }
3168
3169 /* {{{ proto variant Redis::evalsha(string sha1, [array keys, long num_keys]) */
PHP_METHOD(Redis,evalsha)3170 PHP_METHOD(Redis, evalsha) {
3171 REDIS_PROCESS_KW_CMD("EVALSHA", redis_eval_cmd, redis_read_raw_variant_reply);
3172 }
3173
3174 /* {{{ proto status Redis::script('flush')
3175 * {{{ proto status Redis::script('kill')
3176 * {{{ proto string Redis::script('load', lua_script)
3177 * {{{ proto int Reids::script('exists', script_sha1 [, script_sha2, ...])
3178 */
PHP_METHOD(Redis,script)3179 PHP_METHOD(Redis, script) {
3180 zval *z_args;
3181 RedisSock *redis_sock;
3182 smart_string cmd = {0};
3183 int argc = ZEND_NUM_ARGS();
3184
3185 /* Attempt to grab our socket */
3186 if (argc < 1 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL) {
3187 RETURN_FALSE;
3188 }
3189
3190 /* Allocate an array big enough to store our arguments */
3191 z_args = ecalloc(argc, sizeof(zval));
3192
3193 /* Make sure we can grab our arguments, we have a string directive */
3194 if (zend_get_parameters_array(ht, argc, z_args) == FAILURE ||
3195 redis_build_script_cmd(&cmd, argc, z_args) == NULL
3196 ) {
3197 efree(z_args);
3198 RETURN_FALSE;
3199 }
3200
3201 /* Free our allocated arguments */
3202 efree(z_args);
3203
3204 // Kick off our request
3205 REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len);
3206 if (IS_ATOMIC(redis_sock)) {
3207 if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
3208 redis_sock, NULL, NULL) < 0)
3209 {
3210 RETURN_FALSE;
3211 }
3212 }
3213 REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
3214 }
3215
3216 /* {{{ proto DUMP key */
PHP_METHOD(Redis,dump)3217 PHP_METHOD(Redis, dump) {
3218 REDIS_PROCESS_KW_CMD("DUMP", redis_key_cmd, redis_string_response);
3219 }
3220 /* }}} */
3221
3222 /* {{{ proto Redis::restore(ttl, key, value) */
PHP_METHOD(Redis,restore)3223 PHP_METHOD(Redis, restore) {
3224 REDIS_PROCESS_KW_CMD("RESTORE", redis_key_long_val_cmd,
3225 redis_boolean_response);
3226 }
3227 /* }}} */
3228
3229 /* {{{ proto Redis::debug(string key) */
PHP_METHOD(Redis,debug)3230 PHP_METHOD(Redis, debug) {
3231 REDIS_PROCESS_KW_CMD("DEBUG", redis_key_cmd, redis_string_response);
3232 }
3233 /* }}} */
3234
3235 /* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy,
3236 * bool replace]) */
PHP_METHOD(Redis,migrate)3237 PHP_METHOD(Redis, migrate) {
3238 REDIS_PROCESS_CMD(migrate, redis_boolean_response);
3239 }
3240
3241 /* {{{ proto Redis::_prefix(key) */
PHP_METHOD(Redis,_prefix)3242 PHP_METHOD(Redis, _prefix) {
3243 RedisSock *redis_sock;
3244
3245 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
3246 RETURN_FALSE;
3247 }
3248
3249 redis_prefix_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
3250 }
3251
3252 /* {{{ proto Redis::_serialize(value) */
PHP_METHOD(Redis,_serialize)3253 PHP_METHOD(Redis, _serialize) {
3254 RedisSock *redis_sock;
3255
3256 // Grab socket
3257 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
3258 RETURN_FALSE;
3259 }
3260
3261 redis_serialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
3262 }
3263
3264 /* {{{ proto Redis::_unserialize(value) */
PHP_METHOD(Redis,_unserialize)3265 PHP_METHOD(Redis, _unserialize) {
3266 RedisSock *redis_sock;
3267
3268 // Grab socket
3269 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
3270 RETURN_FALSE;
3271 }
3272
3273 redis_unserialize_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
3274 redis_exception_ce);
3275 }
3276
PHP_METHOD(Redis,_compress)3277 PHP_METHOD(Redis, _compress) {
3278 RedisSock *redis_sock;
3279
3280 // Grab socket
3281 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
3282 RETURN_FALSE;
3283 }
3284
3285 redis_compress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
3286 }
3287
PHP_METHOD(Redis,_uncompress)3288 PHP_METHOD(Redis, _uncompress) {
3289 RedisSock *redis_sock;
3290
3291 // Grab socket
3292 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
3293 RETURN_FALSE;
3294 }
3295
3296 redis_uncompress_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
3297 redis_exception_ce);
3298 }
3299
PHP_METHOD(Redis,_pack)3300 PHP_METHOD(Redis, _pack) {
3301 RedisSock *redis_sock;
3302
3303 // Grab socket
3304 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
3305 RETURN_FALSE;
3306 }
3307
3308 redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
3309 }
3310
PHP_METHOD(Redis,_unpack)3311 PHP_METHOD(Redis, _unpack) {
3312 RedisSock *redis_sock;
3313
3314 // Grab socket
3315 if ((redis_sock = redis_sock_get_instance(getThis(), 0)) == NULL) {
3316 RETURN_FALSE;
3317 }
3318
3319 redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
3320 }
3321
3322 /* {{{ proto Redis::getLastError() */
PHP_METHOD(Redis,getLastError)3323 PHP_METHOD(Redis, getLastError) {
3324 zval *object;
3325 RedisSock *redis_sock;
3326
3327 // Grab our object
3328 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
3329 &object, redis_ce) == FAILURE)
3330 {
3331 RETURN_FALSE;
3332 }
3333
3334 // Grab socket
3335 if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
3336 RETURN_FALSE;
3337 }
3338
3339 /* Return our last error or NULL if we don't have one */
3340 if (redis_sock->err) {
3341 RETURN_STRINGL(ZSTR_VAL(redis_sock->err), ZSTR_LEN(redis_sock->err));
3342 }
3343 RETURN_NULL();
3344 }
3345
3346 /* {{{ proto Redis::clearLastError() */
PHP_METHOD(Redis,clearLastError)3347 PHP_METHOD(Redis, clearLastError) {
3348 zval *object;
3349 RedisSock *redis_sock;
3350
3351 // Grab our object
3352 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
3353 &object, redis_ce) == FAILURE)
3354 {
3355 RETURN_FALSE;
3356 }
3357 // Grab socket
3358 if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
3359 RETURN_FALSE;
3360 }
3361
3362 // Clear error message
3363 if (redis_sock->err) {
3364 zend_string_release(redis_sock->err);
3365 redis_sock->err = NULL;
3366 }
3367
3368 RETURN_TRUE;
3369 }
3370
3371 /*
3372 * {{{ proto long Redis::getMode()
3373 */
PHP_METHOD(Redis,getMode)3374 PHP_METHOD(Redis, getMode) {
3375 zval *object;
3376 RedisSock *redis_sock;
3377
3378 /* Grab our object */
3379 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, redis_ce) == FAILURE) {
3380 RETURN_FALSE;
3381 }
3382
3383 /* Grab socket */
3384 if ((redis_sock = redis_sock_get_instance(object, 0)) == NULL) {
3385 RETURN_FALSE;
3386 }
3387
3388 if (IS_PIPELINE(redis_sock)) {
3389 RETVAL_LONG(PIPELINE);
3390 } else if (IS_MULTI(redis_sock)) {
3391 RETVAL_LONG(MULTI);
3392 } else {
3393 RETVAL_LONG(ATOMIC);
3394 }
3395 }
3396
3397 /* {{{ proto Redis::time() */
PHP_METHOD(Redis,time)3398 PHP_METHOD(Redis, time) {
3399 REDIS_PROCESS_KW_CMD("TIME", redis_empty_cmd, redis_mbulk_reply_raw);
3400 }
3401
3402 /* {{{ proto array Redis::role() */
PHP_METHOD(Redis,role)3403 PHP_METHOD(Redis, role) {
3404 REDIS_PROCESS_KW_CMD("ROLE", redis_empty_cmd, redis_read_variant_reply);
3405 }
3406
3407 /*
3408 * Introspection stuff
3409 */
3410
3411 /* {{{ proto Redis::IsConnected */
PHP_METHOD(Redis,isConnected)3412 PHP_METHOD(Redis, isConnected) {
3413 RedisSock *redis_sock;
3414
3415 if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
3416 RETURN_TRUE;
3417 } else {
3418 RETURN_FALSE;
3419 }
3420 }
3421
3422 /* {{{ proto Redis::getHost() */
PHP_METHOD(Redis,getHost)3423 PHP_METHOD(Redis, getHost) {
3424 RedisSock *redis_sock;
3425
3426 if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
3427 RETURN_STRINGL(ZSTR_VAL(redis_sock->host), ZSTR_LEN(redis_sock->host));
3428 } else {
3429 RETURN_FALSE;
3430 }
3431 }
3432
3433 /* {{{ proto Redis::getPort() */
PHP_METHOD(Redis,getPort)3434 PHP_METHOD(Redis, getPort) {
3435 RedisSock *redis_sock;
3436
3437 if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
3438 /* Return our port */
3439 RETURN_LONG(redis_sock->port);
3440 } else {
3441 RETURN_FALSE;
3442 }
3443 }
3444
3445 /* {{{ proto Redis::getDBNum */
PHP_METHOD(Redis,getDBNum)3446 PHP_METHOD(Redis, getDBNum) {
3447 RedisSock *redis_sock;
3448
3449 if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
3450 /* Return our db number */
3451 RETURN_LONG(redis_sock->dbNumber);
3452 } else {
3453 RETURN_FALSE;
3454 }
3455 }
3456
3457 /* {{{ proto Redis::getTimeout */
PHP_METHOD(Redis,getTimeout)3458 PHP_METHOD(Redis, getTimeout) {
3459 RedisSock *redis_sock;
3460
3461 if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
3462 RETURN_DOUBLE(redis_sock->timeout);
3463 } else {
3464 RETURN_FALSE;
3465 }
3466 }
3467
3468 /* {{{ proto Redis::getReadTimeout */
PHP_METHOD(Redis,getReadTimeout)3469 PHP_METHOD(Redis, getReadTimeout) {
3470 RedisSock *redis_sock;
3471
3472 if((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU))) {
3473 RETURN_DOUBLE(redis_sock->read_timeout);
3474 } else {
3475 RETURN_FALSE;
3476 }
3477 }
3478
3479 /* {{{ proto Redis::getPersistentID */
PHP_METHOD(Redis,getPersistentID)3480 PHP_METHOD(Redis, getPersistentID) {
3481 RedisSock *redis_sock;
3482
3483 if ((redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU)) == NULL) {
3484 RETURN_FALSE;
3485 } else if (redis_sock->persistent_id == NULL) {
3486 RETURN_NULL();
3487 }
3488 RETURN_STRINGL(ZSTR_VAL(redis_sock->persistent_id), ZSTR_LEN(redis_sock->persistent_id));
3489 }
3490
3491 /* {{{ proto Redis::getAuth */
PHP_METHOD(Redis,getAuth)3492 PHP_METHOD(Redis, getAuth) {
3493 RedisSock *redis_sock;
3494 zval zret;
3495
3496 if (zend_parse_parameters_none() == FAILURE) {
3497 RETURN_FALSE;
3498 }
3499
3500 redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU);
3501 if (redis_sock == NULL)
3502 RETURN_FALSE;
3503
3504 if (redis_sock->user && redis_sock->pass) {
3505 array_init(&zret);
3506 add_next_index_str(&zret, zend_string_copy(redis_sock->user));
3507 add_next_index_str(&zret, zend_string_copy(redis_sock->pass));
3508 RETURN_ZVAL(&zret, 0, 0);
3509 } else if (redis_sock->pass) {
3510 RETURN_STR_COPY(redis_sock->pass);
3511 } else {
3512 RETURN_NULL();
3513 }
3514 }
3515
3516 /*
3517 * $redis->client('list');
3518 * $redis->client('kill', <ip:port>);
3519 * $redis->client('setname', <name>);
3520 * $redis->client('getname');
3521 */
PHP_METHOD(Redis,client)3522 PHP_METHOD(Redis, client) {
3523 zval *object;
3524 RedisSock *redis_sock;
3525 char *cmd, *opt = NULL, *arg = NULL;
3526 size_t opt_len, arg_len;
3527 int cmd_len;
3528
3529 // Parse our method parameters
3530 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
3531 "Os|s", &object, redis_ce, &opt, &opt_len,
3532 &arg, &arg_len) == FAILURE)
3533 {
3534 RETURN_FALSE;
3535 }
3536
3537 /* Grab our socket */
3538 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
3539 RETURN_FALSE;
3540 }
3541
3542 /* Build our CLIENT command */
3543 if (ZEND_NUM_ARGS() == 2) {
3544 cmd_len = REDIS_SPPRINTF(&cmd, "CLIENT", "ss", opt, opt_len, arg, arg_len);
3545 } else {
3546 cmd_len = REDIS_SPPRINTF(&cmd, "CLIENT", "s", opt, opt_len);
3547 }
3548
3549 /* Execute our queue command */
3550 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
3551
3552 /* We handle CLIENT LIST with a custom response function */
3553 if(!strncasecmp(opt, "list", 4)) {
3554 if (IS_ATOMIC(redis_sock)) {
3555 redis_client_list_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, NULL, NULL);
3556 }
3557 REDIS_PROCESS_RESPONSE(redis_client_list_reply);
3558 } else {
3559 if (IS_ATOMIC(redis_sock)) {
3560 redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
3561 redis_sock,NULL,NULL);
3562 }
3563 REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
3564 }
3565 }
3566
3567 /* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */
PHP_METHOD(Redis,rawcommand)3568 PHP_METHOD(Redis, rawcommand) {
3569 int argc = ZEND_NUM_ARGS(), cmd_len;
3570 char *cmd = NULL;
3571 RedisSock *redis_sock;
3572 zval *z_args;
3573
3574 /* Sanity check on arguments */
3575 if (argc < 1) {
3576 php_error_docref(NULL, E_WARNING,
3577 "Must pass at least one command keyword");
3578 RETURN_FALSE;
3579 }
3580 z_args = emalloc(argc * sizeof(zval));
3581 if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) {
3582 php_error_docref(NULL, E_WARNING,
3583 "Internal PHP error parsing arguments");
3584 efree(z_args);
3585 RETURN_FALSE;
3586 } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 ||
3587 (redis_sock = redis_sock_get(getThis(), 0)) == NULL
3588 ) {
3589 if (cmd) efree(cmd);
3590 efree(z_args);
3591 RETURN_FALSE;
3592 }
3593
3594 /* Clean up command array */
3595 efree(z_args);
3596
3597 /* Execute our command */
3598 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
3599 if (IS_ATOMIC(redis_sock)) {
3600 redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL);
3601 }
3602 REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
3603 }
3604 /* }}} */
3605
3606 /* {{{ proto array Redis::command()
3607 * proto array Redis::command('info', string cmd)
3608 * proto array Redis::command('getkeys', array cmd_args) */
PHP_METHOD(Redis,command)3609 PHP_METHOD(Redis, command) {
3610 REDIS_PROCESS_CMD(command, redis_read_variant_reply);
3611 }
3612 /* }}} */
3613
3614 /* Helper to format any combination of SCAN arguments */
3615 PHP_REDIS_API int
redis_build_scan_cmd(char ** cmd,REDIS_SCAN_TYPE type,char * key,int key_len,long iter,char * pattern,int pattern_len,int count,zend_string * match_type)3616 redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
3617 long iter, char *pattern, int pattern_len, int count,
3618 zend_string *match_type)
3619 {
3620 smart_string cmdstr = {0};
3621 char *keyword;
3622 int argc;
3623
3624 /* Count our arguments +1 for key if it's got one, and + 2 for pattern */
3625 /* or count given that they each carry keywords with them. */
3626 argc = 1 + (key_len > 0) + (pattern_len > 0 ? 2 : 0) + (count > 0 ? 2 : 0) + (match_type ? 2 : 0);
3627
3628 /* Turn our type into a keyword */
3629 switch(type) {
3630 case TYPE_SCAN:
3631 keyword = "SCAN";
3632 break;
3633 case TYPE_SSCAN:
3634 keyword = "SSCAN";
3635 break;
3636 case TYPE_HSCAN:
3637 keyword = "HSCAN";
3638 break;
3639 case TYPE_ZSCAN:
3640 default:
3641 keyword = "ZSCAN";
3642 break;
3643 }
3644
3645 /* Start the command */
3646 redis_cmd_init_sstr(&cmdstr, argc, keyword, strlen(keyword));
3647 if (key_len) redis_cmd_append_sstr(&cmdstr, key, key_len);
3648 redis_cmd_append_sstr_long(&cmdstr, iter);
3649
3650 /* Append COUNT if we've got it */
3651 if(count) {
3652 REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT");
3653 redis_cmd_append_sstr_int(&cmdstr, count);
3654 }
3655
3656 /* Append MATCH if we've got it */
3657 if(pattern_len) {
3658 REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "MATCH");
3659 redis_cmd_append_sstr(&cmdstr, pattern, pattern_len);
3660 }
3661
3662 if (match_type) {
3663 REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TYPE");
3664 redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(match_type), ZSTR_LEN(match_type));
3665 }
3666
3667 /* Return our command length */
3668 *cmd = cmdstr.c;
3669 return cmdstr.len;
3670 }
3671
3672 /* {{{ proto redis::scan(&$iterator, [pattern, [count, [type]]]) */
3673 PHP_REDIS_API void
generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS,REDIS_SCAN_TYPE type)3674 generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
3675 zval *object, *z_iter;
3676 RedisSock *redis_sock;
3677 HashTable *hash;
3678 char *pattern = NULL, *cmd, *key = NULL;
3679 int cmd_len, num_elements, key_free = 0, pattern_free = 0;
3680 size_t key_len = 0, pattern_len = 0;
3681 zend_string *match_type = NULL;
3682 zend_long count = 0, iter;
3683
3684 /* Different prototype depending on if this is a key based scan */
3685 if(type != TYPE_SCAN) {
3686 // Requires a key
3687 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
3688 "Osz/|s!l", &object, redis_ce, &key,
3689 &key_len, &z_iter, &pattern,
3690 &pattern_len, &count)==FAILURE)
3691 {
3692 RETURN_FALSE;
3693 }
3694 } else {
3695 // Doesn't require a key
3696 if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
3697 "Oz/|s!lS", &object, redis_ce, &z_iter,
3698 &pattern, &pattern_len, &count, &match_type)
3699 == FAILURE)
3700 {
3701 RETURN_FALSE;
3702 }
3703 }
3704
3705 /* Grab our socket */
3706 if ((redis_sock = redis_sock_get(object, 0)) == NULL) {
3707 RETURN_FALSE;
3708 }
3709
3710 /* Calling this in a pipeline makes no sense */
3711 if (!IS_ATOMIC(redis_sock)) {
3712 php_error_docref(NULL, E_ERROR,
3713 "Can't call SCAN commands in multi or pipeline mode!");
3714 RETURN_FALSE;
3715 }
3716
3717 // The iterator should be passed in as NULL for the first iteration, but we
3718 // can treat any NON LONG value as NULL for these purposes as we've
3719 // separated the variable anyway.
3720 if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter) < 0) {
3721 /* Convert to long */
3722 convert_to_long(z_iter);
3723 iter = 0;
3724 } else if(Z_LVAL_P(z_iter) != 0) {
3725 /* Update our iterator value for the next passthru */
3726 iter = Z_LVAL_P(z_iter);
3727 } else {
3728 /* We're done, back to iterator zero */
3729 RETURN_FALSE;
3730 }
3731
3732 /* Prefix our key if we've got one and we have a prefix set */
3733 if(key_len) {
3734 key_free = redis_key_prefix(redis_sock, &key, &key_len);
3735 }
3736
3737 if (redis_sock->scan & REDIS_SCAN_PREFIX) {
3738 pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len);
3739 }
3740
3741 /**
3742 * Redis can return to us empty keys, especially in the case where there
3743 * are a large number of keys to scan, and we're matching against a
3744 * pattern. phpredis can be set up to abstract this from the user, by
3745 * setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty
3746 * keys and the user will need to make subsequent calls with an updated
3747 * iterator.
3748 */
3749 do {
3750 /* Free our previous reply if we're back in the loop. We know we are
3751 * if our return_value is an array */
3752 if (Z_TYPE_P(return_value) == IS_ARRAY) {
3753 zval_dtor(return_value);
3754 ZVAL_NULL(return_value);
3755 }
3756
3757 // Format our SCAN command
3758 cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (long)iter,
3759 pattern, pattern_len, count, match_type);
3760
3761 /* Execute our command getting our new iterator value */
3762 REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
3763 if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
3764 redis_sock,type,&iter) < 0)
3765 {
3766 if(key_free) efree(key);
3767 RETURN_FALSE;
3768 }
3769
3770 /* Get the number of elements */
3771 hash = Z_ARRVAL_P(return_value);
3772 num_elements = zend_hash_num_elements(hash);
3773 } while (redis_sock->scan & REDIS_SCAN_RETRY && iter != 0 &&
3774 num_elements == 0);
3775
3776 /* Free our pattern if it was prefixed */
3777 if (pattern_free) efree(pattern);
3778
3779 /* Free our key if it was prefixed */
3780 if(key_free) efree(key);
3781
3782 /* Update our iterator reference */
3783 Z_LVAL_P(z_iter) = iter;
3784 }
3785
PHP_METHOD(Redis,scan)3786 PHP_METHOD(Redis, scan) {
3787 generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SCAN);
3788 }
PHP_METHOD(Redis,hscan)3789 PHP_METHOD(Redis, hscan) {
3790 generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN);
3791 }
PHP_METHOD(Redis,sscan)3792 PHP_METHOD(Redis, sscan) {
3793 generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN);
3794 }
PHP_METHOD(Redis,zscan)3795 PHP_METHOD(Redis, zscan) {
3796 generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
3797 }
3798
3799 /*
3800 * HyperLogLog based commands
3801 */
3802
3803 /* {{{ proto Redis::pfAdd(string key, array elements) }}} */
PHP_METHOD(Redis,pfadd)3804 PHP_METHOD(Redis, pfadd) {
3805 REDIS_PROCESS_CMD(pfadd, redis_long_response);
3806 }
3807
3808 /* {{{ proto Redis::pfCount(string key) }}}*/
PHP_METHOD(Redis,pfcount)3809 PHP_METHOD(Redis, pfcount) {
3810 REDIS_PROCESS_CMD(pfcount, redis_long_response);
3811 }
3812
3813 /* {{{ proto Redis::pfMerge(string dstkey, array keys) }}}*/
PHP_METHOD(Redis,pfmerge)3814 PHP_METHOD(Redis, pfmerge) {
3815 REDIS_PROCESS_CMD(pfmerge, redis_boolean_response);
3816 }
3817
3818 /*
3819 * Geo commands
3820 */
3821
PHP_METHOD(Redis,geoadd)3822 PHP_METHOD(Redis, geoadd) {
3823 REDIS_PROCESS_KW_CMD("GEOADD", redis_key_varval_cmd, redis_long_response);
3824 }
3825
PHP_METHOD(Redis,geohash)3826 PHP_METHOD(Redis, geohash) {
3827 REDIS_PROCESS_KW_CMD("GEOHASH", redis_key_varval_cmd, redis_mbulk_reply_raw);
3828 }
3829
PHP_METHOD(Redis,geopos)3830 PHP_METHOD(Redis, geopos) {
3831 REDIS_PROCESS_KW_CMD("GEOPOS", redis_key_varval_cmd, redis_read_variant_reply);
3832 }
3833
PHP_METHOD(Redis,geodist)3834 PHP_METHOD(Redis, geodist) {
3835 REDIS_PROCESS_CMD(geodist, redis_bulk_double_response);
3836 }
3837
PHP_METHOD(Redis,georadius)3838 PHP_METHOD(Redis, georadius) {
3839 REDIS_PROCESS_KW_CMD("GEORADIUS", redis_georadius_cmd, redis_read_variant_reply);
3840 }
3841
PHP_METHOD(Redis,georadius_ro)3842 PHP_METHOD(Redis, georadius_ro) {
3843 REDIS_PROCESS_KW_CMD("GEORADIUS_RO", redis_georadius_cmd, redis_read_variant_reply);
3844 }
3845
PHP_METHOD(Redis,georadiusbymember)3846 PHP_METHOD(Redis, georadiusbymember) {
3847 REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER", redis_georadiusbymember_cmd, redis_read_variant_reply);
3848 }
3849
PHP_METHOD(Redis,georadiusbymember_ro)3850 PHP_METHOD(Redis, georadiusbymember_ro) {
3851 REDIS_PROCESS_KW_CMD("GEORADIUSBYMEMBER_RO", redis_georadiusbymember_cmd, redis_read_variant_reply);
3852 }
3853
3854 /*
3855 * Streams
3856 */
3857
PHP_METHOD(Redis,xack)3858 PHP_METHOD(Redis, xack) {
3859 REDIS_PROCESS_CMD(xack, redis_long_response);
3860 }
3861
PHP_METHOD(Redis,xadd)3862 PHP_METHOD(Redis, xadd) {
3863 REDIS_PROCESS_CMD(xadd, redis_read_variant_reply);
3864 }
3865
PHP_METHOD(Redis,xclaim)3866 PHP_METHOD(Redis, xclaim) {
3867 REDIS_PROCESS_CMD(xclaim, redis_xclaim_reply);
3868 }
3869
PHP_METHOD(Redis,xdel)3870 PHP_METHOD(Redis, xdel) {
3871 REDIS_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, redis_long_response);
3872 }
3873
PHP_METHOD(Redis,xgroup)3874 PHP_METHOD(Redis, xgroup) {
3875 REDIS_PROCESS_CMD(xgroup, redis_read_variant_reply);
3876 }
3877
PHP_METHOD(Redis,xinfo)3878 PHP_METHOD(Redis, xinfo) {
3879 REDIS_PROCESS_CMD(xinfo, redis_xinfo_reply);
3880 }
3881
PHP_METHOD(Redis,xlen)3882 PHP_METHOD(Redis, xlen) {
3883 REDIS_PROCESS_KW_CMD("XLEN", redis_key_cmd, redis_long_response);
3884 }
3885
PHP_METHOD(Redis,xpending)3886 PHP_METHOD(Redis, xpending) {
3887 REDIS_PROCESS_CMD(xpending, redis_read_variant_reply_strings);
3888 }
3889
PHP_METHOD(Redis,xrange)3890 PHP_METHOD(Redis, xrange) {
3891 REDIS_PROCESS_KW_CMD("XRANGE", redis_xrange_cmd, redis_xrange_reply);
3892 }
3893
PHP_METHOD(Redis,xread)3894 PHP_METHOD(Redis, xread) {
3895 REDIS_PROCESS_CMD(xread, redis_xread_reply);
3896 }
3897
PHP_METHOD(Redis,xreadgroup)3898 PHP_METHOD(Redis, xreadgroup) {
3899 REDIS_PROCESS_CMD(xreadgroup, redis_xread_reply);
3900 }
3901
PHP_METHOD(Redis,xrevrange)3902 PHP_METHOD(Redis, xrevrange) {
3903 REDIS_PROCESS_KW_CMD("XREVRANGE", redis_xrange_cmd, redis_xrange_reply);
3904 }
3905
PHP_METHOD(Redis,xtrim)3906 PHP_METHOD(Redis, xtrim) {
3907 REDIS_PROCESS_CMD(xtrim, redis_long_response);
3908 }
3909
3910 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
3911