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