1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) 2009-2010 The PHP Group                                |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | http://www.php.net/license/3_01.txt.                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Authors: Andrei Zmievski <andrei@php.net>                            |
14   +----------------------------------------------------------------------+
15 */
16 
17 /* TODO
18  * - set LIBKETAMA_COMPATIBLE as the default?
19  * - fix unserialize(serialize($memc))
20  */
21 
22 #include "php_memcached.h"
23 #include "php_memcached_private.h"
24 #include "php_memcached_server.h"
25 #include "g_fmt.h"
26 
27 #include <ctype.h>
28 #include <limits.h>
29 
30 #ifdef HAVE_MEMCACHED_SESSION
31 # include "php_memcached_session.h"
32 #endif
33 #ifdef HAVE_FASTLZ_H
34 #include <fastlz.h>
35 #else
36 #include "fastlz/fastlz.h"
37 #endif
38 #include <zlib.h>
39 
40 #ifdef HAVE_JSON_API
41 # include "ext/json/php_json.h"
42 #endif
43 
44 #ifdef HAVE_MEMCACHED_IGBINARY
45 #ifdef PHP_WIN32
46 //Windows extensions are generally built together,
47 //so it wont be in the installed location
48 #include "igbinary.h"
49 #else
50 # include "ext/igbinary/igbinary.h"
51 #endif
52 #endif
53 
54 #ifdef HAVE_MEMCACHED_MSGPACK
55 # include "ext/msgpack/php_msgpack.h"
56 #endif
57 
58 # include "ext/spl/spl_exceptions.h"
59 
60 static int le_memc;
61 
php_memc_list_entry(void)62 static int php_memc_list_entry(void) {
63 	return le_memc;
64 }
65 
66 /****************************************
67   Protocol parameters
68 ****************************************/
69 #define MEMC_OBJECT_KEY_MAX_LENGTH 250
70 
71 /****************************************
72   Custom options
73 ****************************************/
74 #define MEMC_OPT_COMPRESSION        -1001
75 #define MEMC_OPT_PREFIX_KEY         -1002
76 #define MEMC_OPT_SERIALIZER         -1003
77 #define MEMC_OPT_COMPRESSION_TYPE   -1004
78 #define MEMC_OPT_STORE_RETRY_COUNT  -1005
79 #define MEMC_OPT_USER_FLAGS         -1006
80 
81 /****************************************
82   Custom result codes
83 ****************************************/
84 #define MEMC_RES_PAYLOAD_FAILURE -1001
85 
86 /****************************************
87   Payload value flags
88 ****************************************/
89 #define MEMC_CREATE_MASK(start, n_bits) (((1 << n_bits) - 1) << start)
90 
91 #define MEMC_MASK_TYPE     MEMC_CREATE_MASK(0, 4)
92 #define MEMC_MASK_INTERNAL MEMC_CREATE_MASK(4, 12)
93 #define MEMC_MASK_USER     MEMC_CREATE_MASK(16, 16)
94 
95 #define MEMC_VAL_GET_TYPE(flags)       ((flags) & MEMC_MASK_TYPE)
96 #define MEMC_VAL_SET_TYPE(flags, type) ((flags) |= ((type) & MEMC_MASK_TYPE))
97 
98 #define MEMC_VAL_IS_STRING     0
99 #define MEMC_VAL_IS_LONG       1
100 #define MEMC_VAL_IS_DOUBLE     2
101 #define MEMC_VAL_IS_BOOL       3
102 #define MEMC_VAL_IS_SERIALIZED 4
103 #define MEMC_VAL_IS_IGBINARY   5
104 #define MEMC_VAL_IS_JSON       6
105 #define MEMC_VAL_IS_MSGPACK    7
106 
107 #define MEMC_VAL_COMPRESSED          (1<<0)
108 #define MEMC_VAL_COMPRESSION_ZLIB    (1<<1)
109 #define MEMC_VAL_COMPRESSION_FASTLZ  (1<<2)
110 
111 #define MEMC_VAL_GET_FLAGS(internal_flags)               (((internal_flags) & MEMC_MASK_INTERNAL) >> 4)
112 #define MEMC_VAL_SET_FLAG(internal_flags, internal_flag) ((internal_flags) |= (((internal_flag) << 4) & MEMC_MASK_INTERNAL))
113 #define MEMC_VAL_HAS_FLAG(internal_flags, internal_flag) ((MEMC_VAL_GET_FLAGS(internal_flags) & (internal_flag)) == (internal_flag))
114 #define MEMC_VAL_DEL_FLAG(internal_flags, internal_flag) (internal_flags &= (~(((internal_flag) << 4) & MEMC_MASK_INTERNAL)))
115 
116 /****************************************
117   User-defined flags
118 ****************************************/
119 #define MEMC_VAL_GET_USER_FLAGS(flags)            ((flags & MEMC_MASK_USER) >> 16)
120 #define MEMC_VAL_SET_USER_FLAGS(flags, udf_flags) ((flags) |= ((udf_flags << 16) & MEMC_MASK_USER))
121 #define MEMC_VAL_USER_FLAGS_MAX                   ((1 << 16) - 1)
122 
123 /****************************************
124   "get" operation flags
125 ****************************************/
126 #define MEMC_GET_PRESERVE_ORDER 1
127 #define MEMC_GET_EXTENDED       2
128 
129 /****************************************
130   Helper macros
131 ****************************************/
132 #define RETURN_FROM_GET RETURN_FALSE
133 
134 /****************************************
135   Structures and definitions
136 ****************************************/
137 
138 typedef enum {
139 	MEMC_OP_SET,
140 	MEMC_OP_TOUCH,
141 	MEMC_OP_ADD,
142 	MEMC_OP_REPLACE,
143 	MEMC_OP_APPEND,
144 	MEMC_OP_PREPEND
145 } php_memc_write_op;
146 
147 typedef struct {
148 
149 	zend_bool is_persistent;
150 	zend_bool compression_enabled;
151 	zend_bool encoding_enabled;
152 
153 	zend_long serializer;
154 	zend_long compression_type;
155 
156 	zend_long store_retry_count;
157 	zend_long set_udf_flags;
158 
159 #ifdef HAVE_MEMCACHED_SASL
160 	zend_bool has_sasl_data;
161 #endif
162 } php_memc_user_data_t;
163 
164 typedef struct {
165 	memcached_st *memc;
166 	zend_bool is_pristine;
167 	int rescode;
168 	int memc_errno;
169 	zend_object zo;
170 } php_memc_object_t;
171 
172 typedef struct {
173 	size_t num_valid_keys;
174 
175 	const char **mkeys;
176 	size_t *mkeys_len;
177 
178 	zend_string **strings;
179 
180 } php_memc_keys_t;
181 
182 typedef struct {
183 	zval *object;
184 	zend_fcall_info fci;
185 	zend_fcall_info_cache fcc;
186 } php_memc_result_callback_ctx_t;
187 
php_memc_fetch_object(zend_object * obj)188 static inline php_memc_object_t *php_memc_fetch_object(zend_object *obj) {
189 	return (php_memc_object_t *)((char *)obj - XtOffsetOf(php_memc_object_t, zo));
190 }
191 #define Z_MEMC_OBJ_P(zv) php_memc_fetch_object(Z_OBJ_P(zv));
192 
193 #define MEMC_METHOD_INIT_VARS                          \
194 	zval*                  object         = getThis(); \
195 	php_memc_object_t*     intern         = NULL;      \
196 	php_memc_user_data_t*  memc_user_data = NULL;
197 
198 #if PHP_VERSION_ID < 80000
199 #define MEMC_METHOD_FETCH_OBJECT                                                      \
200 	intern = Z_MEMC_OBJ_P(object);                                                    \
201 	if (!intern->memc) {                                                              \
202 		php_error_docref(NULL, E_WARNING, "Memcached constructor was not called");    \
203 		return;                                                                       \
204 	}                                                                                 \
205 	memc_user_data = (php_memc_user_data_t *) memcached_get_user_data(intern->memc);  \
206 	(void)memc_user_data; /* avoid unused variable warning */
207 #else
208 #define MEMC_METHOD_FETCH_OBJECT                                                      \
209 	intern = Z_MEMC_OBJ_P(object);                                                    \
210 	if (!intern->memc) {                                                              \
211 		zend_throw_error(NULL, "Memcached constructor was not called");               \
212 		RETURN_THROWS();                                                                       \
213 	}                                                                                 \
214 	memc_user_data = (php_memc_user_data_t *) memcached_get_user_data(intern->memc);  \
215 	(void)memc_user_data; /* avoid unused variable warning */
216 #endif
217 
218 static
s_memc_valid_key_binary(zend_string * key)219 zend_bool s_memc_valid_key_binary(zend_string *key)
220 {
221 	return memchr(ZSTR_VAL(key), '\n', ZSTR_LEN(key)) == NULL;
222 }
223 
224 static
s_memc_valid_key_ascii(zend_string * key)225 zend_bool s_memc_valid_key_ascii(zend_string *key)
226 {
227 	const char *str = ZSTR_VAL(key);
228 	size_t i, len = ZSTR_LEN(key);
229 
230 	for (i = 0; i < len; i++) {
231 		if (!isgraph(str[i]) || isspace(str[i]))
232 			return 0;
233 	}
234 	return 1;
235 }
236 
237 #define MEMC_CHECK_KEY(intern, key)                                               \
238 	if (UNEXPECTED(ZSTR_LEN(key) == 0 ||                                          \
239 		ZSTR_LEN(key) > MEMC_OBJECT_KEY_MAX_LENGTH ||                             \
240 		(memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) \
241 				? !s_memc_valid_key_binary(key)                                   \
242 				: !s_memc_valid_key_ascii(key)                                    \
243 		))) {                                                                     \
244 		intern->rescode = MEMCACHED_BAD_KEY_PROVIDED;                             \
245 		RETURN_FALSE;                                                             \
246 	}
247 
248 #ifdef HAVE_MEMCACHED_PROTOCOL
249 typedef struct {
250 	php_memc_proto_handler_t *handler;
251 	zend_object zo;
252 } php_memc_server_t;
253 
php_memc_server_fetch_object(zend_object * obj)254 static inline php_memc_server_t *php_memc_server_fetch_object(zend_object *obj) {
255 	return (php_memc_server_t *)((char *)obj - XtOffsetOf(php_memc_server_t, zo));
256 }
257 #define Z_MEMC_SERVER_P(zv) php_memc_server_fetch_object(Z_OBJ_P(zv))
258 
259 static zend_object_handlers memcached_server_object_handlers;
260 static zend_class_entry *memcached_server_ce = NULL;
261 #endif
262 
263 static zend_class_entry *memcached_ce = NULL;
264 static zend_class_entry *memcached_exception_ce = NULL;
265 static zend_object_handlers memcached_object_handlers;
266 
267 ZEND_DECLARE_MODULE_GLOBALS(php_memcached)
268 
269 #ifdef COMPILE_DL_MEMCACHED
ZEND_GET_MODULE(memcached)270 ZEND_GET_MODULE(memcached)
271 #endif
272 
273 static PHP_INI_MH(OnUpdateCompressionType)
274 {
275 	if (!new_value) {
276 		MEMC_G(compression_type) = COMPRESSION_TYPE_FASTLZ;
277 	} else if (!strcmp(ZSTR_VAL(new_value), "fastlz")) {
278 		MEMC_G(compression_type) = COMPRESSION_TYPE_FASTLZ;
279 	} else if (!strcmp(ZSTR_VAL(new_value), "zlib")) {
280 		MEMC_G(compression_type) = COMPRESSION_TYPE_ZLIB;
281 	} else {
282 		return FAILURE;
283 	}
284 	return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
285 }
286 
PHP_INI_MH(OnUpdateSerializer)287 static PHP_INI_MH(OnUpdateSerializer)
288 {
289 	if (!new_value) {
290 		MEMC_G(serializer_type) = SERIALIZER_DEFAULT;
291 	} else if (!strcmp(ZSTR_VAL(new_value), "php")) {
292 		MEMC_G(serializer_type) = SERIALIZER_PHP;
293 #ifdef HAVE_MEMCACHED_IGBINARY
294 	} else if (!strcmp(ZSTR_VAL(new_value), "igbinary")) {
295 		MEMC_G(serializer_type) = SERIALIZER_IGBINARY;
296 #endif // IGBINARY
297 #ifdef HAVE_JSON_API
298 	} else if (!strcmp(ZSTR_VAL(new_value), "json")) {
299 		MEMC_G(serializer_type) = SERIALIZER_JSON;
300 	} else if (!strcmp(ZSTR_VAL(new_value), "json_array")) {
301 		MEMC_G(serializer_type) = SERIALIZER_JSON_ARRAY;
302 #endif // JSON
303 #ifdef HAVE_MEMCACHED_MSGPACK
304 	} else if (!strcmp(ZSTR_VAL(new_value), "msgpack")) {
305 		MEMC_G(serializer_type) = SERIALIZER_MSGPACK;
306 #endif // msgpack
307 	} else {
308 		return FAILURE;
309 	}
310 
311 	return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
312 }
313 
314 #ifdef HAVE_MEMCACHED_SESSION
315 static
PHP_INI_MH(OnUpdateDeprecatedLockValue)316 PHP_INI_MH(OnUpdateDeprecatedLockValue)
317 {
318 	if (ZSTR_LEN(new_value) > 0 && strcmp(ZSTR_VAL(new_value), "not set")) {
319 		php_error_docref(NULL, E_DEPRECATED, "memcached.sess_lock_wait and memcached.sess_lock_max_wait are deprecated. Please update your configuration to use memcached.sess_lock_wait_min, memcached.sess_lock_wait_max and memcached.sess_lock_retries");
320 	}
321 	return FAILURE;
322 }
323 
324 static
PHP_INI_MH(OnUpdateSessionPrefixString)325 PHP_INI_MH(OnUpdateSessionPrefixString)
326 {
327 	if (new_value && ZSTR_LEN(new_value) > 0) {
328 		if (ZSTR_LEN(new_value) > MEMCACHED_MAX_KEY) {
329 			php_error_docref(NULL, E_WARNING, "memcached.sess_prefix too long (max: %d)", MEMCACHED_MAX_KEY - 1);
330 			return FAILURE;
331 		}
332 		if (!s_memc_valid_key_ascii(new_value)) {
333 			php_error_docref(NULL, E_WARNING, "memcached.sess_prefix cannot contain whitespace or control characters");
334 			return FAILURE;
335 		}
336 	}
337 	return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
338 }
339 
340 static
PHP_INI_MH(OnUpdateConsistentHash)341 PHP_INI_MH(OnUpdateConsistentHash)
342 {
343 	if (!new_value) {
344 		MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA;
345 	} else if (!strcmp(ZSTR_VAL(new_value), "ketama")) {
346 		MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA;
347 	} else if (!strcmp(ZSTR_VAL(new_value), "ketama_weighted")) {
348 		MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED;
349 	} else {
350 		php_error_docref(NULL, E_WARNING, "memcached.sess_consistent_hash_type must be ketama or ketama_weighted");
351 		return FAILURE;
352 	}
353 	return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
354 }
355 #endif // HAVE_MEMCACHED_SESSION
356 
357 #define MEMC_INI_ENTRY(key, default_value, update_fn, gkey) \
358 	STD_PHP_INI_ENTRY("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals)
359 
360 #define MEMC_INI_BOOL(key, default_value, update_fn, gkey) \
361 	STD_PHP_INI_BOOLEAN("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals)
362 
363 #define MEMC_INI_LINK(key, default_value, update_fn, gkey) \
364 	STD_PHP_INI_ENTRY_EX("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals, display_link_numbers)
365 
366 #define MEMC_SESSION_INI_ENTRY(key, default_value, update_fn, gkey) \
367 	STD_PHP_INI_ENTRY("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals)
368 
369 #define MEMC_SESSION_INI_BOOL(key, default_value, update_fn, gkey) \
370 	STD_PHP_INI_BOOLEAN("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals)
371 
372 #define MEMC_SESSION_INI_LINK(key, default_value, update_fn, gkey) \
373 	STD_PHP_INI_ENTRY_EX("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals, display_link_numbers)
374 
375 
376 /* {{{ INI entries */
377 PHP_INI_BEGIN()
378 
379 #ifdef HAVE_MEMCACHED_SESSION
380 	MEMC_SESSION_INI_BOOL ("locking",                "1",          OnUpdateBool,           lock_enabled)
381 	MEMC_SESSION_INI_ENTRY("lock_wait_min",          "150",        OnUpdateLongGEZero,     lock_wait_min)
382 	MEMC_SESSION_INI_ENTRY("lock_wait_max",          "150",        OnUpdateLongGEZero,     lock_wait_max)
383 	MEMC_SESSION_INI_ENTRY("lock_retries",           "5",          OnUpdateLong,           lock_retries)
384 	MEMC_SESSION_INI_ENTRY("lock_expire",            "0",          OnUpdateLongGEZero,     lock_expiration)
385 #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018
386 	MEMC_SESSION_INI_BOOL ("binary_protocol",        "0",          OnUpdateBool,           binary_protocol_enabled)
387 #else
388 	MEMC_SESSION_INI_BOOL ("binary_protocol",        "1",          OnUpdateBool,           binary_protocol_enabled)
389 #endif
390 	MEMC_SESSION_INI_BOOL ("consistent_hash",        "1",          OnUpdateBool,           consistent_hash_enabled)
391 	MEMC_SESSION_INI_ENTRY("consistent_hash_type",   "ketama",     OnUpdateConsistentHash, consistent_hash_name)
392 	MEMC_SESSION_INI_ENTRY("number_of_replicas",     "0",          OnUpdateLongGEZero,     number_of_replicas)
393 	MEMC_SESSION_INI_BOOL ("randomize_replica_read", "0",          OnUpdateBool,           randomize_replica_read_enabled)
394 	MEMC_SESSION_INI_BOOL ("remove_failed_servers",  "0",          OnUpdateBool,           remove_failed_servers_enabled)
395 	MEMC_SESSION_INI_ENTRY("server_failure_limit",   "0",          OnUpdateLongGEZero,     server_failure_limit)
396 	MEMC_SESSION_INI_LINK ("connect_timeout",        "0",          OnUpdateLong,           connect_timeout)
397 
398 	MEMC_SESSION_INI_ENTRY("sasl_username",          "",           OnUpdateString,         sasl_username)
399 	MEMC_SESSION_INI_ENTRY("sasl_password",          "",           OnUpdateString,         sasl_password)
400 	MEMC_SESSION_INI_BOOL ("persistent",             "0",          OnUpdateBool,           persistent_enabled)
401 	MEMC_SESSION_INI_ENTRY("prefix",                 "memc.sess.key.", OnUpdateSessionPrefixString,         prefix)
402 
403 	/* Deprecated */
404 	STD_PHP_INI_ENTRY("memcached.sess_lock_wait", "not set", PHP_INI_ALL, OnUpdateDeprecatedLockValue, no_effect, zend_php_memcached_globals, php_memcached_globals)
405 	STD_PHP_INI_ENTRY("memcached.sess_lock_max_wait", "not set", PHP_INI_ALL, OnUpdateDeprecatedLockValue, no_effect, zend_php_memcached_globals, php_memcached_globals)
406 
407 #endif
408 
409 	MEMC_INI_ENTRY("compression_type",      "fastlz",                OnUpdateCompressionType, compression_name)
410 	MEMC_INI_ENTRY("compression_factor",    "1.3",                   OnUpdateReal,            compression_factor)
411 	MEMC_INI_ENTRY("compression_threshold", "2000",                  OnUpdateLong,            compression_threshold)
412 	MEMC_INI_ENTRY("serializer",            SERIALIZER_DEFAULT_NAME, OnUpdateSerializer,      serializer_name)
413 	MEMC_INI_ENTRY("store_retry_count",     "2",                     OnUpdateLong,            store_retry_count)
414 
415 	MEMC_INI_BOOL ("default_consistent_hash",       "0", OnUpdateBool,       default_behavior.consistent_hash_enabled)
416 	MEMC_INI_BOOL ("default_binary_protocol",       "0", OnUpdateBool,       default_behavior.binary_protocol_enabled)
417 	MEMC_INI_LINK ("default_connect_timeout",       "0", OnUpdateLong,       default_behavior.connect_timeout)
418 
419 PHP_INI_END()
420 /* }}} */
421 
422 #undef MEMC_INI_BOOL
423 #undef MEMC_INI_LINK
424 #undef MEMC_INI_ENTRY
425 #undef MEMC_SESSION_INI_BOOL
426 #undef MEMC_SESSION_INI_LINK
427 #undef MEMC_SESSION_INI_ENTRY
428 
429 /****************************************
430   Forward declarations
431 ****************************************/
432 static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
433 static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
434 static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key);
435 static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
436 static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
437 static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
438 static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
439 
440 /* Invoke PHP functions */
441 static zend_bool s_invoke_cache_callback(zval *zobject, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zend_bool with_cas, zend_string *key, zval *value);
442 
443 /* Iterate result sets */
444 typedef zend_bool (*php_memc_result_apply_fn)(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *context);
445 
446 static
447 memcached_return php_memc_result_apply(php_memc_object_t *intern, php_memc_result_apply_fn result_apply_fn, zend_bool fetch_delay, void *context);
448 
449 static
450 zend_bool php_memc_mget_apply(php_memc_object_t *intern, zend_string *server_key,
451 								php_memc_keys_t *keys, php_memc_result_apply_fn result_apply_fn,
452 								zend_bool with_cas, void *context);
453 
454 
455 /* Callback functions for different server list iterations */
456 static
457 	memcached_return s_server_cursor_list_servers_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context);
458 
459 static
460 	memcached_return s_server_cursor_version_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context);
461 
462 static
463 	zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, zend_string *server_key, zend_string *key, zval *value, time_t expiration);
464 
465 static
466 	void php_memc_destroy(memcached_st *memc, php_memc_user_data_t *memc_user_data);
467 
468 static
469 	zend_bool s_memcached_result_to_zval(memcached_st *memc, memcached_result_st *result, zval *return_value);
470 
471 static
472 	zend_string *s_zval_to_payload(php_memc_object_t *intern, zval *value, uint32_t *flags);
473 
474 static
475 	void s_hash_to_keys(php_memc_keys_t *keys_out, HashTable *hash_in, zend_bool preserve_order, zval *return_value);
476 
477 static
478 	void s_clear_keys(php_memc_keys_t *keys);
479 
480 
481 /****************************************
482   Exported helper functions
483 ****************************************/
484 
php_memc_init_sasl_if_needed()485 zend_bool php_memc_init_sasl_if_needed()
486 {
487 #ifdef HAVE_MEMCACHED_SASL
488 	if (MEMC_G(sasl_initialised)) {
489 		return 1;
490 	}
491 	if (sasl_client_init(NULL) != SASL_OK) {
492 		php_error_docref(NULL, E_ERROR, "Failed to initialize SASL library");
493 		return 0;
494 	}
495 	return 1;
496 #else
497 	php_error_docref(NULL, E_ERROR, "Memcached not built with sasl support");
498 	return 0;
499 #endif
500 }
501 
php_memc_printable_func(zend_fcall_info * fci,zend_fcall_info_cache * fci_cache)502 char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache)
503 {
504 	char *buffer = NULL;
505 
506 	if (fci->object) {
507 		spprintf (&buffer, 0, "%s::%s", ZSTR_VAL(fci->object->ce->name), ZSTR_VAL(fci_cache->function_handler->common.function_name));
508 	} else {
509 		if (Z_TYPE (fci->function_name) == IS_OBJECT) {
510 			spprintf (&buffer, 0, "%s", ZSTR_VAL(Z_OBJCE(fci->function_name)->name));
511 		}
512 		else {
513 			spprintf (&buffer, 0, "%s", Z_STRVAL(fci->function_name));
514 		}
515 	}
516 	return buffer;
517 }
518 
519 
520 /****************************************
521   Handling error codes
522 ****************************************/
523 
524 static
s_memcached_return_is_error(memcached_return status,zend_bool strict)525 zend_bool s_memcached_return_is_error(memcached_return status, zend_bool strict)
526 {
527 	zend_bool result = 0;
528 
529 	switch (status) {
530 		case MEMCACHED_SUCCESS:
531 		case MEMCACHED_STORED:
532 		case MEMCACHED_DELETED:
533 		case MEMCACHED_STAT:
534 		case MEMCACHED_END:
535 		case MEMCACHED_BUFFERED:
536 			result = 0;
537 			break;
538 
539 		case MEMCACHED_SOME_ERRORS:
540 			result = strict;
541 			break;
542 
543 		default:
544 			result = 1;
545 			break;
546 	}
547 	return result;
548 }
549 
550 static
s_memc_status_handle_result_code(php_memc_object_t * intern,memcached_return status)551 int s_memc_status_handle_result_code(php_memc_object_t *intern, memcached_return status)
552 {
553 	intern->rescode = status;
554 	intern->memc_errno = 0;
555 
556 	if (s_memcached_return_is_error(status, 1)) {
557 		intern->memc_errno = memcached_last_error_errno(intern->memc);
558 		return FAILURE;
559 	}
560 	return SUCCESS;
561 }
562 
563 static
s_memc_set_status(php_memc_object_t * intern,memcached_return status,int memc_errno)564 void s_memc_set_status(php_memc_object_t *intern, memcached_return status, int memc_errno)
565 {
566 	intern->rescode = status;
567 	intern->memc_errno = memc_errno;
568 }
569 
570 static
s_memc_status_has_error(php_memc_object_t * intern)571 zend_bool s_memc_status_has_error(php_memc_object_t *intern)
572 {
573 	return s_memcached_return_is_error(intern->rescode, 1);
574 }
575 
576 static
s_memc_status_has_result_code(php_memc_object_t * intern,memcached_return status)577 zend_bool s_memc_status_has_result_code(php_memc_object_t *intern, memcached_return status)
578 {
579 	return intern->rescode == status;
580 }
581 
582 /****************************************
583   Marshall cas tokens
584 ****************************************/
585 
586 static
s_uint64_to_zval(zval * target,uint64_t value)587 void s_uint64_to_zval (zval *target, uint64_t value)
588 {
589 	if (value >= ((uint64_t) LONG_MAX)) {
590 		zend_string *buffer;
591 #ifdef PRIu64
592 		buffer = strpprintf (0, "%" PRIu64, value);
593 #else
594 		/* Best effort */
595 		buffer = strpprintf (0, "%llu", value);
596 #endif
597 		ZVAL_STR(target, buffer);
598 	}
599 	else {
600 		ZVAL_LONG (target, (zend_long) value);
601 	}
602 }
603 
604 static
s_zval_to_uint64(zval * cas)605 uint64_t s_zval_to_uint64 (zval *cas)
606 {
607 	switch (Z_TYPE_P (cas)) {
608 		case IS_LONG:
609 			return (uint64_t) zval_get_long (cas);
610 		break;
611 
612 		case IS_DOUBLE:
613 			if (Z_DVAL_P (cas) < 0.0)
614 				return 0;
615 
616 			return (uint64_t) zval_get_double (cas);
617 		break;
618 
619 		case IS_STRING:
620 		{
621 			uint64_t val;
622 			char *end;
623 
624 			errno = 0;
625 			val = (uint64_t) strtoull (Z_STRVAL_P (cas), &end, 0);
626 
627 			if (*end || (errno == ERANGE && val == UINT64_MAX) || (errno != 0 && val == 0)) {
628 				php_error_docref(NULL, E_ERROR, "Failed to unmarshall cas token");
629 				return 0;
630 			}
631 			return val;
632 		}
633 		break;
634 	}
635 	return 0;
636 }
637 
638 
639 /****************************************
640   Iterate over memcached results and mget
641 ****************************************/
642 
643 static
php_memc_result_apply(php_memc_object_t * intern,php_memc_result_apply_fn result_apply_fn,zend_bool fetch_delay,void * context)644 memcached_return php_memc_result_apply(php_memc_object_t *intern, php_memc_result_apply_fn result_apply_fn, zend_bool fetch_delay, void *context)
645 {
646 	memcached_result_st result, *result_ptr;
647 	memcached_return rc, status = MEMCACHED_SUCCESS;
648 
649 	memcached_result_create(intern->memc, &result);
650 
651 	do {
652 		result_ptr = memcached_fetch_result(intern->memc, &result, &rc);
653 
654 		if (s_memcached_return_is_error(rc, 0)) {
655 			status = rc;
656 		}
657 
658 		/* nothing returned */
659 		if (!result_ptr) {
660 			break;
661 		}
662 		else {
663 			zend_string *key;
664 			zval val, zcas;
665 			zend_bool retval;
666 
667 			uint64_t cas;
668 			uint32_t flags;
669 
670 			const char *res_key;
671 			size_t res_key_len;
672 
673 			if (!s_memcached_result_to_zval(intern->memc, &result, &val)) {
674 				if (EG(exception)) {
675 					status = MEMC_RES_PAYLOAD_FAILURE;
676 					memcached_quit(intern->memc);
677 					break;
678 				}
679 				status = MEMCACHED_SOME_ERRORS;
680 				continue;
681 			}
682 
683 			res_key     = memcached_result_key_value(&result);
684 			res_key_len = memcached_result_key_length(&result);
685 			cas         = memcached_result_cas(&result);
686 			flags       = memcached_result_flags(&result);
687 
688 			s_uint64_to_zval(&zcas, cas);
689 
690 			key = zend_string_init (res_key, res_key_len, 0);
691 			retval = result_apply_fn(intern, key, &val, &zcas, flags, context);
692 
693 			zend_string_release(key);
694 			zval_ptr_dtor(&val);
695 			zval_ptr_dtor(&zcas);
696 
697 			/* Stop iterating on false */
698 			if (!retval) {
699 				if (!fetch_delay) {
700 					/* Make sure we clear our results */
701 					while (memcached_fetch_result(intern->memc, &result, &rc)) {}
702 				}
703 				break;
704 			}
705 		}
706 	} while (result_ptr != NULL);
707 
708 	memcached_result_free(&result);
709 	return status;
710 }
711 
712 static
php_memc_mget_apply(php_memc_object_t * intern,zend_string * server_key,php_memc_keys_t * keys,php_memc_result_apply_fn result_apply_fn,zend_bool with_cas,void * context)713 zend_bool php_memc_mget_apply(php_memc_object_t *intern, zend_string *server_key, php_memc_keys_t *keys,
714 						php_memc_result_apply_fn result_apply_fn, zend_bool with_cas, void *context)
715 {
716 	memcached_return status;
717 	int mget_status;
718 	uint64_t orig_cas_flag = 0;
719 
720 	// Reset status code
721 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
722 
723 	if (!keys->num_valid_keys) {
724 		intern->rescode = MEMCACHED_BAD_KEY_PROVIDED;
725 		return 0;
726 	}
727 
728 	if (with_cas) {
729 		orig_cas_flag = memcached_behavior_get (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS);
730 
731 		if (!orig_cas_flag) {
732 			memcached_behavior_set (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1);
733 		}
734 	}
735 
736 	if (server_key) {
737 		status = memcached_mget_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), keys->mkeys, keys->mkeys_len, keys->num_valid_keys);
738 	} else {
739 		status = memcached_mget(intern->memc, keys->mkeys, keys->mkeys_len, keys->num_valid_keys);
740 	}
741 
742 	/* Need to handle result code before restoring cas flags, would mess up errno */
743 	mget_status = s_memc_status_handle_result_code(intern, status);
744 
745 	if (with_cas && !orig_cas_flag) {
746 		memcached_behavior_set (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag);
747 	}
748 
749 	/* Return on failure codes */
750 	if (mget_status == FAILURE) {
751 		return 0;
752 	}
753 
754 	if (!result_apply_fn) {
755 		/* no callback, for example getDelayed */
756 		return 1;
757 	}
758 
759 	status = php_memc_result_apply(intern, result_apply_fn, 0, context);
760 
761 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
762 		return 0;
763 	}
764 
765 	return 1;
766 }
767 
768 /****************************************
769   Invoke callbacks
770 ****************************************/
771 
772 static
s_invoke_new_instance_cb(zval * object,zend_fcall_info * fci,zend_fcall_info_cache * fci_cache,zend_string * persistent_id)773 zend_bool s_invoke_new_instance_cb(zval *object, zend_fcall_info *fci, zend_fcall_info_cache *fci_cache, zend_string *persistent_id)
774 {
775 	zend_bool ret = 1;
776 	zval retval;
777 	zval params[2];
778 
779 	ZVAL_COPY(&params[0], object);
780 	if (persistent_id) {
781 		ZVAL_STR(&params[1], zend_string_copy(persistent_id));
782 	} else {
783 		ZVAL_NULL(&params[1]);
784 	}
785 
786 	fci->retval = &retval;
787 	fci->params = params;
788 	fci->param_count = 2;
789 
790 	if (zend_call_function(fci, fci_cache) == FAILURE) {
791 		char *buf = php_memc_printable_func (fci, fci_cache);
792 		php_error_docref(NULL, E_WARNING, "Failed to invoke 'on_new' callback %s()", buf);
793 		efree (buf);
794 		ret = 0;
795 	}
796 
797 	zval_ptr_dtor(&params[0]);
798 	zval_ptr_dtor(&params[1]);
799 	zval_ptr_dtor(&retval);
800 
801 	return ret;
802 }
803 
804 static
s_invoke_cache_callback(zval * zobject,zend_fcall_info * fci,zend_fcall_info_cache * fcc,zend_bool with_cas,zend_string * key,zval * value)805 zend_bool s_invoke_cache_callback(zval *zobject, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zend_bool with_cas, zend_string *key, zval *value)
806 {
807 	zend_bool status = 0;
808 	zval params[4];
809 	zval retval;
810 	php_memc_object_t *intern = Z_MEMC_OBJ_P(zobject);
811 
812 	/* Prepare params */
813 	ZVAL_COPY(&params[0], zobject);
814 	ZVAL_STR_COPY(&params[1], key);    /* key */
815 	ZVAL_NEW_REF(&params[2], value);   /* value */
816 
817 	if (with_cas) {
818 		fci->param_count = 3;
819 	} else {
820 		ZVAL_NEW_EMPTY_REF(&params[3]);    /* expiration */
821 		ZVAL_NULL(Z_REFVAL(params[3]));
822 		fci->param_count = 4;
823 	}
824 
825 	fci->retval = &retval;
826 	fci->params = params;
827 
828 	if (zend_call_function(fci, fcc) == SUCCESS) {
829 		if (zend_is_true(&retval)) {
830 			time_t expiration;
831 			zval *val = Z_REFVAL(params[2]);
832 
833 			if (with_cas) {
834 				if (Z_TYPE_P(val) == IS_ARRAY) {
835 					zval *rv = zend_hash_str_find(Z_ARRVAL_P(val), "value", sizeof("value") - 1);
836 					if (rv) {
837 						zval *cas = zend_hash_str_find(Z_ARRVAL_P(val), "cas", sizeof("cas") -1);
838 						expiration = cas? Z_LVAL_P(cas) : 0;
839 						status = s_memc_write_zval (intern, MEMC_OP_SET, NULL, key, rv, expiration);
840 					}
841 					/* memleak?  zval_ptr_dtor(value); */
842 					ZVAL_COPY(value, val);
843 				}
844 			} else {
845 				expiration = zval_get_long(Z_REFVAL(params[3]));
846 				status = s_memc_write_zval (intern, MEMC_OP_SET, NULL, key, val, expiration);
847 				/* memleak?  zval_ptr_dtor(value); */
848 				ZVAL_COPY(value, val);
849 			}
850 		}
851 	}
852 	else {
853 		s_memc_set_status(intern, MEMCACHED_NOTFOUND, 0);
854 	}
855 
856 	zval_ptr_dtor(&params[0]);
857 	zval_ptr_dtor(&params[1]);
858 	zval_ptr_dtor(&params[2]);
859 	if (!with_cas) {
860 		zval_ptr_dtor(&params[3]);
861 	}
862 	zval_ptr_dtor(&retval);
863 
864 	return status;
865 }
866 
867 /****************************************
868   Wrapper for setting from zval
869 ****************************************/
870 
871 static
s_compress_value(php_memc_compression_type compression_type,zend_string ** payload_in,uint32_t * flags)872 zend_bool s_compress_value (php_memc_compression_type compression_type, zend_string **payload_in, uint32_t *flags)
873 {
874 	/* status */
875 	zend_bool compress_status = 0;
876 	zend_string *payload = *payload_in;
877 	uint32_t compression_type_flag = 0;
878 
879 	/* Additional 5% for the data */
880 	size_t buffer_size = (size_t) (((double) ZSTR_LEN(payload) * 1.05) + 1.0);
881 	char *buffer       = emalloc(buffer_size);
882 
883 	/* Store compressed size here */
884 	size_t compressed_size = 0;
885 	uint32_t original_size = ZSTR_LEN(payload);
886 
887 	switch (compression_type) {
888 
889 		case COMPRESSION_TYPE_FASTLZ:
890 		{
891 			compressed_size = fastlz_compress(ZSTR_VAL(payload), ZSTR_LEN(payload), buffer);
892 
893 			if (compressed_size > 0) {
894 				compress_status = 1;
895 				compression_type_flag = MEMC_VAL_COMPRESSION_FASTLZ;
896 			}
897 		}
898 			break;
899 
900 		case COMPRESSION_TYPE_ZLIB:
901 		{
902 			compressed_size = buffer_size;
903 			int status = compress((Bytef *) buffer, &compressed_size, (Bytef *) ZSTR_VAL(payload), ZSTR_LEN(payload));
904 
905 			if (status == Z_OK) {
906 				compress_status = 1;
907 				compression_type_flag = MEMC_VAL_COMPRESSION_ZLIB;
908 			}
909 		}
910 			break;
911 
912 		default:
913 			compress_status = 0;
914 			break;
915 	}
916 
917 	/* This means the value was too small to be compressed and ended up larger */
918 	if (ZSTR_LEN(payload) <= (compressed_size * MEMC_G(compression_factor))) {
919 		compress_status = 0;
920 	}
921 
922 	/* Replace the payload with the compressed copy */
923 	if (compress_status) {
924 		MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSED | compression_type_flag);
925 		payload = zend_string_realloc(payload, compressed_size + sizeof(uint32_t), 0);
926 
927 		/* Copy the uin32_t at the beginning */
928 		memcpy(ZSTR_VAL(payload), &original_size, sizeof(uint32_t));
929 		memcpy(ZSTR_VAL(payload) + sizeof (uint32_t), buffer, compressed_size);
930 		efree(buffer);
931 
932 		zend_string_forget_hash_val(payload);
933 		*payload_in = payload;
934 
935 		return 1;
936 	}
937 
938 	/* Original payload was not modified */
939 	efree(buffer);
940 	return 0;
941 }
942 
943 static
s_serialize_value(php_memc_serializer_type serializer,zval * value,smart_str * buf,uint32_t * flags)944 zend_bool s_serialize_value (php_memc_serializer_type serializer, zval *value, smart_str *buf, uint32_t *flags)
945 {
946 	switch (serializer) {
947 
948 		/*
949 			Igbinary serialization
950 		*/
951 #ifdef HAVE_MEMCACHED_IGBINARY
952 		case SERIALIZER_IGBINARY:
953 		{
954 			uint8_t *buffer;
955 			size_t buffer_len;
956 
957 			if (igbinary_serialize(&buffer, &buffer_len, value) != 0) {
958 				php_error_docref(NULL, E_WARNING, "could not serialize value with igbinary");
959 				return 0;
960 			}
961 			smart_str_appendl (buf, (char *)buffer, buffer_len);
962 			efree(buffer);
963 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_IGBINARY);
964 		}
965 			break;
966 #endif
967 
968 		/*
969 			JSON serialization
970 		*/
971 #ifdef HAVE_JSON_API
972 		case SERIALIZER_JSON:
973 		case SERIALIZER_JSON_ARRAY:
974 		{
975 			php_json_encode(buf, value, 0);
976 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_JSON);
977 		}
978 			break;
979 #endif
980 
981 		/*
982 			msgpack serialization
983 		*/
984 #ifdef HAVE_MEMCACHED_MSGPACK
985 		case SERIALIZER_MSGPACK:
986 			php_msgpack_serialize(buf, value);
987 			if (!buf->s) {
988 				php_error_docref(NULL, E_WARNING, "could not serialize value with msgpack");
989 				return 0;
990 			}
991 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_MSGPACK);
992 			break;
993 #endif
994 
995 		/*
996 			PHP serialization
997 		*/
998 		default:
999 		{
1000 			php_serialize_data_t var_hash;
1001 			PHP_VAR_SERIALIZE_INIT(var_hash);
1002 			php_var_serialize(buf, value, &var_hash);
1003 			PHP_VAR_SERIALIZE_DESTROY(var_hash);
1004 
1005 			if (!buf->s) {
1006 				php_error_docref(NULL, E_WARNING, "could not serialize value");
1007 				return 0;
1008 			}
1009 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_SERIALIZED);
1010 		}
1011 			break;
1012 	}
1013 
1014 	/* Check for exceptions caused by serializers */
1015 	if (EG(exception) && buf->s->len) {
1016 		return 0;
1017 	}
1018 	return 1;
1019 }
1020 
1021 static
s_zval_to_payload(php_memc_object_t * intern,zval * value,uint32_t * flags)1022 zend_string *s_zval_to_payload(php_memc_object_t *intern, zval *value, uint32_t *flags)
1023 {
1024 	zend_string *payload;
1025 	php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
1026 	zend_bool should_compress = memc_user_data->compression_enabled;
1027 
1028 	switch (Z_TYPE_P(value)) {
1029 
1030 		case IS_STRING:
1031 			payload = zval_get_string(value);
1032 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_STRING);
1033 			break;
1034 
1035 		case IS_LONG:
1036 		{
1037 			smart_str buffer = {0};
1038 			smart_str_append_long (&buffer, Z_LVAL_P(value));
1039 			smart_str_0(&buffer);
1040 			payload = buffer.s;
1041 
1042 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_LONG);
1043 			should_compress = 0;
1044 		}
1045 			break;
1046 
1047 		case IS_DOUBLE:
1048 		{
1049 			char buffer[40];
1050 			php_memcached_g_fmt(buffer, Z_DVAL_P(value));
1051 			payload = zend_string_init (buffer, strlen (buffer), 0);
1052 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_DOUBLE);
1053 			should_compress = 0;
1054 		}
1055 			break;
1056 
1057 		case IS_TRUE:
1058 			payload = zend_string_init ("1", 1, 0);
1059 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL);
1060 			should_compress = 0;
1061 			break;
1062 
1063 		case IS_FALSE:
1064 			payload = zend_string_alloc (0, 0);
1065 			MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL);
1066 			should_compress = 0;
1067 			break;
1068 
1069 		default:
1070 		{
1071 			smart_str buffer = {0};
1072 
1073 			if (!s_serialize_value (memc_user_data->serializer, value, &buffer, flags)) {
1074 				smart_str_free(&buffer);
1075 				return NULL;
1076 			}
1077 			payload = buffer.s;
1078 		}
1079 			break;
1080 	}
1081 
1082 	/* turn off compression for values below the threshold */
1083 	if (ZSTR_LEN(payload) == 0 || ZSTR_LEN(payload) < MEMC_G(compression_threshold)) {
1084 		should_compress = 0;
1085 	}
1086 
1087 	/* If we have compression flag, compress the value */
1088 	if (should_compress) {
1089 		/* s_compress_value() will always leave a valid payload, even if that payload
1090 		 * did not actually get compressed. The flags will be set according to the
1091 		 * to the compression type or no compression.
1092 		 *
1093 		 * No need to check the return value because the payload is always valid.
1094 		 */
1095 		(void)s_compress_value (memc_user_data->compression_type, &payload, flags);
1096 	}
1097 
1098 	if (memc_user_data->set_udf_flags >= 0) {
1099 		MEMC_VAL_SET_USER_FLAGS(*flags, ((uint32_t) memc_user_data->set_udf_flags));
1100 	}
1101 
1102 	return payload;
1103 }
1104 
1105 static
s_should_retry_write(php_memc_object_t * intern,memcached_return status)1106 zend_bool s_should_retry_write (php_memc_object_t *intern, memcached_return status)
1107 {
1108 	if (memcached_server_count (intern->memc) == 0) {
1109 		return 0;
1110 	}
1111 
1112 	return s_memcached_return_is_error (status, 1);
1113 }
1114 
1115 static
s_memc_write_zval(php_memc_object_t * intern,php_memc_write_op op,zend_string * server_key,zend_string * key,zval * value,time_t expiration)1116 zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, zend_string *server_key, zend_string *key, zval *value, time_t expiration)
1117 {
1118 	uint32_t flags = 0;
1119 	zend_string *payload = NULL;
1120 	memcached_return status = 0;
1121 	php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
1122 	zend_long retries = memc_user_data->store_retry_count;
1123 
1124 	if (value) {
1125 		payload = s_zval_to_payload(intern, value, &flags);
1126 
1127 		if (!payload) {
1128 			s_memc_set_status(intern, MEMC_RES_PAYLOAD_FAILURE, 0);
1129 			return 0;
1130 		}
1131 	}
1132 
1133 #define memc_write_using_fn(fn_name) payload ? fn_name(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags) : MEMC_RES_PAYLOAD_FAILURE;
1134 #define memc_write_using_fn_by_key(fn_name) payload ? fn_name(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags) : MEMC_RES_PAYLOAD_FAILURE;
1135 
1136 	if (server_key) {
1137 		switch (op) {
1138 			case MEMC_OP_SET:
1139 				status = memc_write_using_fn_by_key(memcached_set_by_key);
1140 			break;
1141 
1142 			case MEMC_OP_TOUCH:
1143 				status = php_memcached_touch_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), expiration);
1144 			break;
1145 
1146 			case MEMC_OP_ADD:
1147 				status = memc_write_using_fn_by_key(memcached_add_by_key);
1148 			break;
1149 
1150 			case MEMC_OP_REPLACE:
1151 				status = memc_write_using_fn_by_key(memcached_replace_by_key);
1152 			break;
1153 
1154 			case MEMC_OP_APPEND:
1155 				status = memc_write_using_fn_by_key(memcached_append_by_key);
1156 			break;
1157 
1158 			case MEMC_OP_PREPEND:
1159 				status = memc_write_using_fn_by_key(memcached_prepend_by_key);
1160 			break;
1161 		}
1162 
1163 		if (status == MEMCACHED_END) {
1164 			status = MEMCACHED_SUCCESS;
1165 		}
1166 	}
1167 	else {
1168 retry:
1169 		switch (op) {
1170 			case MEMC_OP_SET:
1171 				status = memc_write_using_fn(memcached_set);
1172 			break;
1173 
1174 			case MEMC_OP_TOUCH:
1175 				status = php_memcached_touch(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), expiration);
1176 			break;
1177 
1178 			case MEMC_OP_ADD:
1179 				status = memc_write_using_fn(memcached_add);
1180 			break;
1181 
1182 			case MEMC_OP_REPLACE:
1183 				status = memc_write_using_fn(memcached_replace);
1184 			break;
1185 
1186 			case MEMC_OP_APPEND:
1187 				status = memc_write_using_fn(memcached_append);
1188 			break;
1189 
1190 			case MEMC_OP_PREPEND:
1191 				status = memc_write_using_fn(memcached_prepend);
1192 			break;
1193 		}
1194 		if (status == MEMCACHED_END) {
1195 			status = MEMCACHED_SUCCESS;
1196 		}
1197 	}
1198 
1199 	if (s_should_retry_write (intern, status) && retries-- > 0) {
1200 		goto retry;
1201 	}
1202 
1203 #undef memc_write_using_fn
1204 #undef memc_write_using_fn_by_key
1205 
1206 	if (payload) {
1207 		zend_string_release(payload);
1208 	}
1209 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
1210 		return 0;
1211 	}
1212 	return 1;
1213 }
1214 
1215 
1216 /****************************************
1217   Methods
1218 ****************************************/
1219 
1220 
1221 /* {{{ Memcached::__construct([string persistent_id[, callback on_new[, string connection_str]]]))
1222    Creates a Memcached object, optionally using persistent memcache connection */
PHP_METHOD(Memcached,__construct)1223 static PHP_METHOD(Memcached, __construct)
1224 {
1225 	php_memc_object_t *intern;
1226 	php_memc_user_data_t *memc_user_data;
1227 
1228 	zend_string *persistent_id = NULL;
1229 	zend_string *conn_str = NULL;
1230 	zend_string *plist_key = NULL;
1231 	zend_fcall_info fci = {0};
1232 	zend_fcall_info_cache fci_cache;
1233 
1234 	zend_bool is_persistent = 0;
1235 
1236 	/* "|S!f!S" */
1237 	ZEND_PARSE_PARAMETERS_START(0, 3)
1238 	        Z_PARAM_OPTIONAL
1239 	        Z_PARAM_STR_EX(persistent_id, 1, 0)
1240 	        Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
1241 	        Z_PARAM_STR(conn_str)
1242 	ZEND_PARSE_PARAMETERS_END();
1243 
1244 	intern = Z_MEMC_OBJ_P(getThis());
1245 	intern->is_pristine = 1;
1246 
1247 	if (persistent_id && persistent_id->len) {
1248 		zend_resource *le;
1249 
1250 		plist_key = zend_string_alloc(sizeof("memcached:id=") + persistent_id->len - 1, 0);
1251 		snprintf(ZSTR_VAL(plist_key), plist_key->len + 1, "memcached:id=%s", ZSTR_VAL(persistent_id));
1252 
1253 		if ((le = zend_hash_find_ptr(&EG(persistent_list), plist_key)) != NULL) {
1254 			if (le->type == php_memc_list_entry()) {
1255 				intern->memc = le->ptr;
1256 				intern->is_pristine = 0;
1257 				zend_string_release (plist_key);
1258 				return;
1259 			}
1260 		}
1261 		is_persistent = 1;
1262 	}
1263 
1264 	if (conn_str && conn_str->len > 0) {
1265 		intern->memc = memcached (ZSTR_VAL(conn_str), ZSTR_LEN(conn_str));
1266 	}
1267 	else {
1268 		intern->memc = memcached (NULL, 0);
1269 	}
1270 
1271 	if (!intern->memc) {
1272 		php_error_docref(NULL, E_ERROR, "Failed to allocate memory for memcached structure");
1273 		/* never reached */
1274 	}
1275 
1276 	memc_user_data                    = pecalloc (1, sizeof(*memc_user_data), is_persistent);
1277 	memc_user_data->serializer        = MEMC_G(serializer_type);
1278 	memc_user_data->compression_type  = MEMC_G(compression_type);
1279 	memc_user_data->compression_enabled = 1;
1280 	memc_user_data->encoding_enabled  = 0;
1281 	memc_user_data->store_retry_count = MEMC_G(store_retry_count);
1282 	memc_user_data->set_udf_flags     = -1;
1283 	memc_user_data->is_persistent     = is_persistent;
1284 
1285 	memcached_set_user_data(intern->memc, memc_user_data);
1286 
1287 	/* Set default behaviors */
1288 	if (MEMC_G(default_behavior.consistent_hash_enabled)) {
1289 		memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, MEMCACHED_DISTRIBUTION_CONSISTENT);
1290 		if (rc != MEMCACHED_SUCCESS) {
1291 			php_error_docref(NULL, E_WARNING, "Failed to turn on consistent hash: %s", memcached_strerror(intern->memc, rc));
1292 		}
1293 	}
1294 
1295 	if (MEMC_G(default_behavior.binary_protocol_enabled)) {
1296 		memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1);
1297 		if (rc != MEMCACHED_SUCCESS) {
1298 			php_error_docref(NULL, E_WARNING, "Failed to turn on binary protocol: %s", memcached_strerror(intern->memc, rc));
1299 		}
1300 		/* Also enable TCP_NODELAY when binary protocol is enabled */
1301 		rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1);
1302 		if (rc != MEMCACHED_SUCCESS) {
1303 			php_error_docref(NULL, E_WARNING, "Failed to set TCP_NODELAY: %s", memcached_strerror(intern->memc, rc));
1304 		}
1305 	}
1306 
1307 	if (MEMC_G(default_behavior.connect_timeout)) {
1308 		memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, MEMC_G(default_behavior.connect_timeout));
1309 		if (rc != MEMCACHED_SUCCESS) {
1310 			php_error_docref(NULL, E_WARNING, "Failed to set connect timeout: %s", memcached_strerror(intern->memc, rc));
1311 		}
1312 	}
1313 
1314 	if (fci.size) {
1315 		if (!s_invoke_new_instance_cb(getThis(), &fci, &fci_cache, persistent_id) || EG(exception)) {
1316 			/* error calling or exception thrown from callback */
1317 			if (plist_key) {
1318 				zend_string_release(plist_key);
1319 			}
1320 			/*
1321 				Setting intern->memc null prevents object destruction from freeing the memcached_st
1322 				We free it manually here because it might be persistent and has not been
1323 				registered to persistent_list yet
1324 			*/
1325 			php_memc_destroy(intern->memc, memc_user_data);
1326 			intern->memc = NULL;
1327 			return;
1328 		}
1329 	}
1330 
1331 	if (plist_key) {
1332 		zend_resource le;
1333 
1334 		le.type = php_memc_list_entry();
1335 		le.ptr  = intern->memc;
1336 
1337 		GC_SET_REFCOUNT(&le, 1);
1338 
1339 		/* plist_key is not a persistent allocated key, thus we use str_update here */
1340 		if (zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(plist_key), ZSTR_LEN(plist_key), &le, sizeof(le)) == NULL) {
1341 			zend_string_release(plist_key);
1342 			php_error_docref(NULL, E_ERROR, "could not register persistent entry");
1343 			/* not reached */
1344 		}
1345 		zend_string_release(plist_key);
1346 	}
1347 }
1348 /* }}} */
1349 
1350 
1351 
1352 static
s_hash_to_keys(php_memc_keys_t * keys_out,HashTable * hash_in,zend_bool preserve_order,zval * return_value)1353 void s_hash_to_keys(php_memc_keys_t *keys_out, HashTable *hash_in, zend_bool preserve_order, zval *return_value)
1354 {
1355 	size_t idx = 0, alloc_count;
1356 	zval *zv;
1357 
1358 	keys_out->num_valid_keys = 0;
1359 
1360 	alloc_count = zend_hash_num_elements(hash_in);
1361 	if (!alloc_count) {
1362 		return;
1363 	}
1364 	keys_out->mkeys     = ecalloc (alloc_count, sizeof (char *));
1365 	keys_out->mkeys_len = ecalloc (alloc_count, sizeof (size_t));
1366 	keys_out->strings   = ecalloc (alloc_count, sizeof (zend_string *));
1367 
1368 	ZEND_HASH_FOREACH_VAL(hash_in, zv) {
1369 		zend_string *key = zval_get_string(zv);
1370 
1371 		if (preserve_order && return_value) {
1372 			add_assoc_null_ex(return_value, ZSTR_VAL(key), ZSTR_LEN(key));
1373 		}
1374 
1375 		if (ZSTR_LEN(key) > 0 && ZSTR_LEN(key) < MEMCACHED_MAX_KEY) {
1376 			keys_out->mkeys[idx]     = ZSTR_VAL(key);
1377 			keys_out->mkeys_len[idx] = ZSTR_LEN(key);
1378 
1379 			keys_out->strings[idx] = key;
1380 			idx++;
1381 		}
1382 		else {
1383 			zend_string_release (key);
1384 		}
1385 
1386 	} ZEND_HASH_FOREACH_END();
1387 
1388 	if (!idx) {
1389 		efree (keys_out->mkeys);
1390 		efree (keys_out->mkeys_len);
1391 		efree (keys_out->strings);
1392 	}
1393 	keys_out->num_valid_keys = idx;
1394 }
1395 
1396 static
s_key_to_keys(php_memc_keys_t * keys_out,zend_string * key)1397 void s_key_to_keys(php_memc_keys_t *keys_out, zend_string *key)
1398 {
1399 	zval zv_keys;
1400 
1401 	array_init(&zv_keys);
1402 	add_next_index_str(&zv_keys, zend_string_copy(key));
1403 
1404 	s_hash_to_keys(keys_out, Z_ARRVAL(zv_keys), 0, NULL);
1405 	zval_ptr_dtor(&zv_keys);
1406 }
1407 
1408 static
s_clear_keys(php_memc_keys_t * keys)1409 void s_clear_keys(php_memc_keys_t *keys)
1410 {
1411 	size_t i;
1412 
1413 	if (!keys->num_valid_keys) {
1414 		return;
1415 	}
1416 
1417 	for (i = 0; i < keys->num_valid_keys; i++) {
1418 		zend_string_release (keys->strings[i]);
1419 	}
1420 	efree(keys->strings);
1421 	efree(keys->mkeys);
1422 	efree(keys->mkeys_len);
1423 }
1424 
1425 typedef struct {
1426 	zend_bool extended;
1427 	zval *return_value;
1428 } php_memc_get_ctx_t;
1429 
1430 static
s_get_apply_fn(php_memc_object_t * intern,zend_string * key,zval * value,zval * cas,uint32_t flags,void * in_context)1431 zend_bool s_get_apply_fn(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
1432 {
1433 	php_memc_get_ctx_t *context = (php_memc_get_ctx_t *) in_context;
1434 
1435 	if (context->extended) {
1436 		Z_TRY_ADDREF_P(value);
1437 		Z_TRY_ADDREF_P(cas);
1438 
1439 		array_init (context->return_value);
1440 		add_assoc_zval (context->return_value, "value", value);
1441 		add_assoc_zval (context->return_value, "cas",   cas);
1442 		add_assoc_long (context->return_value, "flags", (zend_long) MEMC_VAL_GET_USER_FLAGS(flags));
1443 	}
1444 	else {
1445 		ZVAL_COPY(context->return_value, value);
1446 	}
1447 	return 0; /* Stop after one */
1448 }
1449 
1450 static
php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key)1451 void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
1452 {
1453 	php_memc_get_ctx_t context = {0};
1454 	php_memc_keys_t keys = {0};
1455 	zend_long get_flags = 0;
1456 	zend_string *key;
1457 	zend_string *server_key = NULL;
1458 	zend_bool mget_status;
1459 	memcached_return status = MEMCACHED_SUCCESS;
1460 	zend_fcall_info fci = empty_fcall_info;
1461 	zend_fcall_info_cache fcc = empty_fcall_info_cache;
1462 	MEMC_METHOD_INIT_VARS;
1463 
1464 	if (by_key) {
1465 		/* "SS|f!l" */
1466 		ZEND_PARSE_PARAMETERS_START(2, 4)
1467 		        Z_PARAM_STR(server_key)
1468 		        Z_PARAM_STR(key)
1469 		        Z_PARAM_OPTIONAL
1470 		        Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
1471 		        Z_PARAM_LONG(get_flags)
1472 		ZEND_PARSE_PARAMETERS_END();
1473 	} else {
1474 		/* "S|f!l" */
1475 		ZEND_PARSE_PARAMETERS_START(1, 3)
1476 		        Z_PARAM_STR(key)
1477 		        Z_PARAM_OPTIONAL
1478 		        Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
1479 		        Z_PARAM_LONG(get_flags)
1480 		ZEND_PARSE_PARAMETERS_END();
1481 	}
1482 
1483 	MEMC_METHOD_FETCH_OBJECT;
1484 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
1485 	MEMC_CHECK_KEY(intern, key);
1486 
1487 	context.extended = (get_flags & MEMC_GET_EXTENDED);
1488 
1489 	context.return_value = return_value;
1490 
1491 	s_key_to_keys(&keys, key);
1492 	mget_status = php_memc_mget_apply(intern, server_key, &keys, s_get_apply_fn, context.extended, &context);
1493 	s_clear_keys(&keys);
1494 
1495 	if (!mget_status) {
1496 		if (s_memc_status_has_result_code(intern, MEMCACHED_NOTFOUND) && fci.size > 0) {
1497 			status = s_invoke_cache_callback(object, &fci, &fcc, context.extended, key, return_value);
1498 
1499 			if (!status) {
1500 				zval_ptr_dtor(return_value);
1501 				RETURN_FROM_GET;
1502 			}
1503 		}
1504 	}
1505 
1506 	if (s_memc_status_has_error(intern)) {
1507 		zval_ptr_dtor(return_value);
1508 		RETURN_FROM_GET;
1509 	}
1510 }
1511 
1512 /* {{{ Memcached::get(string key [, mixed callback [, int get_flags = 0])
1513    Returns a value for the given key or false */
PHP_METHOD(Memcached,get)1514 PHP_METHOD(Memcached, get)
1515 {
1516 	php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1517 }
1518 /* }}} */
1519 
1520 /* {{{ Memcached::getByKey(string server_key, string key [, mixed callback [, int get_flags = 0])
1521    Returns a value for key from the server identified by the server key or false */
PHP_METHOD(Memcached,getByKey)1522 PHP_METHOD(Memcached, getByKey)
1523 {
1524 	php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1525 }
1526 /* }}} */
1527 
1528 static
s_get_multi_apply_fn(php_memc_object_t * intern,zend_string * key,zval * value,zval * cas,uint32_t flags,void * in_context)1529 zend_bool s_get_multi_apply_fn(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
1530 {
1531 	php_memc_get_ctx_t *context = (php_memc_get_ctx_t *) in_context;
1532 
1533 	Z_TRY_ADDREF_P(value);
1534 
1535 	if (context->extended) {
1536 		zval node;
1537 
1538 		Z_TRY_ADDREF_P(cas);
1539 
1540 		array_init(&node);
1541 		add_assoc_zval(&node, "value", value);
1542 		add_assoc_zval(&node, "cas",   cas);
1543 		add_assoc_long(&node, "flags", (zend_long) MEMC_VAL_GET_USER_FLAGS(flags));
1544 
1545 		zend_symtable_update(Z_ARRVAL_P(context->return_value), key, &node);
1546 	}
1547 	else {
1548 		zend_symtable_update(Z_ARRVAL_P(context->return_value), key, value);
1549 	}
1550 	return 1;
1551 }
1552 
1553 /* {{{ -- php_memc_getMulti_impl */
php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key)1554 static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
1555 {
1556 	php_memc_get_ctx_t context;
1557 	php_memc_keys_t keys_out;
1558 
1559 	zval *keys = NULL;
1560 	zend_string *server_key = NULL;
1561 	zend_long flags = 0;
1562 	MEMC_METHOD_INIT_VARS;
1563 	zend_bool retval, preserve_order;
1564 
1565 	if (by_key) {
1566 		/* "Sa|l" */
1567 		ZEND_PARSE_PARAMETERS_START(2, 3)
1568 		        Z_PARAM_STR(server_key)
1569 		        Z_PARAM_ARRAY(keys)
1570 		        Z_PARAM_OPTIONAL
1571 		        Z_PARAM_LONG(flags)
1572 		ZEND_PARSE_PARAMETERS_END();
1573 	} else {
1574 		/* "a|l" */
1575 		ZEND_PARSE_PARAMETERS_START(1, 2)
1576 		        Z_PARAM_ARRAY(keys)
1577 		        Z_PARAM_OPTIONAL
1578 		        Z_PARAM_LONG(flags)
1579 		ZEND_PARSE_PARAMETERS_END();
1580 	}
1581 
1582 	MEMC_METHOD_FETCH_OBJECT;
1583 
1584 	array_init(return_value);
1585 	if (zend_hash_num_elements(Z_ARRVAL_P(keys)) == 0) {
1586 		/* BC compatible */
1587 		s_memc_set_status(intern, MEMCACHED_NOTFOUND, 0);
1588 		return;
1589 	}
1590 
1591 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
1592 
1593 	preserve_order = (flags & MEMC_GET_PRESERVE_ORDER);
1594 	s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), preserve_order, return_value);
1595 
1596 	context.extended = (flags & MEMC_GET_EXTENDED);
1597 	context.return_value = return_value;
1598 
1599 	retval = php_memc_mget_apply(intern, server_key, &keys_out, s_get_multi_apply_fn, context.extended, &context);
1600 
1601 	s_clear_keys(&keys_out);
1602 
1603 	if (!retval && (s_memc_status_has_result_code(intern, MEMCACHED_NOTFOUND) || s_memc_status_has_result_code(intern, MEMCACHED_SOME_ERRORS))) {
1604 		return;
1605 	}
1606 
1607 	if (!retval || EG(exception)) {
1608 		zval_dtor(return_value);
1609 		RETURN_FROM_GET;
1610 	}
1611 }
1612 /* }}} */
1613 
1614 /* {{{ Memcached::getMulti(array keys[, long flags = 0 ])
1615    Returns values for the given keys or false */
PHP_METHOD(Memcached,getMulti)1616 PHP_METHOD(Memcached, getMulti)
1617 {
1618 	php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1619 }
1620 /* }}} */
1621 
1622 /* {{{ Memcached::getMultiByKey(string server_key, array keys[, long flags = 0 ])
1623    Returns values for the given keys from the server identified by the server key or false */
PHP_METHOD(Memcached,getMultiByKey)1624 PHP_METHOD(Memcached, getMultiByKey)
1625 {
1626 	php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1627 }
1628 /* }}} */
1629 
1630 /* {{{ Memcached::getDelayed(array keys [, bool with_cas [, mixed callback ] ])
1631    Sends a request for the given keys and returns immediately */
PHP_METHOD(Memcached,getDelayed)1632 PHP_METHOD(Memcached, getDelayed)
1633 {
1634 	php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1635 }
1636 /* }}} */
1637 
1638 /* {{{ Memcached::getDelayedByKey(string server_key, array keys [, bool with_cas [, mixed callback ] ])
1639    Sends a request for the given keys from the server identified by the server key and returns immediately */
PHP_METHOD(Memcached,getDelayedByKey)1640 PHP_METHOD(Memcached, getDelayedByKey)
1641 {
1642 	php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1643 }
1644 /* }}} */
1645 
1646 
1647 static
s_create_result_array(zend_string * key,zval * value,zval * cas,uint32_t flags,zval * return_value)1648 void s_create_result_array(zend_string *key, zval *value, zval *cas, uint32_t flags, zval *return_value)
1649 {
1650 	Z_TRY_ADDREF_P(value);
1651 	Z_TRY_ADDREF_P(cas);
1652 
1653 	add_assoc_str_ex(return_value, ZEND_STRL("key"), zend_string_copy(key));
1654 	add_assoc_zval_ex(return_value, ZEND_STRL("value"), value);
1655 
1656 	if (Z_LVAL_P(cas)) {
1657 		/* BC compatible */
1658 		add_assoc_zval_ex(return_value, ZEND_STRL("cas"), cas);
1659 		add_assoc_long_ex(return_value, ZEND_STRL("flags"), MEMC_VAL_GET_USER_FLAGS(flags));
1660 	}
1661 }
1662 
1663 static
s_result_callback_apply(php_memc_object_t * intern,zend_string * key,zval * value,zval * cas,uint32_t flags,void * in_context)1664 zend_bool s_result_callback_apply(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
1665 {
1666 	zend_bool status = 1;
1667 	zval params[2];
1668 	zval retval;
1669 	php_memc_result_callback_ctx_t *context = (php_memc_result_callback_ctx_t *) in_context;
1670 
1671 	ZVAL_COPY(&params[0], context->object);
1672 	array_init(&params[1]);
1673 
1674 	s_create_result_array(key, value, cas, flags, &params[1]);
1675 
1676 	context->fci.retval = &retval;
1677 	context->fci.params = params;
1678 	context->fci.param_count = 2;
1679 
1680 	if (zend_call_function(&context->fci, &context->fcc) == FAILURE) {
1681 		php_error_docref(NULL, E_WARNING, "could not invoke result callback");
1682 		status = 0;
1683 	}
1684 
1685 	zval_ptr_dtor(&retval);
1686 
1687 	zval_ptr_dtor(&params[0]);
1688 	zval_ptr_dtor(&params[1]);
1689 
1690 	return status;
1691 }
1692 
1693 /* {{{ -- php_memc_getDelayed_impl */
php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key)1694 static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
1695 {
1696 	php_memc_keys_t keys_out = {0};
1697 
1698 	zval *keys = NULL;
1699 	zend_string *server_key = NULL;
1700 	zend_bool with_cas = 0;
1701 
1702 	zend_fcall_info fci = empty_fcall_info;
1703 	zend_fcall_info_cache fcc = empty_fcall_info_cache;
1704 	memcached_return status = MEMCACHED_SUCCESS;
1705 	MEMC_METHOD_INIT_VARS;
1706 
1707 
1708 	if (by_key) {
1709 		/* "Sa/|bf!" */
1710 		ZEND_PARSE_PARAMETERS_START(2, 4)
1711 		        Z_PARAM_STR(server_key)
1712 		        Z_PARAM_ARRAY_EX(keys, 0, 1)
1713 		        Z_PARAM_OPTIONAL
1714 		        Z_PARAM_BOOL(with_cas)
1715 		        Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
1716 		ZEND_PARSE_PARAMETERS_END();
1717 	} else {
1718 		/* "a/|bf!" */
1719 		ZEND_PARSE_PARAMETERS_START(1, 3)
1720 		        Z_PARAM_ARRAY_EX(keys, 0, 1)
1721 		        Z_PARAM_OPTIONAL
1722 		        Z_PARAM_BOOL(with_cas)
1723 		        Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
1724 		ZEND_PARSE_PARAMETERS_END();
1725 	}
1726 
1727 	MEMC_METHOD_FETCH_OBJECT;
1728 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
1729 
1730 	s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), 0, NULL);
1731 
1732 	if (fci.size > 0) {
1733 		php_memc_result_callback_ctx_t context = {
1734 			getThis(), fci, fcc
1735 		};
1736 		status = php_memc_mget_apply(intern, server_key, &keys_out, &s_result_callback_apply, with_cas, (void *) &context);
1737 	}
1738 	else {
1739 		status = php_memc_mget_apply(intern, server_key, &keys_out, NULL, with_cas, NULL);
1740 	}
1741 
1742 	s_clear_keys(&keys_out);
1743 	RETURN_BOOL(status);
1744 }
1745 /* }}} */
1746 
1747 static
s_fetch_apply(php_memc_object_t * intern,zend_string * key,zval * value,zval * cas,uint32_t flags,void * in_context)1748 zend_bool s_fetch_apply(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
1749 {
1750 	zval *return_value = (zval *) in_context;
1751 	s_create_result_array(key, value, cas, flags, return_value);
1752 
1753 	return 0; // stop iterating after one
1754 }
1755 
1756 /* {{{ Memcached::fetch()
1757    Returns the next result from a previous delayed request */
PHP_METHOD(Memcached,fetch)1758 PHP_METHOD(Memcached, fetch)
1759 {
1760 	memcached_return status = MEMCACHED_SUCCESS;
1761 	MEMC_METHOD_INIT_VARS;
1762 
1763 	if (zend_parse_parameters_none() == FAILURE) {
1764 		return;
1765 	}
1766 
1767 	MEMC_METHOD_FETCH_OBJECT;
1768 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
1769 
1770 	array_init(return_value);
1771 	status = php_memc_result_apply(intern, s_fetch_apply, 1, return_value);
1772 
1773 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
1774 		zval_ptr_dtor(return_value);
1775 		RETURN_FROM_GET;
1776 	}
1777 }
1778 /* }}} */
1779 
1780 static
s_fetch_all_apply(php_memc_object_t * intern,zend_string * key,zval * value,zval * cas,uint32_t flags,void * in_context)1781 zend_bool s_fetch_all_apply(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
1782 {
1783 	zval zv;
1784 	zval *return_value = (zval *) in_context;
1785 
1786 	array_init (&zv);
1787 	s_create_result_array(key, value, cas, flags, &zv);
1788 
1789 	add_next_index_zval(return_value, &zv);
1790 	return 1;
1791 }
1792 
1793 /* {{{ Memcached::fetchAll()
1794    Returns all the results from a previous delayed request */
PHP_METHOD(Memcached,fetchAll)1795 PHP_METHOD(Memcached, fetchAll)
1796 {
1797 	memcached_return status = MEMCACHED_SUCCESS;
1798 	MEMC_METHOD_INIT_VARS;
1799 
1800 	if (zend_parse_parameters_none() == FAILURE) {
1801 		return;
1802 	}
1803 
1804 	MEMC_METHOD_FETCH_OBJECT;
1805 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
1806 
1807 	array_init(return_value);
1808 	status = php_memc_result_apply(intern, s_fetch_all_apply, 0, return_value);
1809 
1810 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
1811 		zval_dtor(return_value);
1812 		RETURN_FALSE;
1813 	}
1814 }
1815 /* }}} */
1816 
1817 /* {{{ Memcached::set(string key, mixed value [, int expiration ])
1818    Sets the value for the given key */
PHP_METHOD(Memcached,set)1819 PHP_METHOD(Memcached, set)
1820 {
1821 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 0);
1822 }
1823 /* }}} */
1824 
1825 /* {{{ Memcached::setByKey(string server_key, string key, mixed value [, int expiration ])
1826    Sets the value for the given key on the server identified by the server key */
PHP_METHOD(Memcached,setByKey)1827 PHP_METHOD(Memcached, setByKey)
1828 {
1829 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 1);
1830 }
1831 /* }}} */
1832 
1833 /* {{{ Memcached::touch(string key, [, int expiration ])
1834    Sets a new expiration for the given key */
PHP_METHOD(Memcached,touch)1835 PHP_METHOD(Memcached, touch)
1836 {
1837 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 0);
1838 }
1839 /* }}} */
1840 
1841 /* {{{ Memcached::touchbyKey(string key, [, int expiration ])
1842    Sets a new expiration for the given key */
PHP_METHOD(Memcached,touchByKey)1843 PHP_METHOD(Memcached, touchByKey)
1844 {
1845 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 1);
1846 }
1847 /* }}} */
1848 
1849 /* {{{ Memcached::setMulti(array items [, int expiration  ])
1850    Sets the keys/values specified in the items array */
PHP_METHOD(Memcached,setMulti)1851 PHP_METHOD(Memcached, setMulti)
1852 {
1853 	php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1854 }
1855 /* }}} */
1856 
1857 /* {{{ Memcached::setMultiByKey(string server_key, array items [, int expiration ])
1858    Sets the keys/values specified in the items array on the server identified by the given server key */
PHP_METHOD(Memcached,setMultiByKey)1859 PHP_METHOD(Memcached, setMultiByKey)
1860 {
1861 	php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1862 }
1863 /* }}} */
1864 
1865 /* {{{ -- php_memc_setMulti_impl */
php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key)1866 static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
1867 {
1868 	zval *entries;
1869 	zend_string *server_key = NULL;
1870 	zend_long expiration = 0;
1871 	zval *value;
1872 	zend_string *skey;
1873 	zend_ulong num_key;
1874 	int tmp_len = 0;
1875 	MEMC_METHOD_INIT_VARS;
1876 
1877 	if (by_key) {
1878 		/* "Sa|ll" */
1879 		ZEND_PARSE_PARAMETERS_START(2, 3)
1880 		        Z_PARAM_STR(server_key)
1881 		        Z_PARAM_ARRAY(entries)
1882 		        Z_PARAM_OPTIONAL
1883 		        Z_PARAM_LONG(expiration)
1884 		ZEND_PARSE_PARAMETERS_END();
1885 	} else {
1886 		/* "a|ll" */
1887 		ZEND_PARSE_PARAMETERS_START(1, 2)
1888 		        Z_PARAM_ARRAY(entries)
1889 		        Z_PARAM_OPTIONAL
1890 		        Z_PARAM_LONG(expiration)
1891 		ZEND_PARSE_PARAMETERS_END();
1892 	}
1893 
1894 	MEMC_METHOD_FETCH_OBJECT;
1895 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
1896 
1897 	ZEND_HASH_FOREACH_KEY_VAL (Z_ARRVAL_P(entries), num_key, skey, value) {
1898 		zend_string *str_key = NULL;
1899 
1900 		if (skey) {
1901 			str_key = skey;
1902 		}
1903 		else {
1904 			char tmp_key[64];
1905 
1906 			tmp_len = snprintf(tmp_key, sizeof(tmp_key) - 1, "%ld", (long)num_key);
1907 			str_key = zend_string_init(tmp_key, tmp_len, 0);
1908 		}
1909 
1910 		if (!s_memc_write_zval (intern, MEMC_OP_SET, server_key, str_key, value, expiration)) {
1911 			php_error_docref(NULL, E_WARNING, "failed to set key %s", ZSTR_VAL(str_key));
1912 		}
1913 
1914 		if (!skey) {
1915 			zend_string_release (str_key);
1916 		}
1917 
1918 	} ZEND_HASH_FOREACH_END();
1919 
1920 	RETURN_BOOL(!s_memc_status_has_error(intern));
1921 }
1922 /* }}} */
1923 
1924 /* {{{ Memcached::add(string key, mixed value [, int expiration ])
1925    Sets the value for the given key, failing if the key already exists */
PHP_METHOD(Memcached,add)1926 PHP_METHOD(Memcached, add)
1927 {
1928 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 0);
1929 }
1930 /* }}} */
1931 
1932 /* {{{ Memcached::addByKey(string server_key, string key, mixed value [, int expiration ])
1933    Sets the value for the given key on the server identified by the sever key, failing if the key already exists */
PHP_METHOD(Memcached,addByKey)1934 PHP_METHOD(Memcached, addByKey)
1935 {
1936 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 1);
1937 }
1938 /* }}} */
1939 
1940 /* {{{ Memcached::append(string key, mixed value)
1941    Appends the value to existing one for the key */
PHP_METHOD(Memcached,append)1942 PHP_METHOD(Memcached, append)
1943 {
1944 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 0);
1945 }
1946 /* }}} */
1947 
1948 /* {{{ Memcached::appendByKey(string server_key, string key, mixed value)
1949    Appends the value to existing one for the key on the server identified by the server key */
PHP_METHOD(Memcached,appendByKey)1950 PHP_METHOD(Memcached, appendByKey)
1951 {
1952 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 1);
1953 }
1954 /* }}} */
1955 
1956 /* {{{ Memcached::prepend(string key, mixed value)
1957    Prepends the value to existing one for the key */
PHP_METHOD(Memcached,prepend)1958 PHP_METHOD(Memcached, prepend)
1959 {
1960 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 0);
1961 }
1962 /* }}} */
1963 
1964 /* {{{ Memcached::prependByKey(string server_key, string key, mixed value)
1965    Prepends the value to existing one for the key on the server identified by the server key */
PHP_METHOD(Memcached,prependByKey)1966 PHP_METHOD(Memcached, prependByKey)
1967 {
1968 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 1);
1969 }
1970 /* }}} */
1971 
1972 /* {{{ Memcached::replace(string key, mixed value [, int expiration ])
1973    Replaces the value for the given key, failing if the key doesn't exist */
PHP_METHOD(Memcached,replace)1974 PHP_METHOD(Memcached, replace)
1975 {
1976 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 0);
1977 }
1978 /* }}} */
1979 
1980 /* {{{ Memcached::replaceByKey(string server_key, string key, mixed value [, int expiration ])
1981    Replaces the value for the given key on the server identified by the server key, failing if the key doesn't exist */
PHP_METHOD(Memcached,replaceByKey)1982 PHP_METHOD(Memcached, replaceByKey)
1983 {
1984 	php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 1);
1985 }
1986 /* }}} */
1987 
1988 /* {{{ -- php_memc_store_impl */
php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS,int op,zend_bool by_key)1989 static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key)
1990 {
1991 	zend_string *key;
1992 	zend_string *server_key = NULL;
1993 	zend_string *s_value;
1994 	zval s_zvalue;
1995 	zval *value = NULL;
1996 	zend_long expiration = 0;
1997 	MEMC_METHOD_INIT_VARS;
1998 
1999 	if (by_key) {
2000 		if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) {
2001 			/* "SSS" */
2002 			ZEND_PARSE_PARAMETERS_START(3, 3)
2003 			        Z_PARAM_STR(server_key)
2004 			        Z_PARAM_STR(key)
2005 			        Z_PARAM_STR(s_value)
2006 			ZEND_PARSE_PARAMETERS_END();
2007 			value = &s_zvalue;
2008 			ZVAL_STR(value, s_value);
2009 		} else if (op == MEMC_OP_TOUCH) {
2010 			/* "SS|l" */
2011 			ZEND_PARSE_PARAMETERS_START(2, 3)
2012 			        Z_PARAM_STR(server_key)
2013 			        Z_PARAM_STR(key)
2014 				Z_PARAM_OPTIONAL
2015 				Z_PARAM_LONG(expiration)
2016 			ZEND_PARSE_PARAMETERS_END();
2017 		} else {
2018 			/* "SSz|l" */
2019 			ZEND_PARSE_PARAMETERS_START(3, 4)
2020 			        Z_PARAM_STR(server_key)
2021 			        Z_PARAM_STR(key)
2022 			        Z_PARAM_ZVAL(value)
2023 				Z_PARAM_OPTIONAL
2024 				Z_PARAM_LONG(expiration)
2025 			ZEND_PARSE_PARAMETERS_END();
2026 		}
2027 	} else {
2028 		if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) {
2029 			/* "SS" */
2030 			ZEND_PARSE_PARAMETERS_START(2, 2)
2031 			        Z_PARAM_STR(key)
2032 			        Z_PARAM_STR(s_value)
2033 			ZEND_PARSE_PARAMETERS_END();
2034 			value = &s_zvalue;
2035 			ZVAL_STR(value, s_value);
2036 		} else if (op == MEMC_OP_TOUCH) {
2037 			/* "S|l */
2038 			ZEND_PARSE_PARAMETERS_START(1, 2)
2039 			        Z_PARAM_STR(key)
2040 			        Z_PARAM_OPTIONAL
2041 			        Z_PARAM_LONG(expiration)
2042 			ZEND_PARSE_PARAMETERS_END();
2043 		} else {
2044 			/* "Sz|l" */
2045 			ZEND_PARSE_PARAMETERS_START(2, 3)
2046 			        Z_PARAM_STR(key)
2047 			        Z_PARAM_ZVAL(value)
2048 			        Z_PARAM_OPTIONAL
2049 			        Z_PARAM_LONG(expiration)
2050 			ZEND_PARSE_PARAMETERS_END();
2051 		}
2052 	}
2053 
2054 	MEMC_METHOD_FETCH_OBJECT;
2055 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2056 	MEMC_CHECK_KEY(intern, key);
2057 
2058 	if (memc_user_data->compression_enabled) {
2059 		/*
2060 		 * When compression is enabled, we cannot do appends/prepends because that would
2061 		 * corrupt the compressed values. It is up to the user to fetch the value,
2062 		 * append/prepend new data, and store it again.
2063 		 */
2064 		if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) {
2065 			php_error_docref(NULL, E_WARNING, "cannot append/prepend with compression turned on");
2066 			RETURN_NULL();
2067 		}
2068 	}
2069 
2070 	if (!s_memc_write_zval (intern, op, server_key, key, value, expiration)) {
2071 		RETURN_FALSE;
2072 	}
2073 	RETURN_TRUE;
2074 }
2075 /* }}} */
2076 
2077 /* {{{ -- php_memc_cas_impl */
php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key)2078 static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
2079 {
2080 	zval *zv_cas;
2081 	uint64_t cas;
2082 	zend_string *key;
2083 	zend_string *server_key = NULL;
2084 	zval *value;
2085 	zend_long expiration = 0;
2086 	zend_string *payload;
2087 	uint32_t flags = 0;
2088 	memcached_return status;
2089 	MEMC_METHOD_INIT_VARS;
2090 
2091 	if (by_key) {
2092 		/* "zSSz|l" */
2093 		ZEND_PARSE_PARAMETERS_START(4, 5)
2094 		        Z_PARAM_ZVAL(zv_cas)
2095 		        Z_PARAM_STR(server_key)
2096 		        Z_PARAM_STR(key)
2097 		        Z_PARAM_ZVAL(value)
2098 		        Z_PARAM_OPTIONAL
2099 		        Z_PARAM_LONG(expiration)
2100 		ZEND_PARSE_PARAMETERS_END();
2101 	} else {
2102 		/* "zSz|l" */
2103 		ZEND_PARSE_PARAMETERS_START(3, 4)
2104 		        Z_PARAM_ZVAL(zv_cas)
2105 		        Z_PARAM_STR(key)
2106 		        Z_PARAM_ZVAL(value)
2107 		        Z_PARAM_OPTIONAL
2108 		        Z_PARAM_LONG(expiration)
2109 		ZEND_PARSE_PARAMETERS_END();
2110 	}
2111 
2112 	MEMC_METHOD_FETCH_OBJECT;
2113 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2114 	MEMC_CHECK_KEY(intern, key);
2115 
2116 	cas = s_zval_to_uint64(zv_cas);
2117 
2118 	payload = s_zval_to_payload(intern, value, &flags);
2119 	if (payload == NULL) {
2120 		intern->rescode = MEMC_RES_PAYLOAD_FAILURE;
2121 		RETURN_FALSE;
2122 	}
2123 
2124 	if (by_key) {
2125 		status = memcached_cas_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas);
2126 	} else {
2127 		status = memcached_cas(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas);
2128 	}
2129 
2130 	zend_string_release(payload);
2131 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2132 		RETURN_FALSE;
2133 	}
2134 
2135 	RETURN_TRUE;
2136 }
2137 /* }}} */
2138 
2139 /* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ])
2140    Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */
PHP_METHOD(Memcached,cas)2141 PHP_METHOD(Memcached, cas)
2142 {
2143 	php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2144 }
2145 /* }}} */
2146 
2147 /* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ])
2148    Sets the value for the given key on the server identified by the server_key, failing if the cas_token doesn't match the one in memcache */
PHP_METHOD(Memcached,casByKey)2149 PHP_METHOD(Memcached, casByKey)
2150 {
2151 	php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2152 }
2153 /* }}} */
2154 
2155 /* {{{ Memcached::delete(string key [, int time ])
2156    Deletes the given key */
PHP_METHOD(Memcached,delete)2157 PHP_METHOD(Memcached, delete)
2158 {
2159 	php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2160 }
2161 /* }}} */
2162 
2163 /* {{{ Memcached::deleteMulti(array keys [, int time ])
2164    Deletes the given keys */
PHP_METHOD(Memcached,deleteMulti)2165 PHP_METHOD(Memcached, deleteMulti)
2166 {
2167 	php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2168 }
2169 /* }}} */
2170 
2171 /* {{{ Memcached::deleteByKey(string server_key, string key [, int time ])
2172    Deletes the given key from the server identified by the server key */
PHP_METHOD(Memcached,deleteByKey)2173 PHP_METHOD(Memcached, deleteByKey)
2174 {
2175 	php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2176 }
2177 /* }}} */
2178 
2179 /* {{{ Memcached::deleteMultiByKey(array keys [, int time ])
2180    Deletes the given key from the server identified by the server key */
PHP_METHOD(Memcached,deleteMultiByKey)2181 PHP_METHOD(Memcached, deleteMultiByKey)
2182 {
2183 	php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2184 }
2185 /* }}} */
2186 
2187 /* {{{ -- php_memc_delete_impl */
php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key)2188 static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
2189 {
2190 	zend_string *key, *server_key;
2191 	zend_long expiration = 0;
2192 	memcached_return status;
2193 	MEMC_METHOD_INIT_VARS;
2194 
2195 	if (by_key) {
2196 		/* "SS|l" */
2197 		ZEND_PARSE_PARAMETERS_START(2, 3)
2198 		        Z_PARAM_STR(server_key)
2199 		        Z_PARAM_STR(key)
2200 		        Z_PARAM_OPTIONAL
2201 		        Z_PARAM_LONG(expiration)
2202 		ZEND_PARSE_PARAMETERS_END();
2203 	} else {
2204 		/* "S|l" */
2205 		ZEND_PARSE_PARAMETERS_START(1, 2)
2206 		        Z_PARAM_STR(key)
2207 		        Z_PARAM_OPTIONAL
2208 		        Z_PARAM_LONG(expiration)
2209 		ZEND_PARSE_PARAMETERS_END();
2210 		server_key = key;
2211 	}
2212 
2213 	MEMC_METHOD_FETCH_OBJECT;
2214 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2215 	MEMC_CHECK_KEY(intern, key);
2216 
2217 	if (by_key) {
2218 		status = memcached_delete_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key),
2219 									 ZSTR_LEN(key), expiration);
2220 	} else {
2221 		status = memcached_delete(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), expiration);
2222 	}
2223 
2224 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2225 		RETURN_FALSE;
2226 	}
2227 
2228 	RETURN_TRUE;
2229 }
2230 /* }}} */
2231 
2232 /* {{{ -- php_memc_deleteMulti_impl */
php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key)2233 static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
2234 {
2235 	zval *entries, *zv, ret;
2236 	zend_string *server_key = NULL;
2237 	zend_long expiration = 0;
2238 	zend_string *entry;
2239 
2240 	memcached_return status;
2241 	MEMC_METHOD_INIT_VARS;
2242 
2243 	if (by_key) {
2244 		/* "Sa/|l" */
2245 		ZEND_PARSE_PARAMETERS_START(2, 3)
2246 		        Z_PARAM_STR(server_key)
2247 		        Z_PARAM_ARRAY_EX(entries, 0, 1)
2248 		        Z_PARAM_OPTIONAL
2249 		        Z_PARAM_LONG(expiration)
2250 		ZEND_PARSE_PARAMETERS_END();
2251 	} else {
2252 		/* "a/|l" */
2253 		ZEND_PARSE_PARAMETERS_START(1, 2)
2254 		        Z_PARAM_ARRAY_EX(entries, 0, 1)
2255 		        Z_PARAM_OPTIONAL
2256 		        Z_PARAM_LONG(expiration)
2257 		ZEND_PARSE_PARAMETERS_END();
2258 	}
2259 
2260 	MEMC_METHOD_FETCH_OBJECT;
2261 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2262 
2263 	array_init(return_value);
2264 	ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P(entries), zv) {
2265 		entry = zval_get_string(zv);
2266 
2267 		if (ZSTR_LEN(entry) == 0) {
2268 			zend_string_release(entry);
2269 			continue;
2270 		}
2271 
2272 		if (by_key) {
2273 			status = memcached_delete_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(entry), ZSTR_LEN(entry), expiration);
2274 		} else {
2275 			status = memcached_delete_by_key(intern->memc, ZSTR_VAL(entry), ZSTR_LEN(entry), ZSTR_VAL(entry), ZSTR_LEN(entry), expiration);
2276 		}
2277 
2278 		if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2279 			ZVAL_LONG(&ret, status);
2280 		} else {
2281 			ZVAL_TRUE(&ret);
2282 		}
2283 		zend_symtable_update(Z_ARRVAL_P(return_value), entry, &ret);
2284 		zend_string_release(entry);
2285 	} ZEND_HASH_FOREACH_END();
2286 
2287 	return;
2288 }
2289 /* }}} */
2290 
2291 /* {{{ -- php_memc_incdec_impl */
php_memc_incdec_impl(INTERNAL_FUNCTION_PARAMETERS,zend_bool by_key,zend_bool incr)2292 static void php_memc_incdec_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key, zend_bool incr)
2293 {
2294 	zend_string *key, *server_key = NULL;
2295 	zend_long offset = 1;
2296 	zend_long expiry = 0;
2297 	zend_long initial = 0;
2298 	uint64_t value = UINT64_MAX;
2299 	memcached_return status;
2300 	int n_args = ZEND_NUM_ARGS();
2301 
2302 	MEMC_METHOD_INIT_VARS;
2303 
2304 	if (!by_key) {
2305 		/* "S|lll" */
2306 		ZEND_PARSE_PARAMETERS_START(1, 4)
2307 			Z_PARAM_STR(key)
2308 			Z_PARAM_OPTIONAL
2309 			Z_PARAM_LONG(offset)
2310 			Z_PARAM_LONG(initial)
2311 			Z_PARAM_LONG(expiry)
2312 		ZEND_PARSE_PARAMETERS_END();
2313 	} else {
2314 		/* "SS|lll" */
2315 		ZEND_PARSE_PARAMETERS_START(2, 5)
2316 			Z_PARAM_STR(server_key)
2317 			Z_PARAM_STR(key)
2318 			Z_PARAM_OPTIONAL
2319 			Z_PARAM_LONG(offset)
2320 			Z_PARAM_LONG(initial)
2321 			Z_PARAM_LONG(expiry)
2322 		ZEND_PARSE_PARAMETERS_END();
2323 	}
2324 
2325 	MEMC_METHOD_FETCH_OBJECT;
2326 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2327 	MEMC_CHECK_KEY(intern, key);
2328 
2329 	if (offset < 0) {
2330 		php_error_docref(NULL, E_WARNING, "offset cannot be a negative value");
2331 		RETURN_FALSE;
2332 	}
2333 
2334 	if ((!by_key && n_args < 3) || (by_key && n_args < 4)) {
2335 		if (by_key) {
2336 			if (incr) {
2337 				status = memcached_increment_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
2338 			} else {
2339 				status = memcached_decrement_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
2340 			}
2341 		} else {
2342 			/* The libmemcached API has a quirk that memcached_increment() takes only a 32-bit
2343 			 * offset, but memcached_increment_by_key() and all other increment and decrement
2344 			 * functions take a 64-bit offset. The memcached protocol allows increment/decrement
2345 			 * greater than UINT_MAX, so we just work around memcached_increment() here.
2346 			 */
2347 			if (incr) {
2348 				status = memcached_increment_by_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
2349 			} else {
2350 				status = memcached_decrement_by_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
2351 			}
2352 		}
2353 
2354 	} else {
2355 		zend_long retries = memc_user_data->store_retry_count;
2356 
2357 retry_inc_dec:
2358 		if (!memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) {
2359 			php_error_docref(NULL, E_WARNING, "Initial value is only supported with binary protocol");
2360 			RETURN_FALSE;
2361 		}
2362 		if (by_key) {
2363 			if (incr) {
2364 				status = memcached_increment_with_initial_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), offset, initial, (time_t)expiry, &value);
2365 			} else {
2366 				status = memcached_decrement_with_initial_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), offset, initial, (time_t)expiry, &value);
2367 			}
2368 		} else {
2369 			if (incr) {
2370 				status = memcached_increment_with_initial(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), offset, initial, (time_t)expiry, &value);
2371 			} else {
2372 				status = memcached_decrement_with_initial(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), offset, initial, (time_t)expiry, &value);
2373 			}
2374 		}
2375 		if (s_should_retry_write(intern, status) && retries-- > 0) {
2376 			goto retry_inc_dec;
2377 		}
2378 	}
2379 
2380 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2381 		RETURN_FALSE;
2382 	}
2383 
2384 	if (value == UINT64_MAX) {
2385 		RETURN_FALSE;
2386 	}
2387 
2388 	RETURN_LONG((long)value);
2389 }
2390 /* }}} */
2391 
2392 /* {{{ Memcached::increment(string key [, int delta [, initial_value [, expiry time ] ] ])
2393    Increments the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached,increment)2394 PHP_METHOD(Memcached, increment)
2395 {
2396 	php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
2397 }
2398 /* }}} */
2399 
2400 /* {{{ Memcached::decrement(string key [, int delta [, initial_value [, expiry time ] ] ])
2401    Decrements the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached,decrement)2402 PHP_METHOD(Memcached, decrement)
2403 {
2404 	php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
2405 }
2406 /* }}} */
2407 
2408 /* {{{ Memcached::decrementByKey(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ])
2409    Decrements by server the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached,decrementByKey)2410 PHP_METHOD(Memcached, decrementByKey)
2411 {
2412 	php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
2413 }
2414 /* }}} */
2415 
2416 /* {{{ Memcached::increment(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ])
2417    Increments by server the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached,incrementByKey)2418 PHP_METHOD(Memcached, incrementByKey)
2419 {
2420 	php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
2421 }
2422 /* }}} */
2423 
2424 /* {{{ Memcached::addServer(string hostname, int port [, int weight ])
2425    Adds the given memcache server to the list */
PHP_METHOD(Memcached,addServer)2426 PHP_METHOD(Memcached, addServer)
2427 {
2428 	zend_string *host;
2429 	zend_long port, weight = 0;
2430 	memcached_return status;
2431 	MEMC_METHOD_INIT_VARS;
2432 
2433 	/* "Sa/|l" */
2434 	ZEND_PARSE_PARAMETERS_START(2, 3)
2435 	        Z_PARAM_STR(host)
2436 	        Z_PARAM_LONG(port)
2437 	        Z_PARAM_OPTIONAL
2438 	        Z_PARAM_LONG(weight)
2439 	ZEND_PARSE_PARAMETERS_END();
2440 
2441 	MEMC_METHOD_FETCH_OBJECT;
2442 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2443 
2444 	status = memcached_server_add_with_weight(intern->memc, ZSTR_VAL(host), port, weight);
2445 
2446 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2447 		RETURN_FALSE;
2448 	}
2449 
2450 	RETURN_TRUE;
2451 }
2452 /* }}} */
2453 
2454 /* {{{ Memcached::addServers(array servers)
2455    Adds the given memcache servers to the server list */
PHP_METHOD(Memcached,addServers)2456 PHP_METHOD(Memcached, addServers)
2457 {
2458 	zval *servers;
2459 	zval *entry;
2460 	zval *z_host, *z_port, *z_weight = NULL;
2461 	HashPosition	pos;
2462 	int   entry_size, i = 0;
2463 	memcached_server_st *list = NULL;
2464 	memcached_return status;
2465 	MEMC_METHOD_INIT_VARS;
2466 
2467 	/* "a/" */
2468 	ZEND_PARSE_PARAMETERS_START(1, 1)
2469 	        Z_PARAM_ARRAY_EX(servers, 0, 1)
2470 	ZEND_PARSE_PARAMETERS_END();
2471 
2472 	MEMC_METHOD_FETCH_OBJECT;
2473 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2474 
2475 	ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P(servers), entry) {
2476 		if (Z_TYPE_P(entry) != IS_ARRAY) {
2477 			php_error_docref(NULL, E_WARNING, "server list entry #%d is not an array", i+1);
2478 			i++;
2479 			continue;
2480 		}
2481 
2482 		entry_size = zend_hash_num_elements(Z_ARRVAL_P(entry));
2483 
2484 		if (entry_size > 1) {
2485 			zend_string *host;
2486 			zend_long port;
2487 			uint32_t weight;
2488 
2489 			zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(entry), &pos);
2490 
2491 			/* Check that we have a host */
2492 			if ((z_host = zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), &pos)) == NULL) {
2493 				php_error_docref(NULL, E_WARNING, "could not get server host for entry #%d", i+1);
2494 				i++;
2495 				continue;
2496 			}
2497 
2498 			/* Check that we have a port */
2499 			if (zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos) == FAILURE ||
2500 			    (z_port = zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), &pos)) == NULL) {
2501 				php_error_docref(NULL, E_WARNING, "could not get server port for entry #%d", i+1);
2502 				i++;
2503 				continue;
2504 			}
2505 
2506 			host = zval_get_string(z_host);
2507 			port = zval_get_long(z_port);
2508 
2509 			weight = 0;
2510 			if (entry_size > 2) {
2511 				/* Try to get weight */
2512 				if (zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos) == FAILURE ||
2513 					(z_weight = zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), &pos)) == NULL) {
2514 					php_error_docref(NULL, E_WARNING, "could not get server weight for entry #%d", i+1);
2515 				}
2516 
2517 				weight = zval_get_long(z_weight);
2518 			}
2519 
2520 			list = memcached_server_list_append_with_weight(list, ZSTR_VAL(host), port, weight, &status);
2521 
2522 			zend_string_release(host);
2523 
2524 			if (s_memc_status_handle_result_code(intern, status) == SUCCESS) {
2525 				i++;
2526 				continue;
2527 			}
2528 		}
2529 		i++;
2530 		/* catch-all for all errors */
2531 		php_error_docref(NULL, E_WARNING, "could not add entry #%d to the server list", i + 1);
2532 	} ZEND_HASH_FOREACH_END();
2533 
2534 	status = memcached_server_push(intern->memc, list);
2535 	memcached_server_list_free(list);
2536 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2537 		RETURN_FALSE;
2538 	}
2539 
2540 	RETURN_TRUE;
2541 }
2542 /* }}} */
2543 
2544 /* {{{ Memcached::getServerList()
2545    Returns the list of the memcache servers in use */
PHP_METHOD(Memcached,getServerList)2546 PHP_METHOD(Memcached, getServerList)
2547 {
2548 	memcached_server_function callbacks[1];
2549 	MEMC_METHOD_INIT_VARS;
2550 
2551 	if (zend_parse_parameters_none() == FAILURE) {
2552 		return;
2553 	}
2554 
2555 	MEMC_METHOD_FETCH_OBJECT;
2556 
2557 	callbacks[0] = s_server_cursor_list_servers_cb;
2558 	array_init(return_value);
2559 	memcached_server_cursor(intern->memc, callbacks, return_value, 1);
2560 }
2561 /* }}} */
2562 
2563 /* {{{ Memcached::getServerByKey(string server_key)
2564    Returns the server identified by the given server key */
PHP_METHOD(Memcached,getServerByKey)2565 PHP_METHOD(Memcached, getServerByKey)
2566 {
2567 	zend_string *server_key;
2568 	php_memcached_instance_st server_instance;
2569 	memcached_return error;
2570 	MEMC_METHOD_INIT_VARS;
2571 
2572 	/* "S" */
2573 	ZEND_PARSE_PARAMETERS_START(1, 1)
2574 	        Z_PARAM_STR(server_key)
2575 	ZEND_PARSE_PARAMETERS_END();
2576 
2577 	MEMC_METHOD_FETCH_OBJECT;
2578 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2579 
2580 	server_instance = memcached_server_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), &error);
2581 	if (server_instance == NULL) {
2582 		s_memc_status_handle_result_code(intern, error);
2583 		RETURN_FALSE;
2584 	}
2585 
2586 	array_init(return_value);
2587 	add_assoc_string(return_value, "host", (char*) memcached_server_name(server_instance));
2588 	add_assoc_long(return_value, "port", memcached_server_port(server_instance));
2589 	add_assoc_long(return_value, "weight", 0);
2590 }
2591 /* }}} */
2592 
2593 /* {{{ Memcached::resetServerList()
2594    Reset the server list in use */
PHP_METHOD(Memcached,resetServerList)2595 PHP_METHOD(Memcached, resetServerList)
2596 {
2597 	MEMC_METHOD_INIT_VARS;
2598 
2599 	if (zend_parse_parameters_none() == FAILURE) {
2600 		return;
2601 	}
2602 
2603 	MEMC_METHOD_FETCH_OBJECT;
2604 
2605 	memcached_servers_reset(intern->memc);
2606 	RETURN_TRUE;
2607 }
2608 /* }}} */
2609 
2610 /* {{{ Memcached::quit()
2611    Close any open connections */
PHP_METHOD(Memcached,quit)2612 PHP_METHOD(Memcached, quit)
2613 {
2614 	MEMC_METHOD_INIT_VARS;
2615 
2616 	if (zend_parse_parameters_none() == FAILURE) {
2617 		return;
2618 	}
2619 
2620 	MEMC_METHOD_FETCH_OBJECT;
2621 
2622 	memcached_quit(intern->memc);
2623 	RETURN_TRUE;
2624 }
2625 /* }}} */
2626 
2627 /* {{{ Memcached::flushBuffers()
2628    Flush and senf buffered commands */
PHP_METHOD(Memcached,flushBuffers)2629 PHP_METHOD(Memcached, flushBuffers)
2630 {
2631 	MEMC_METHOD_INIT_VARS;
2632 
2633 	if (zend_parse_parameters_none() == FAILURE) {
2634 		return;
2635 	}
2636 
2637 	MEMC_METHOD_FETCH_OBJECT;
2638 	RETURN_BOOL(memcached_flush_buffers(intern->memc) == MEMCACHED_SUCCESS);
2639 }
2640 /* }}} */
2641 
2642 /* {{{ Memcached::getLastErrorMessage()
2643    Returns the last error message that occurred */
PHP_METHOD(Memcached,getLastErrorMessage)2644 PHP_METHOD(Memcached, getLastErrorMessage)
2645 {
2646 	MEMC_METHOD_INIT_VARS;
2647 
2648 	if (zend_parse_parameters_none() == FAILURE) {
2649 		return;
2650 	}
2651 
2652 	MEMC_METHOD_FETCH_OBJECT;
2653 
2654 	RETURN_STRING(memcached_last_error_message(intern->memc));
2655 }
2656 /* }}} */
2657 
2658 /* {{{ Memcached::getLastErrorCode()
2659    Returns the last error code that occurred */
PHP_METHOD(Memcached,getLastErrorCode)2660 PHP_METHOD(Memcached, getLastErrorCode)
2661 {
2662 	MEMC_METHOD_INIT_VARS;
2663 
2664 	if (zend_parse_parameters_none() == FAILURE) {
2665 		return;
2666 	}
2667 
2668 	MEMC_METHOD_FETCH_OBJECT;
2669 
2670 	RETURN_LONG(memcached_last_error(intern->memc));
2671 }
2672 /* }}} */
2673 
2674 /* {{{ Memcached::getLastErrorErrno()
2675    Returns the last error errno that occurred */
PHP_METHOD(Memcached,getLastErrorErrno)2676 PHP_METHOD(Memcached, getLastErrorErrno)
2677 {
2678 	MEMC_METHOD_INIT_VARS;
2679 
2680 	if (zend_parse_parameters_none() == FAILURE) {
2681 		return;
2682 	}
2683 
2684 	MEMC_METHOD_FETCH_OBJECT;
2685 
2686 	RETURN_LONG(memcached_last_error_errno(intern->memc));
2687 }
2688 /* }}} */
2689 
2690 /* {{{ Memcached::getLastDisconnectedServer()
2691    Returns the last disconnected server
2692    Was added in 0.34 according to libmemcached's Changelog */
PHP_METHOD(Memcached,getLastDisconnectedServer)2693 PHP_METHOD(Memcached, getLastDisconnectedServer)
2694 {
2695 	php_memcached_instance_st server_instance;
2696 	MEMC_METHOD_INIT_VARS;
2697 
2698 	if (zend_parse_parameters_none() == FAILURE) {
2699 		return;
2700 	}
2701 
2702 	MEMC_METHOD_FETCH_OBJECT;
2703 
2704 	server_instance = memcached_server_get_last_disconnect(intern->memc);
2705 	if (server_instance == NULL) {
2706 		RETURN_FALSE;
2707 	}
2708 
2709 	array_init(return_value);
2710 	add_assoc_string(return_value, "host", (char*) memcached_server_name(server_instance));
2711 	add_assoc_long(return_value, "port", memcached_server_port(server_instance));
2712 }
2713 /* }}} */
2714 
2715 
2716 
2717 static
s_long_value(const char * str,zend_long * value)2718 zend_bool s_long_value(const char *str, zend_long *value)
2719 {
2720 	char *end = (char *) str;
2721 
2722 	errno = 0;
2723 	*value = strtol(str, &end, 10);
2724 
2725 	if (errno || str == end || *end != '\0') {
2726 		return 0;
2727 	}
2728 	return 1;
2729 }
2730 
2731 static
s_double_value(const char * str,double * value)2732 zend_bool s_double_value(const char *str, double *value)
2733 {
2734 	char *end = (char *) str;
2735 
2736 	errno = 0;
2737 	*value = strtod(str, &end);
2738 
2739 	if (errno || str == end || *end != '\0') {
2740 		return 0;
2741 	}
2742 	return 1;
2743 }
2744 
2745 static
s_stat_execute_cb(php_memcached_instance_st instance,const char * key,size_t key_length,const char * value,size_t value_length,void * context)2746 memcached_return s_stat_execute_cb (php_memcached_instance_st instance, const char *key, size_t key_length, const char *value, size_t value_length, void *context)
2747 {
2748 	zend_string *server_key;
2749 	zend_long long_val;
2750 	double d_val;
2751 	char *buffer;
2752 
2753 	zval *return_value = (zval *) context;
2754 	zval *server_values;
2755 
2756 	server_key = strpprintf(0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance));
2757 	server_values = zend_hash_find(Z_ARRVAL_P(return_value), server_key);
2758 
2759 	if (!server_values) {
2760 		zval zv;
2761 		array_init(&zv);
2762 
2763 		server_values = zend_hash_add(Z_ARRVAL_P(return_value), server_key, &zv);
2764 	}
2765 
2766 	spprintf (&buffer, 0, "%.*s", (int)value_length, value);
2767 
2768 	/* Check type */
2769 	if (s_long_value (buffer, &long_val)) {
2770 		add_assoc_long(server_values, key, long_val);
2771 	}
2772 	else if (s_double_value (buffer, &d_val)) {
2773 		add_assoc_double(server_values, key, d_val);
2774 	}
2775 	else {
2776 		add_assoc_stringl_ex(server_values, key, key_length, (char*)value, value_length);
2777 	}
2778 	efree (buffer);
2779 	zend_string_release(server_key);
2780 
2781 	return MEMCACHED_SUCCESS;
2782 }
2783 
2784 /* {{{ Memcached::getStats()
2785    Returns statistics for the memcache servers */
PHP_METHOD(Memcached,getStats)2786 PHP_METHOD(Memcached, getStats)
2787 {
2788 	memcached_return status;
2789 	char *args = NULL;
2790 	zend_string *args_string = NULL;
2791 	uint64_t orig_no_block, orig_protocol;
2792 	MEMC_METHOD_INIT_VARS;
2793 
2794 	/* "|S!" */
2795 	ZEND_PARSE_PARAMETERS_START(0, 1)
2796 	        Z_PARAM_OPTIONAL
2797 	        Z_PARAM_STR_EX(args_string, 1, 0)
2798 	ZEND_PARSE_PARAMETERS_END();
2799 
2800 	MEMC_METHOD_FETCH_OBJECT;
2801 
2802 	if (args_string)
2803 		args = ZSTR_VAL(args_string);
2804 
2805 	/* stats hangs in nonblocking mode, turn off during the call. Only change the
2806 	 * value if needed, because libmemcached reconnects for this behavior_set. */
2807 	orig_no_block = memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK);
2808 	orig_protocol = memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL);
2809 	if (orig_no_block && orig_protocol)
2810 		memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 0);
2811 
2812 	array_init(return_value);
2813 	status = memcached_stat_execute(intern->memc, args, s_stat_execute_cb, return_value);
2814 
2815 	if (orig_no_block && orig_protocol)
2816 		memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, orig_no_block);
2817 
2818 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2819 		zval_ptr_dtor(return_value);
2820 		RETURN_FALSE;
2821 	}
2822 }
2823 /* }}} */
2824 
2825 /* {{{ Memcached::getVersion()
2826    Returns the version of each memcached server in the pool */
PHP_METHOD(Memcached,getVersion)2827 PHP_METHOD(Memcached, getVersion)
2828 {
2829 	memcached_return status;
2830 	memcached_server_function callbacks[1];
2831 	MEMC_METHOD_INIT_VARS;
2832 
2833 	if (zend_parse_parameters_none() == FAILURE) {
2834 		return;
2835 	}
2836 
2837 	MEMC_METHOD_FETCH_OBJECT;
2838 
2839 	status = memcached_version(intern->memc);
2840 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2841 		RETURN_FALSE;
2842 	}
2843 
2844 	callbacks[0] = s_server_cursor_version_cb;
2845 
2846 	array_init(return_value);
2847 	status = memcached_server_cursor(intern->memc, callbacks, return_value, 1);
2848 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2849 		zval_dtor(return_value);
2850 		RETURN_FALSE;
2851 	}
2852 }
2853 /* }}} */
2854 
2855 /* {{{ Memcached::getAllKeys()
2856 	Returns the keys stored on all the servers */
2857 static
s_dump_keys_cb(const memcached_st * ptr,const char * key,size_t key_length,void * in_context)2858 memcached_return s_dump_keys_cb(const memcached_st *ptr, const char *key, size_t key_length, void *in_context)
2859 {
2860 	zval *return_value = (zval*) in_context;
2861 	add_next_index_stringl(return_value, key, key_length);
2862 
2863 	return MEMCACHED_SUCCESS;
2864 }
2865 
PHP_METHOD(Memcached,getAllKeys)2866 PHP_METHOD(Memcached, getAllKeys)
2867 {
2868 	memcached_return rc;
2869 	memcached_dump_func callback[1];
2870 	MEMC_METHOD_INIT_VARS;
2871 
2872 	if (zend_parse_parameters_none() == FAILURE) {
2873 		return;
2874 	}
2875 
2876 	callback[0] = s_dump_keys_cb;
2877 	MEMC_METHOD_FETCH_OBJECT;
2878 
2879 	array_init(return_value);
2880 
2881 	rc = memcached_dump(intern->memc, callback, return_value, 1);
2882 
2883 	/* Ignore two errors. libmemcached has a hardcoded loop of 200 slab
2884 	 * classes that matches memcached < 1.4.24, at which version the server
2885 	 * has only 63 slabs and throws an error when requesting the 64th slab.
2886 	 *
2887 	 * In multi-server some non-deterministic number of elements will be dropped.
2888 	 */
2889 	if (rc != MEMCACHED_CLIENT_ERROR && rc != MEMCACHED_SERVER_ERROR
2890 	    && s_memc_status_handle_result_code(intern, rc) == FAILURE) {
2891 		zval_dtor(return_value);
2892 		RETURN_FALSE;
2893 	}
2894 }
2895 /* }}} */
2896 
2897 /* {{{ Memcached::flush([ int delay ])
2898    Flushes the data on all the servers */
PHP_METHOD(Memcached,flush)2899 static PHP_METHOD(Memcached, flush)
2900 {
2901 	zend_long delay = 0;
2902 	memcached_return status;
2903 	MEMC_METHOD_INIT_VARS;
2904 
2905 	/* "|l" */
2906 	ZEND_PARSE_PARAMETERS_START(0, 1)
2907 	        Z_PARAM_OPTIONAL
2908 	        Z_PARAM_LONG(delay)
2909 	ZEND_PARSE_PARAMETERS_END();
2910 
2911 	MEMC_METHOD_FETCH_OBJECT;
2912 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
2913 
2914 	status = memcached_flush(intern->memc, delay);
2915 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
2916 		RETURN_FALSE;
2917 	}
2918 
2919 	RETURN_TRUE;
2920 }
2921 /* }}} */
2922 
2923 /* {{{ Memcached::getOption(int option)
2924    Returns the value for the given option constant */
PHP_METHOD(Memcached,getOption)2925 static PHP_METHOD(Memcached, getOption)
2926 {
2927 	zend_long option;
2928 	uint64_t result;
2929 	memcached_behavior flag;
2930 	MEMC_METHOD_INIT_VARS;
2931 
2932 	/* "l" */
2933 	ZEND_PARSE_PARAMETERS_START(1, 1)
2934 	        Z_PARAM_LONG(option)
2935 	ZEND_PARSE_PARAMETERS_END();
2936 
2937 	MEMC_METHOD_FETCH_OBJECT;
2938 
2939 	switch (option) {
2940 		case MEMC_OPT_COMPRESSION_TYPE:
2941 			RETURN_LONG(memc_user_data->compression_type);
2942 
2943 		case MEMC_OPT_COMPRESSION:
2944 			RETURN_BOOL(memc_user_data->compression_enabled);
2945 
2946 		case MEMC_OPT_PREFIX_KEY:
2947 		{
2948 			memcached_return retval;
2949 			char *result;
2950 
2951 			result = memcached_callback_get(intern->memc, MEMCACHED_CALLBACK_PREFIX_KEY, &retval);
2952 			if (retval == MEMCACHED_SUCCESS && result) {
2953 				RETURN_STRING(result);
2954 			} else {
2955 				RETURN_EMPTY_STRING();
2956 			}
2957 		}
2958 
2959 		case MEMC_OPT_SERIALIZER:
2960 			RETURN_LONG((long)memc_user_data->serializer);
2961 			break;
2962 
2963 		case MEMC_OPT_USER_FLAGS:
2964 			RETURN_LONG(memc_user_data->set_udf_flags);
2965 			break;
2966 
2967 		case MEMC_OPT_STORE_RETRY_COUNT:
2968 			RETURN_LONG((long)memc_user_data->store_retry_count);
2969 			break;
2970 
2971 		case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE:
2972 		case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE:
2973 			if (memcached_server_count(intern->memc) == 0) {
2974 				php_error_docref(NULL, E_WARNING, "no servers defined");
2975 				return;
2976 			}
2977 
2978 		default:
2979 			/*
2980 			 * Assume that it's a libmemcached behavior option.
2981 			 */
2982 			flag = (memcached_behavior) option;
2983 			result = memcached_behavior_get(intern->memc, flag);
2984 			RETURN_LONG((long)result);
2985 	}
2986 }
2987 /* }}} */
2988 
2989 static
php_memc_set_option(php_memc_object_t * intern,long option,zval * value)2990 int php_memc_set_option(php_memc_object_t *intern, long option, zval *value)
2991 {
2992 	zend_long lval;
2993 	memcached_return rc = MEMCACHED_FAILURE;
2994 	memcached_behavior flag;
2995 	php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
2996 
2997 	switch (option) {
2998 		case MEMC_OPT_COMPRESSION:
2999 			memc_user_data->compression_enabled = zval_get_long(value) ? 1 : 0;
3000 			break;
3001 
3002 		case MEMC_OPT_COMPRESSION_TYPE:
3003 			lval = zval_get_long(value);
3004 			if (lval == COMPRESSION_TYPE_FASTLZ ||
3005 				lval == COMPRESSION_TYPE_ZLIB) {
3006 				memc_user_data->compression_type = lval;
3007 			} else {
3008 				/* invalid compression type */
3009 				intern->rescode = MEMCACHED_INVALID_ARGUMENTS;
3010 				return 0;
3011 			}
3012 			break;
3013 
3014 		case MEMC_OPT_PREFIX_KEY:
3015 		{
3016 			zend_string *str;
3017 			char *key;
3018 			str = zval_get_string(value);
3019 			if (ZSTR_LEN(str) == 0) {
3020 				key = NULL;
3021 			} else {
3022 				key = ZSTR_VAL(str);
3023 			}
3024 			if (memcached_callback_set(intern->memc, MEMCACHED_CALLBACK_PREFIX_KEY, key) == MEMCACHED_BAD_KEY_PROVIDED) {
3025 				zend_string_release(str);
3026 				intern->rescode = MEMCACHED_INVALID_ARGUMENTS;
3027 				php_error_docref(NULL, E_WARNING, "bad key provided");
3028 				return 0;
3029 			}
3030 			zend_string_release(str);
3031 		}
3032 			break;
3033 
3034 		case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED:
3035 			flag = (memcached_behavior) option;
3036 
3037 			lval = zval_get_long(value);
3038 			rc = memcached_behavior_set(intern->memc, flag, (uint64_t)lval);
3039 
3040 			if (s_memc_status_handle_result_code(intern, rc) == FAILURE) {
3041 				php_error_docref(NULL, E_WARNING, "error setting memcached option: %s", memcached_strerror (intern->memc, rc));
3042 				return 0;
3043 			}
3044 
3045 			/*
3046 			 * This is necessary because libmemcached does not reset hash/distribution
3047 			 * options on false case, like it does for MEMCACHED_BEHAVIOR_KETAMA
3048 			 * (non-weighted) case. We have to clean up ourselves.
3049 			 */
3050 			if (!lval) {
3051 				(void)memcached_behavior_set_key_hash(intern->memc, MEMCACHED_HASH_DEFAULT);
3052 				(void)memcached_behavior_set_distribution_hash(intern->memc, MEMCACHED_HASH_DEFAULT);
3053 				(void)memcached_behavior_set_distribution(intern->memc, MEMCACHED_DISTRIBUTION_MODULA);
3054 			}
3055 			break;
3056 
3057 		case MEMC_OPT_SERIALIZER:
3058 		{
3059 			lval = zval_get_long(value);
3060 			/* igbinary serializer */
3061 #ifdef HAVE_MEMCACHED_IGBINARY
3062 			if (lval == SERIALIZER_IGBINARY) {
3063 				memc_user_data->serializer = SERIALIZER_IGBINARY;
3064 			} else
3065 #endif
3066 #ifdef HAVE_JSON_API
3067 			if (lval == SERIALIZER_JSON) {
3068 				memc_user_data->serializer = SERIALIZER_JSON;
3069 			} else if (lval == SERIALIZER_JSON_ARRAY) {
3070 				memc_user_data->serializer = SERIALIZER_JSON_ARRAY;
3071 			} else
3072 #endif
3073 			/* msgpack serializer */
3074 #ifdef HAVE_MEMCACHED_MSGPACK
3075 			if (lval == SERIALIZER_MSGPACK) {
3076 				memc_user_data->serializer = SERIALIZER_MSGPACK;
3077 			} else
3078 #endif
3079 			/* php serializer */
3080 			if (lval == SERIALIZER_PHP) {
3081 				memc_user_data->serializer = SERIALIZER_PHP;
3082 			} else {
3083 				memc_user_data->serializer = SERIALIZER_PHP;
3084 				intern->rescode = MEMCACHED_INVALID_ARGUMENTS;
3085 				php_error_docref(NULL, E_WARNING, "invalid serializer provided");
3086 				return 0;
3087 			}
3088 			break;
3089 		}
3090 
3091 		case MEMC_OPT_USER_FLAGS:
3092 			lval = zval_get_long(value);
3093 
3094 			if (lval < 0) {
3095 				memc_user_data->set_udf_flags = -1;
3096 				return 1;
3097 			}
3098 
3099 			if (lval > MEMC_VAL_USER_FLAGS_MAX) {
3100 				php_error_docref(NULL, E_WARNING, "MEMC_OPT_USER_FLAGS must be < %u", MEMC_VAL_USER_FLAGS_MAX);
3101 				return 0;
3102 			}
3103 			memc_user_data->set_udf_flags = lval;
3104 			break;
3105 
3106 		case MEMC_OPT_STORE_RETRY_COUNT:
3107 			lval = zval_get_long(value);
3108 			memc_user_data->store_retry_count = lval;
3109 			break;
3110 
3111 		default:
3112 			/*
3113 			 * Assume that it's a libmemcached behavior option.
3114 			 */
3115 			if (option < 0) {
3116 				rc = MEMCACHED_INVALID_ARGUMENTS;
3117 			}
3118 			else {
3119 				flag = (memcached_behavior) option;
3120 				lval = zval_get_long(value);
3121 
3122 				if (flag < MEMCACHED_BEHAVIOR_MAX) {
3123 					// don't reset the option when the option value wasn't modified,
3124 					// while the libmemcached may shutdown all connections.
3125 					if (memcached_behavior_get(intern->memc, flag) == (uint64_t)lval) {
3126 						return 1;
3127 					}
3128 					rc = memcached_behavior_set(intern->memc, flag, (uint64_t)lval);
3129 				}
3130 				else {
3131 					rc = MEMCACHED_INVALID_ARGUMENTS;
3132 				}
3133 			}
3134 
3135 			if (s_memc_status_handle_result_code(intern, rc) == FAILURE) {
3136 				php_error_docref(NULL, E_WARNING, "error setting memcached option: %s", memcached_strerror (intern->memc, rc));
3137 				return 0;
3138 			}
3139 			break;
3140 	}
3141 	return 1;
3142 }
3143 
3144 static
s_zval_to_uint32_array(zval * input,size_t * num_elements)3145 uint32_t *s_zval_to_uint32_array (zval *input, size_t *num_elements)
3146 {
3147 	zval *pzval;
3148 	uint32_t *retval;
3149 	size_t i = 0;
3150 
3151 	*num_elements = zend_hash_num_elements(Z_ARRVAL_P(input));
3152 
3153 	if (!*num_elements) {
3154 		return NULL;
3155 	}
3156 
3157 	retval = ecalloc(*num_elements, sizeof(uint32_t));
3158 
3159 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), pzval) {
3160 		zend_long value = 0;
3161 
3162 		value = zval_get_long(pzval);
3163 		if (value < 0) {
3164 			php_error_docref(NULL, E_WARNING, "the map must contain positive integers");
3165 			efree (retval);
3166 			*num_elements = 0;
3167 			return NULL;
3168 		}
3169 		retval [i] = (uint32_t) value;
3170 		i++;
3171 	} ZEND_HASH_FOREACH_END();
3172 	return retval;
3173 }
3174 
3175 /* {{{ Memcached::setBucket(array host_map, array forward_map, integer replicas)
3176    Sets the memcached virtual buckets */
3177 
PHP_METHOD(Memcached,setBucket)3178 PHP_METHOD(Memcached, setBucket)
3179 {
3180 	zval *zserver_map;
3181 	zval *zforward_map = NULL;
3182 	zend_long replicas = 0;
3183 	zend_bool retval = 1;
3184 
3185 	uint32_t *server_map = NULL, *forward_map = NULL;
3186 	size_t server_map_len = 0, forward_map_len = 0;
3187 	memcached_return rc;
3188 	MEMC_METHOD_INIT_VARS;
3189 
3190 	/* "aa!l" */
3191 	ZEND_PARSE_PARAMETERS_START(3, 3)
3192 	        Z_PARAM_ARRAY(zserver_map)
3193 	        Z_PARAM_ARRAY_EX(zforward_map, 1, 0)
3194 	        Z_PARAM_LONG(replicas)
3195 	ZEND_PARSE_PARAMETERS_END();
3196 
3197 	MEMC_METHOD_FETCH_OBJECT;
3198 
3199 	if (zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) == 0) {
3200 		php_error_docref(NULL, E_WARNING, "server map cannot be empty");
3201 		RETURN_FALSE;
3202 	}
3203 
3204 	if (zforward_map && zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) != zend_hash_num_elements (Z_ARRVAL_P(zforward_map))) {
3205 		php_error_docref(NULL, E_WARNING, "forward_map length must match the server_map length");
3206 		RETURN_FALSE;
3207 	}
3208 
3209 	if (replicas < 0) {
3210 		php_error_docref(NULL, E_WARNING, "replicas must be larger than zero");
3211 		RETURN_FALSE;
3212 	}
3213 
3214 	server_map = s_zval_to_uint32_array (zserver_map, &server_map_len);
3215 
3216 	if (!server_map) {
3217 		RETURN_FALSE;
3218 	}
3219 
3220 	if (zforward_map) {
3221 		forward_map = s_zval_to_uint32_array (zforward_map, &forward_map_len);
3222 
3223 		if (!forward_map) {
3224 			efree (server_map);
3225 			RETURN_FALSE;
3226 		}
3227 	}
3228 
3229 	rc = memcached_bucket_set (intern->memc, server_map, forward_map, (uint32_t) server_map_len, replicas);
3230 
3231 	if (s_memc_status_handle_result_code(intern, rc) == FAILURE) {
3232 		retval = 0;
3233 	}
3234 
3235 	efree(server_map);
3236 
3237 	if (forward_map) {
3238 		efree(forward_map);
3239 	}
3240 	RETURN_BOOL(retval);
3241 }
3242 /* }}} */
3243 
3244 /* {{{ Memcached::setOptions(array options)
3245    Sets the value for the given option constant */
PHP_METHOD(Memcached,setOptions)3246 static PHP_METHOD(Memcached, setOptions)
3247 {
3248 	zval *options;
3249 	zend_bool ok = 1;
3250 	zend_string *key;
3251 	zend_ulong key_index;
3252 	zval *value;
3253 
3254 	MEMC_METHOD_INIT_VARS;
3255 
3256 	/* "a" */
3257 	ZEND_PARSE_PARAMETERS_START(1, 1)
3258 	        Z_PARAM_ARRAY(options)
3259 	ZEND_PARSE_PARAMETERS_END();
3260 
3261 
3262 	MEMC_METHOD_FETCH_OBJECT;
3263 
3264 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), key_index, key, value) {
3265 		if (key) {
3266 			php_error_docref(NULL, E_WARNING, "invalid configuration option");
3267 			ok = 0;
3268 		} else {
3269 			if (!php_memc_set_option(intern, (long) key_index, value)) {
3270 				ok = 0;
3271 			}
3272 		}
3273 	} ZEND_HASH_FOREACH_END();
3274 
3275 	RETURN_BOOL(ok);
3276 }
3277 /* }}} */
3278 
3279 /* {{{ Memcached::setOption(int option, mixed value)
3280    Sets the value for the given option constant */
PHP_METHOD(Memcached,setOption)3281 static PHP_METHOD(Memcached, setOption)
3282 {
3283 	zend_long option;
3284 	zval *value;
3285 	MEMC_METHOD_INIT_VARS;
3286 
3287 	/* "lz/" */
3288 	ZEND_PARSE_PARAMETERS_START(2, 2)
3289 	        Z_PARAM_LONG(option)
3290 	        Z_PARAM_ZVAL_EX(value, 0, 1)
3291 	ZEND_PARSE_PARAMETERS_END();
3292 
3293 	MEMC_METHOD_FETCH_OBJECT;
3294 
3295 	RETURN_BOOL(php_memc_set_option(intern, option, value));
3296 }
3297 /* }}} */
3298 
3299 #ifdef HAVE_MEMCACHED_SASL
3300 /* {{{ Memcached::setSaslAuthData(string user, string pass)
3301    Sets sasl credentials */
PHP_METHOD(Memcached,setSaslAuthData)3302 static PHP_METHOD(Memcached, setSaslAuthData)
3303 {
3304 	MEMC_METHOD_INIT_VARS;
3305 	memcached_return status;
3306 	zend_string *user, *pass;
3307 
3308 	/* "SS/" */
3309 	ZEND_PARSE_PARAMETERS_START(2, 2)
3310 	        Z_PARAM_STR(user)
3311 	        Z_PARAM_STR(pass)
3312 	ZEND_PARSE_PARAMETERS_END();
3313 
3314 	if (!php_memc_init_sasl_if_needed()) {
3315 		RETURN_FALSE;
3316 	}
3317 
3318 	MEMC_METHOD_FETCH_OBJECT;
3319 
3320 	if (!memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) {
3321 		php_error_docref(NULL, E_WARNING, "SASL is only supported with binary protocol");
3322 		RETURN_FALSE;
3323 	}
3324 	memc_user_data->has_sasl_data = 1;
3325 	status = memcached_set_sasl_auth_data(intern->memc, ZSTR_VAL(user), ZSTR_VAL(pass));
3326 
3327 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
3328 		RETURN_FALSE;
3329 	}
3330 	RETURN_TRUE;
3331 }
3332 /* }}} */
3333 #endif /* HAVE_MEMCACHED_SASL */
3334 
3335 #ifdef HAVE_MEMCACHED_SET_ENCODING_KEY
3336 /* {{{ Memcached::setEncodingKey(string key)
3337    Sets AES encryption key (libmemcached 1.0.6 and higher) */
PHP_METHOD(Memcached,setEncodingKey)3338 static PHP_METHOD(Memcached, setEncodingKey)
3339 {
3340 	MEMC_METHOD_INIT_VARS;
3341 	memcached_return status;
3342 	zend_string *key;
3343 
3344 	/* "S" */
3345 	ZEND_PARSE_PARAMETERS_START(1, 1)
3346 	        Z_PARAM_STR(key)
3347 	ZEND_PARSE_PARAMETERS_END();
3348 
3349 	MEMC_METHOD_FETCH_OBJECT;
3350 
3351 	// libmemcached < 1.0.18 cannot handle a change of encoding key. Warn about this and return false.
3352 #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018
3353 	if (memc_user_data->encoding_enabled) {
3354 		php_error_docref(NULL, E_WARNING, "libmemcached versions less than 1.0.18 cannot change encoding key");
3355 		RETURN_FALSE;
3356 	}
3357 #endif
3358 
3359 	status = memcached_set_encoding_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key));
3360 
3361 	if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
3362 		RETURN_FALSE;
3363 	}
3364 
3365 	memc_user_data->encoding_enabled = 1;
3366 	RETURN_TRUE;
3367 }
3368 /* }}} */
3369 #endif /* HAVE_MEMCACHED_SET_ENCODING_KEY */
3370 
3371 /* {{{ Memcached::getResultCode()
3372    Returns the result code from the last operation */
PHP_METHOD(Memcached,getResultCode)3373 static PHP_METHOD(Memcached, getResultCode)
3374 {
3375 	MEMC_METHOD_INIT_VARS;
3376 
3377 	if (zend_parse_parameters_none() == FAILURE) {
3378 		return;
3379 	}
3380 
3381 	MEMC_METHOD_FETCH_OBJECT;
3382 
3383 	RETURN_LONG(intern->rescode);
3384 }
3385 /* }}} */
3386 
3387 /* {{{ Memcached::getResultMessage()
3388    Returns the result message from the last operation */
PHP_METHOD(Memcached,getResultMessage)3389 static PHP_METHOD(Memcached, getResultMessage)
3390 {
3391 	MEMC_METHOD_INIT_VARS;
3392 
3393 	if (zend_parse_parameters_none() == FAILURE) {
3394 		return;
3395 	}
3396 
3397 	MEMC_METHOD_FETCH_OBJECT;
3398 
3399 	switch (intern->rescode) {
3400 		case MEMC_RES_PAYLOAD_FAILURE:
3401 			RETURN_STRING("PAYLOAD FAILURE");
3402 			break;
3403 
3404 		case MEMCACHED_ERRNO:
3405 		case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE:
3406 		case MEMCACHED_UNKNOWN_READ_FAILURE:
3407 			if (intern->memc_errno) {
3408 				zend_string *str = strpprintf(0, "%s: %s",
3409 						memcached_strerror(intern->memc, (memcached_return)intern->rescode), strerror(intern->memc_errno));
3410 				RETURN_STR(str);
3411 			}
3412 			/* Fall through */
3413 		default:
3414 			RETURN_STRING(memcached_strerror(intern->memc, (memcached_return)intern->rescode));
3415 			break;
3416 	}
3417 
3418 }
3419 /* }}} */
3420 
3421 /* {{{ Memcached::isPersistent()
3422    Returns the true if instance uses a persistent connection */
PHP_METHOD(Memcached,isPersistent)3423 static PHP_METHOD(Memcached, isPersistent)
3424 {
3425 	MEMC_METHOD_INIT_VARS;
3426 
3427 	if (zend_parse_parameters_none() == FAILURE) {
3428 		return;
3429 	}
3430 
3431 	MEMC_METHOD_FETCH_OBJECT;
3432 
3433 	RETURN_BOOL(memc_user_data->is_persistent);
3434 }
3435 /* }}} */
3436 
3437 /* {{{ Memcached::isPristine()
3438    Returns the true if instance is recently created */
PHP_METHOD(Memcached,isPristine)3439 static PHP_METHOD(Memcached, isPristine)
3440 {
3441 	MEMC_METHOD_INIT_VARS;
3442 
3443 	if (zend_parse_parameters_none() == FAILURE) {
3444 		return;
3445 	}
3446 
3447 	MEMC_METHOD_FETCH_OBJECT;
3448 
3449 	RETURN_BOOL(intern->is_pristine);
3450 }
3451 /* }}} */
3452 
3453 /* {{{ bool Memcached::checkKey(string key)
3454    Checks if a key is valid */
PHP_METHOD(Memcached,checkKey)3455 PHP_METHOD(Memcached, checkKey)
3456 {
3457 	zend_string *key;
3458 	MEMC_METHOD_INIT_VARS;
3459 
3460 	ZEND_PARSE_PARAMETERS_START(1, 1)
3461 		Z_PARAM_STR(key)
3462 	ZEND_PARSE_PARAMETERS_END();
3463 
3464 	MEMC_METHOD_FETCH_OBJECT;
3465 	s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
3466 	MEMC_CHECK_KEY(intern, key);
3467 	RETURN_TRUE;
3468 }
3469 /* }}} */
3470 
3471 /****************************************
3472   Internal support code
3473 ****************************************/
3474 
3475 /* {{{ constructor/destructor */
3476 static
php_memc_destroy(memcached_st * memc,php_memc_user_data_t * memc_user_data)3477 void php_memc_destroy(memcached_st *memc, php_memc_user_data_t *memc_user_data)
3478 {
3479 #ifdef HAVE_MEMCACHED_SASL
3480 	if (memc_user_data->has_sasl_data) {
3481 		memcached_destroy_sasl_auth_data(memc);
3482 	}
3483 #endif
3484 
3485 	memcached_free(memc);
3486 	pefree(memc_user_data, memc_user_data->is_persistent);
3487 }
3488 
3489 static
php_memc_object_free_storage(zend_object * object)3490 void php_memc_object_free_storage(zend_object *object)
3491 {
3492 	php_memc_object_t *intern = php_memc_fetch_object(object);
3493 
3494 	if (intern->memc) {
3495 		php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
3496 
3497 		if (!memc_user_data->is_persistent) {
3498 			php_memc_destroy(intern->memc, memc_user_data);
3499 		}
3500 	}
3501 
3502 	intern->memc = NULL;
3503 	zend_object_std_dtor(&intern->zo);
3504 }
3505 
3506 static
php_memc_object_new(zend_class_entry * ce)3507 zend_object *php_memc_object_new(zend_class_entry *ce)
3508 {
3509 	php_memc_object_t *intern = ecalloc(1, sizeof(php_memc_object_t) + zend_object_properties_size(ce));
3510 
3511 	zend_object_std_init(&intern->zo, ce);
3512 	object_properties_init(&intern->zo, ce);
3513 
3514 	intern->zo.handlers = &memcached_object_handlers;
3515 	return &intern->zo;
3516 }
3517 
3518 #ifdef HAVE_MEMCACHED_PROTOCOL
3519 static
php_memc_server_free_storage(zend_object * object)3520 void php_memc_server_free_storage(zend_object *object)
3521 {
3522 	php_memc_server_t *intern = php_memc_server_fetch_object(object);
3523 	zend_object_std_dtor(&intern->zo);
3524 }
3525 
php_memc_server_new(zend_class_entry * ce)3526 zend_object *php_memc_server_new(zend_class_entry *ce)
3527 {
3528 	php_memc_server_t *intern;
3529 
3530 	intern = ecalloc(1, sizeof(php_memc_server_t) + zend_object_properties_size(ce));
3531 
3532 	zend_object_std_init(&intern->zo, ce);
3533 	object_properties_init(&intern->zo, ce);
3534 
3535 	intern->zo.handlers = &memcached_server_object_handlers;
3536 
3537 	return &intern->zo;
3538 }
3539 #endif
3540 
ZEND_RSRC_DTOR_FUNC(php_memc_dtor)3541 ZEND_RSRC_DTOR_FUNC(php_memc_dtor)
3542 {
3543 	if (res->ptr) {
3544 		memcached_st *memc = (memcached_st *) res->ptr;
3545 		php_memc_destroy(memc, memcached_get_user_data(memc));
3546 		res->ptr = NULL;
3547 	}
3548 }
3549 
3550 /* }}} */
3551 
3552 /* {{{ internal API functions */
3553 static
s_server_cursor_list_servers_cb(const memcached_st * ptr,php_memcached_instance_st instance,void * in_context)3554 memcached_return s_server_cursor_list_servers_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context)
3555 {
3556 	zval array;
3557 	zval *return_value = (zval *) in_context;
3558 
3559 	array_init(&array);
3560 	add_assoc_string(&array, "host", (char*)memcached_server_name(instance));
3561 	add_assoc_long(&array,   "port", memcached_server_port(instance));
3562 	add_assoc_string(&array, "type", (char*)memcached_server_type(instance));
3563 	/*
3564 	 * API does not allow to get at this field.
3565 	add_assoc_long(array, "weight", instance->weight);
3566 	*/
3567 
3568 	add_next_index_zval(return_value, &array);
3569 	return MEMCACHED_SUCCESS;
3570 }
3571 
3572 static
s_server_cursor_version_cb(const memcached_st * ptr,php_memcached_instance_st instance,void * in_context)3573 memcached_return s_server_cursor_version_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context)
3574 {
3575 	zend_string *address, *version;
3576 	zval rv, *return_value = (zval *)in_context;
3577 
3578 #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000009
3579 	version = strpprintf(0, "%d.%d.%d",
3580 				memcached_server_major_version(instance),
3581 				memcached_server_minor_version(instance),
3582 				memcached_server_micro_version(instance));
3583 #else
3584 	version = strpprintf(0, "%d.%d.%d",
3585 				instance->major_version,
3586 				instance->minor_version,
3587 				instance->micro_version);
3588 #endif
3589 
3590 	address = strpprintf(0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance));
3591 
3592 	ZVAL_STR(&rv, version);
3593 	zend_hash_add(Z_ARRVAL_P(return_value), address, &rv);
3594 
3595 	zend_string_release(address);
3596 
3597 	return MEMCACHED_SUCCESS;
3598 }
3599 
3600 
3601 static
s_decompress_value(const char * payload,size_t payload_len,uint32_t flags)3602 zend_string *s_decompress_value (const char *payload, size_t payload_len, uint32_t flags)
3603 {
3604 	zend_string *buffer;
3605 
3606 	uint32_t stored_length;
3607 	unsigned long length;
3608 	zend_bool decompress_status = 0;
3609 	zend_bool is_fastlz = 0, is_zlib = 0;
3610 
3611 	if (payload_len < sizeof (uint32_t)) {
3612 		return NULL;
3613 	}
3614 
3615 	is_fastlz = MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ);
3616 	is_zlib   = MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB);
3617 
3618 	if (!is_fastlz && !is_zlib) {
3619 		php_error_docref(NULL, E_WARNING, "could not decompress value: unrecognised compression type");
3620 		return NULL;
3621 	}
3622 
3623 	memcpy(&stored_length, payload, sizeof (uint32_t));
3624 
3625 	payload     += sizeof (uint32_t);
3626 	payload_len -= sizeof (uint32_t);
3627 
3628 	buffer = zend_string_alloc (stored_length, 0);
3629 
3630 	if (is_fastlz) {
3631 		decompress_status = ((length = fastlz_decompress(payload, payload_len, &buffer->val, buffer->len)) > 0);
3632 	}
3633 	else if (is_zlib) {
3634 		decompress_status = (uncompress((Bytef *) buffer->val, &buffer->len, (Bytef *)payload, payload_len) == Z_OK);
3635 	}
3636 
3637 	ZSTR_VAL(buffer)[stored_length] = '\0';
3638 
3639 	if (!decompress_status) {
3640 		php_error_docref(NULL, E_WARNING, "could not decompress value");
3641 		zend_string_release (buffer);
3642 		return NULL;
3643 	}
3644 
3645 	zend_string_forget_hash_val(buffer);
3646 	return buffer;
3647 }
3648 
3649 static
s_unserialize_value(memcached_st * memc,int val_type,zend_string * payload,zval * return_value)3650 zend_bool s_unserialize_value (memcached_st *memc, int val_type, zend_string *payload, zval *return_value)
3651 {
3652 	switch (val_type) {
3653 		case MEMC_VAL_IS_SERIALIZED:
3654 		{
3655 			php_unserialize_data_t var_hash;
3656 			const unsigned char *p, *max;
3657 
3658 			p   = (const unsigned char *) ZSTR_VAL(payload);
3659 			max = p + ZSTR_LEN(payload);
3660 
3661 			PHP_VAR_UNSERIALIZE_INIT(var_hash);
3662 			if (!php_var_unserialize(return_value, &p, max, &var_hash)) {
3663 				zval_ptr_dtor(return_value);
3664 				ZVAL_FALSE(return_value);
3665 				PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
3666 				php_error_docref(NULL, E_WARNING, "could not unserialize value");
3667 				return 0;
3668 			}
3669 			PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
3670 		}
3671 			break;
3672 
3673 		case MEMC_VAL_IS_IGBINARY:
3674 #ifdef HAVE_MEMCACHED_IGBINARY
3675 			if (igbinary_unserialize((uint8_t *) ZSTR_VAL(payload), ZSTR_LEN(payload), return_value)) {
3676 				ZVAL_FALSE(return_value);
3677 				php_error_docref(NULL, E_WARNING, "could not unserialize value with igbinary");
3678 				return 0;
3679 			}
3680 #else
3681 			ZVAL_FALSE(return_value);
3682 			php_error_docref(NULL, E_WARNING, "could not unserialize value, no igbinary support");
3683 			return 0;
3684 #endif
3685 			break;
3686 
3687 		case MEMC_VAL_IS_JSON:
3688 #ifdef HAVE_JSON_API
3689 		{
3690 			php_memc_user_data_t *memc_user_data = memcached_get_user_data(memc);
3691 			php_json_decode(return_value, ZSTR_VAL(payload), ZSTR_LEN(payload), (memc_user_data->serializer == SERIALIZER_JSON_ARRAY), PHP_JSON_PARSER_DEFAULT_DEPTH);
3692 		}
3693 #else
3694 			ZVAL_FALSE(return_value);
3695 			php_error_docref(NULL, E_WARNING, "could not unserialize value, no json support");
3696 			return 0;
3697 #endif
3698 			break;
3699 
3700 		case MEMC_VAL_IS_MSGPACK:
3701 #ifdef HAVE_MEMCACHED_MSGPACK
3702 			php_msgpack_unserialize(return_value, ZSTR_VAL(payload), ZSTR_LEN(payload));
3703 #else
3704 			ZVAL_FALSE(return_value);
3705 			php_error_docref(NULL, E_WARNING, "could not unserialize value, no msgpack support");
3706 			return 0;
3707 #endif
3708  			break;
3709 	}
3710 	return 1;
3711 }
3712 
3713 static
s_memcached_result_to_zval(memcached_st * memc,memcached_result_st * result,zval * return_value)3714 zend_bool s_memcached_result_to_zval(memcached_st *memc, memcached_result_st *result, zval *return_value)
3715 {
3716 	zend_string *data;
3717 	const char *payload;
3718 	size_t payload_len;
3719 	uint32_t flags;
3720 	zend_bool retval = 1;
3721 
3722 	payload     = memcached_result_value(result);
3723 	payload_len = memcached_result_length(result);
3724 	flags       = memcached_result_flags(result);
3725 
3726 	if (!payload && payload_len > 0) {
3727 		php_error_docref(NULL, E_WARNING, "Could not handle non-existing value of length %zu", payload_len);
3728 		return 0;
3729 	}
3730 
3731 	if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSED)) {
3732 		data = s_decompress_value (payload, payload_len, flags);
3733 		if (!data) {
3734 			return 0;
3735 		}
3736 	} else {
3737 		data = zend_string_init(payload, payload_len, 0);
3738 	}
3739 
3740 	switch (MEMC_VAL_GET_TYPE(flags)) {
3741 
3742 		case MEMC_VAL_IS_STRING:
3743 			ZVAL_STR_COPY(return_value, data);
3744 			break;
3745 
3746 		case MEMC_VAL_IS_LONG:
3747 			ZVAL_LONG(return_value, strtol(ZSTR_VAL(data), NULL, 10));
3748 			break;
3749 
3750 		case MEMC_VAL_IS_DOUBLE:
3751 		{
3752 			if (zend_string_equals_literal(data, "Infinity")) {
3753 				ZVAL_DOUBLE(return_value, php_get_inf());
3754 			}
3755 			else if (zend_string_equals_literal(data, "-Infinity")) {
3756 				ZVAL_DOUBLE(return_value, -php_get_inf());
3757 			}
3758 			else if (zend_string_equals_literal(data, "NaN")) {
3759 				ZVAL_DOUBLE(return_value, php_get_nan());
3760 			}
3761 			else {
3762 				ZVAL_DOUBLE(return_value, zend_strtod(ZSTR_VAL(data), NULL));
3763 			}
3764 		}
3765 			break;
3766 
3767 		case MEMC_VAL_IS_BOOL:
3768 			ZVAL_BOOL(return_value, payload_len > 0 && ZSTR_VAL(data)[0] == '1');
3769 			break;
3770 
3771 		case MEMC_VAL_IS_SERIALIZED:
3772 		case MEMC_VAL_IS_IGBINARY:
3773 		case MEMC_VAL_IS_JSON:
3774 		case MEMC_VAL_IS_MSGPACK:
3775 			retval = s_unserialize_value (memc, MEMC_VAL_GET_TYPE(flags), data, return_value);
3776 			break;
3777 
3778 		default:
3779 			php_error_docref(NULL, E_WARNING, "unknown payload type");
3780 			retval = 0;
3781 			break;
3782 	}
3783 	zend_string_release(data);
3784 
3785 	return retval;
3786 }
3787 
3788 
3789 PHP_MEMCACHED_API
php_memc_get_ce(void)3790 zend_class_entry *php_memc_get_ce(void)
3791 {
3792 	return memcached_ce;
3793 }
3794 
3795 PHP_MEMCACHED_API
php_memc_get_exception(void)3796 zend_class_entry *php_memc_get_exception(void)
3797 {
3798 	return memcached_exception_ce;
3799 }
3800 
3801 PHP_MEMCACHED_API
php_memc_get_exception_base(int root)3802 zend_class_entry *php_memc_get_exception_base(int root)
3803 {
3804 	if (!root) {
3805 		if (!spl_ce_RuntimeException) {
3806 			zend_class_entry *pce;
3807 			zval *pce_z;
3808 
3809 			if ((pce_z = zend_hash_str_find(CG(class_table),
3810 							"runtimeexception",
3811 							sizeof("RuntimeException") - 1)) != NULL) {
3812 				pce = Z_CE_P(pce_z);
3813 				spl_ce_RuntimeException = pce;
3814 				return pce;
3815 			}
3816 		} else {
3817 			return spl_ce_RuntimeException;
3818 		}
3819 	}
3820 
3821 	return zend_exception_get_default();
3822 }
3823 
3824 
3825 #ifdef HAVE_MEMCACHED_PROTOCOL
3826 
3827 static
s_destroy_cb(zend_fcall_info * fci)3828 void s_destroy_cb (zend_fcall_info *fci)
3829 {
3830 	if (fci->size > 0) {
3831 		zval_ptr_dtor(&fci->function_name);
3832 		if (fci->object) {
3833 			OBJ_RELEASE(fci->object);
3834 		}
3835 	}
3836 }
3837 
3838 static
PHP_METHOD(MemcachedServer,run)3839 PHP_METHOD(MemcachedServer, run)
3840 {
3841 	int i;
3842 	zend_bool rc;
3843 	zend_string *address;
3844 
3845 	php_memc_server_t *intern;
3846 	intern = Z_MEMC_SERVER_P(getThis());
3847 
3848 	/* "S" */
3849 	ZEND_PARSE_PARAMETERS_START(1, 1)
3850 	        Z_PARAM_STR(address)
3851 	ZEND_PARSE_PARAMETERS_END();
3852 
3853 	rc = php_memc_proto_handler_run(intern->handler, address);
3854 
3855 	for (i = MEMC_SERVER_ON_MIN + 1; i < MEMC_SERVER_ON_MAX; i++) {
3856 		s_destroy_cb(&MEMC_SERVER_G(callbacks) [i].fci);
3857 	}
3858 
3859 	RETURN_BOOL(rc);
3860 }
3861 
3862 static
PHP_METHOD(MemcachedServer,on)3863 PHP_METHOD(MemcachedServer, on)
3864 {
3865 	zend_long event;
3866 	zend_fcall_info fci;
3867 	zend_fcall_info_cache fci_cache;
3868 	zend_bool rc = 0;
3869 
3870 	/* "lf!" */
3871 	ZEND_PARSE_PARAMETERS_START(2, 2)
3872 	        Z_PARAM_LONG(event)
3873 	        Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
3874 	ZEND_PARSE_PARAMETERS_END();
3875 
3876 	if (event <= MEMC_SERVER_ON_MIN || event >= MEMC_SERVER_ON_MAX) {
3877 		RETURN_FALSE;
3878 	}
3879 
3880 	if (fci.size > 0) {
3881 		s_destroy_cb (&MEMC_SERVER_G(callbacks) [event].fci);
3882 
3883 		MEMC_SERVER_G(callbacks) [event].fci       = fci;
3884 		MEMC_SERVER_G(callbacks) [event].fci_cache = fci_cache;
3885 
3886 		Z_TRY_ADDREF(fci.function_name);
3887 		if (fci.object) {
3888 			GC_ADDREF(fci.object);
3889 		}
3890 	}
3891 	RETURN_BOOL(rc);
3892 }
3893 
3894 #endif
3895 
3896 #if PHP_VERSION_ID < 80000
3897 #include "php_memcached_legacy_arginfo.h"
3898 #else
3899 #include "php_memcached_arginfo.h"
3900 #endif
3901 
3902 /* {{{ memcached_module_entry
3903  */
3904 
3905 #if ZEND_MODULE_API_NO >= 20050922
3906 static const zend_module_dep memcached_deps[] = {
3907 #ifdef HAVE_MEMCACHED_SESSION
3908 	ZEND_MOD_REQUIRED("session")
3909 #endif
3910 #ifdef HAVE_MEMCACHED_IGBINARY
3911 	ZEND_MOD_REQUIRED("igbinary")
3912 #endif
3913 #ifdef HAVE_MEMCACHED_MSGPACK
3914 	ZEND_MOD_REQUIRED("msgpack")
3915 #endif
3916 	ZEND_MOD_REQUIRED("spl")
3917 	ZEND_MOD_END
3918 };
3919 #endif
3920 
3921 static
PHP_GINIT_FUNCTION(php_memcached)3922 PHP_GINIT_FUNCTION(php_memcached)
3923 {
3924 #ifdef HAVE_MEMCACHED_SESSION
3925 
3926 	php_memcached_globals->session.lock_enabled = 0;
3927 	php_memcached_globals->session.lock_wait_max = 150;
3928 	php_memcached_globals->session.lock_wait_min = 150;
3929 	php_memcached_globals->session.lock_retries = 200;
3930 	php_memcached_globals->session.lock_expiration = 30;
3931 	php_memcached_globals->session.binary_protocol_enabled = 1;
3932 	php_memcached_globals->session.consistent_hash_enabled = 1;
3933 	php_memcached_globals->session.consistent_hash_type = MEMCACHED_BEHAVIOR_KETAMA;
3934 	php_memcached_globals->session.consistent_hash_name = NULL;
3935 	php_memcached_globals->session.number_of_replicas = 0;
3936 	php_memcached_globals->session.server_failure_limit = 1;
3937 	php_memcached_globals->session.randomize_replica_read_enabled = 1;
3938 	php_memcached_globals->session.remove_failed_servers_enabled = 1;
3939 	php_memcached_globals->session.connect_timeout = 1000;
3940 	php_memcached_globals->session.prefix = NULL;
3941 	php_memcached_globals->session.persistent_enabled = 0;
3942 	php_memcached_globals->session.sasl_username = NULL;
3943 	php_memcached_globals->session.sasl_password = NULL;
3944 
3945 #endif
3946 	php_memcached_globals->memc.serializer_name = NULL;
3947 	php_memcached_globals->memc.serializer_type = SERIALIZER_DEFAULT;
3948 	php_memcached_globals->memc.compression_name = NULL;
3949 	php_memcached_globals->memc.compression_threshold = 2000;
3950 	php_memcached_globals->memc.compression_type = COMPRESSION_TYPE_FASTLZ;
3951 	php_memcached_globals->memc.compression_factor = 1.30;
3952 	php_memcached_globals->memc.store_retry_count = 2;
3953 
3954 	php_memcached_globals->memc.sasl_initialised = 0;
3955 	php_memcached_globals->no_effect = 0;
3956 
3957 	/* Defaults for certain options */
3958 	php_memcached_globals->memc.default_behavior.consistent_hash_enabled = 0;
3959 	php_memcached_globals->memc.default_behavior.binary_protocol_enabled = 0;
3960 	php_memcached_globals->memc.default_behavior.connect_timeout         = 0;
3961 }
3962 
3963 zend_module_entry memcached_module_entry = {
3964 	STANDARD_MODULE_HEADER_EX, NULL,
3965 	memcached_deps,
3966 	"memcached",
3967 	NULL,
3968 	PHP_MINIT(memcached),
3969 	PHP_MSHUTDOWN(memcached),
3970 	NULL,
3971 	NULL,
3972 	PHP_MINFO(memcached),
3973 	PHP_MEMCACHED_VERSION,
3974 	PHP_MODULE_GLOBALS(php_memcached),
3975 	PHP_GINIT(php_memcached),
3976 	NULL,
3977 	NULL,
3978 	STANDARD_MODULE_PROPERTIES_EX
3979 };
3980 /* }}} */
3981 
3982 
3983 /* {{{ php_memc_register_constants */
php_memc_register_constants(INIT_FUNC_ARGS)3984 static void php_memc_register_constants(INIT_FUNC_ARGS)
3985 {
3986 	#define REGISTER_MEMC_CLASS_CONST_LONG(name, value) zend_declare_class_constant_long(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value)
3987 	#define REGISTER_MEMC_CLASS_CONST_BOOL(name, value) zend_declare_class_constant_bool(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value)
3988 	#define REGISTER_MEMC_CLASS_CONST_NULL(name) zend_declare_class_constant_null(php_memc_get_ce() , ZEND_STRS( #name ) - 1)
3989 
3990 	/*
3991 	 * Class options
3992 	 */
3993 	REGISTER_MEMC_CLASS_CONST_LONG(LIBMEMCACHED_VERSION_HEX, LIBMEMCACHED_VERSION_HEX);
3994 
3995 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION, MEMC_OPT_COMPRESSION);
3996 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION_TYPE, MEMC_OPT_COMPRESSION_TYPE);
3997 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_PREFIX_KEY,  MEMC_OPT_PREFIX_KEY);
3998 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERIALIZER,  MEMC_OPT_SERIALIZER);
3999 
4000 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_USER_FLAGS,  MEMC_OPT_USER_FLAGS);
4001 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_STORE_RETRY_COUNT,  MEMC_OPT_STORE_RETRY_COUNT);
4002 
4003 	/*
4004 	 * Indicate whether igbinary serializer is available
4005 	 */
4006 #ifdef HAVE_MEMCACHED_IGBINARY
4007 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_IGBINARY, 1);
4008 #else
4009 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_IGBINARY, 0);
4010 #endif
4011 
4012 	/*
4013 	 * Indicate whether json serializer is available
4014 	 */
4015 #ifdef HAVE_JSON_API
4016 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_JSON, 1);
4017 #else
4018 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_JSON, 0);
4019 #endif
4020 
4021 	/*
4022 	 * Indicate whether msgpack serializer is available
4023 	 */
4024 #ifdef HAVE_MEMCACHED_MSGPACK
4025 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_MSGPACK, 1);
4026 #else
4027 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_MSGPACK, 0);
4028 #endif
4029 
4030 	/*
4031 	 * Indicate whether set_encoding_key is available
4032 	 */
4033 #ifdef HAVE_MEMCACHED_SET_ENCODING_KEY
4034 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ENCODING, 1);
4035 #else
4036 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ENCODING, 0);
4037 #endif
4038 
4039 #ifdef HAVE_MEMCACHED_SESSION
4040 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SESSION, 1);
4041 #else
4042 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SESSION, 0);
4043 #endif
4044 
4045 #ifdef HAVE_MEMCACHED_SASL
4046 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SASL, 1);
4047 #else
4048 	REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SASL, 0);
4049 #endif
4050 
4051 	/*
4052 	 * libmemcached behavior options
4053 	 */
4054 
4055 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_HASH, MEMCACHED_BEHAVIOR_HASH);
4056 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_DEFAULT, MEMCACHED_HASH_DEFAULT);
4057 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_MD5, MEMCACHED_HASH_MD5);
4058 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_CRC, MEMCACHED_HASH_CRC);
4059 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1_64, MEMCACHED_HASH_FNV1_64);
4060 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1A_64, MEMCACHED_HASH_FNV1A_64);
4061 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1_32, MEMCACHED_HASH_FNV1_32);
4062 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1A_32, MEMCACHED_HASH_FNV1A_32);
4063 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_HSIEH, MEMCACHED_HASH_HSIEH);
4064 	REGISTER_MEMC_CLASS_CONST_LONG(HASH_MURMUR, MEMCACHED_HASH_MURMUR);
4065 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_DISTRIBUTION, MEMCACHED_BEHAVIOR_DISTRIBUTION);
4066 	REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_MODULA, MEMCACHED_DISTRIBUTION_MODULA);
4067 	REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_CONSISTENT, MEMCACHED_DISTRIBUTION_CONSISTENT);
4068 	REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_VIRTUAL_BUCKET, MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET);
4069 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_LIBKETAMA_COMPATIBLE, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
4070 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_LIBKETAMA_HASH, MEMCACHED_BEHAVIOR_KETAMA_HASH);
4071 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_TCP_KEEPALIVE, MEMCACHED_BEHAVIOR_TCP_KEEPALIVE);
4072 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_BUFFER_WRITES, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS);
4073 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_BINARY_PROTOCOL, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL);
4074 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_NO_BLOCK, MEMCACHED_BEHAVIOR_NO_BLOCK);
4075 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_TCP_NODELAY, MEMCACHED_BEHAVIOR_TCP_NODELAY);
4076 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_SOCKET_SEND_SIZE, MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE);
4077 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_SOCKET_RECV_SIZE, MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE);
4078 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_CONNECT_TIMEOUT, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT);
4079 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_RETRY_TIMEOUT, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT);
4080 #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000003
4081 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_DEAD_TIMEOUT, MEMCACHED_BEHAVIOR_DEAD_TIMEOUT);
4082 #endif
4083 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_SEND_TIMEOUT, MEMCACHED_BEHAVIOR_SND_TIMEOUT);
4084 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_RECV_TIMEOUT, MEMCACHED_BEHAVIOR_RCV_TIMEOUT);
4085 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_POLL_TIMEOUT, MEMCACHED_BEHAVIOR_POLL_TIMEOUT);
4086 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_CACHE_LOOKUPS, MEMCACHED_BEHAVIOR_CACHE_LOOKUPS);
4087 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERVER_FAILURE_LIMIT, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT);
4088 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_AUTO_EJECT_HOSTS, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS);
4089 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_HASH_WITH_PREFIX_KEY, MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY);
4090 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_NOREPLY, MEMCACHED_BEHAVIOR_NOREPLY);
4091 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_SORT_HOSTS, MEMCACHED_BEHAVIOR_SORT_HOSTS);
4092 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_VERIFY_KEY, MEMCACHED_BEHAVIOR_VERIFY_KEY);
4093 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_USE_UDP, MEMCACHED_BEHAVIOR_USE_UDP);
4094 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_NUMBER_OF_REPLICAS, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS);
4095 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_RANDOMIZE_REPLICA_READ, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ);
4096 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_REMOVE_FAILED_SERVERS, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS);
4097 #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000018
4098 	REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERVER_TIMEOUT_LIMIT, MEMCACHED_BEHAVIOR_SERVER_TIMEOUT_LIMIT);
4099 #endif
4100 
4101 	/*
4102 	 * libmemcached result codes
4103 	 */
4104 
4105 	REGISTER_MEMC_CLASS_CONST_LONG(RES_SUCCESS,                   MEMCACHED_SUCCESS);
4106 	REGISTER_MEMC_CLASS_CONST_LONG(RES_FAILURE,                   MEMCACHED_FAILURE);
4107 	REGISTER_MEMC_CLASS_CONST_LONG(RES_HOST_LOOKUP_FAILURE,       MEMCACHED_HOST_LOOKUP_FAILURE);
4108 	REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_FAILURE,        MEMCACHED_CONNECTION_FAILURE);
4109 	REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_BIND_FAILURE,   MEMCACHED_CONNECTION_BIND_FAILURE);
4110 	REGISTER_MEMC_CLASS_CONST_LONG(RES_WRITE_FAILURE,             MEMCACHED_WRITE_FAILURE);
4111 	REGISTER_MEMC_CLASS_CONST_LONG(RES_READ_FAILURE,              MEMCACHED_READ_FAILURE);
4112 	REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_READ_FAILURE,      MEMCACHED_UNKNOWN_READ_FAILURE);
4113 	REGISTER_MEMC_CLASS_CONST_LONG(RES_PROTOCOL_ERROR,            MEMCACHED_PROTOCOL_ERROR);
4114 	REGISTER_MEMC_CLASS_CONST_LONG(RES_CLIENT_ERROR,              MEMCACHED_CLIENT_ERROR);
4115 	REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_ERROR,              MEMCACHED_SERVER_ERROR);
4116 	REGISTER_MEMC_CLASS_CONST_LONG(RES_DATA_EXISTS,               MEMCACHED_DATA_EXISTS);
4117 	REGISTER_MEMC_CLASS_CONST_LONG(RES_DATA_DOES_NOT_EXIST,       MEMCACHED_DATA_DOES_NOT_EXIST);
4118 	REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTSTORED,                 MEMCACHED_NOTSTORED);
4119 	REGISTER_MEMC_CLASS_CONST_LONG(RES_STORED,                    MEMCACHED_STORED);
4120 	REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTFOUND,                  MEMCACHED_NOTFOUND);
4121 	REGISTER_MEMC_CLASS_CONST_LONG(RES_PARTIAL_READ,              MEMCACHED_PARTIAL_READ);
4122 	REGISTER_MEMC_CLASS_CONST_LONG(RES_SOME_ERRORS,               MEMCACHED_SOME_ERRORS);
4123 	REGISTER_MEMC_CLASS_CONST_LONG(RES_NO_SERVERS,                MEMCACHED_NO_SERVERS);
4124 	REGISTER_MEMC_CLASS_CONST_LONG(RES_END,                       MEMCACHED_END);
4125 	REGISTER_MEMC_CLASS_CONST_LONG(RES_DELETED,                   MEMCACHED_DELETED);
4126 	REGISTER_MEMC_CLASS_CONST_LONG(RES_VALUE,                     MEMCACHED_VALUE);
4127 	REGISTER_MEMC_CLASS_CONST_LONG(RES_STAT,                      MEMCACHED_STAT);
4128 	REGISTER_MEMC_CLASS_CONST_LONG(RES_ITEM,                      MEMCACHED_ITEM);
4129 	REGISTER_MEMC_CLASS_CONST_LONG(RES_ERRNO,                     MEMCACHED_ERRNO);
4130 	REGISTER_MEMC_CLASS_CONST_LONG(RES_FAIL_UNIX_SOCKET,          MEMCACHED_FAIL_UNIX_SOCKET);
4131 	REGISTER_MEMC_CLASS_CONST_LONG(RES_NOT_SUPPORTED,             MEMCACHED_NOT_SUPPORTED);
4132 	REGISTER_MEMC_CLASS_CONST_LONG(RES_NO_KEY_PROVIDED,           MEMCACHED_NO_KEY_PROVIDED);
4133 	REGISTER_MEMC_CLASS_CONST_LONG(RES_FETCH_NOTFINISHED,         MEMCACHED_FETCH_NOTFINISHED);
4134 	REGISTER_MEMC_CLASS_CONST_LONG(RES_TIMEOUT,                   MEMCACHED_TIMEOUT);
4135 	REGISTER_MEMC_CLASS_CONST_LONG(RES_BUFFERED,                  MEMCACHED_BUFFERED);
4136 	REGISTER_MEMC_CLASS_CONST_LONG(RES_BAD_KEY_PROVIDED,          MEMCACHED_BAD_KEY_PROVIDED);
4137 	REGISTER_MEMC_CLASS_CONST_LONG(RES_INVALID_HOST_PROTOCOL,     MEMCACHED_INVALID_HOST_PROTOCOL);
4138 	REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_MARKED_DEAD,        MEMCACHED_SERVER_MARKED_DEAD);
4139 	REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_STAT_KEY,          MEMCACHED_UNKNOWN_STAT_KEY);
4140 	REGISTER_MEMC_CLASS_CONST_LONG(RES_INVALID_ARGUMENTS,         MEMCACHED_INVALID_ARGUMENTS);
4141 	REGISTER_MEMC_CLASS_CONST_LONG(RES_PARSE_ERROR,               MEMCACHED_PARSE_ERROR);
4142 	REGISTER_MEMC_CLASS_CONST_LONG(RES_PARSE_USER_ERROR,          MEMCACHED_PARSE_USER_ERROR);
4143 	REGISTER_MEMC_CLASS_CONST_LONG(RES_DEPRECATED,                MEMCACHED_DEPRECATED);
4144 	REGISTER_MEMC_CLASS_CONST_LONG(RES_IN_PROGRESS,               MEMCACHED_IN_PROGRESS);
4145 	REGISTER_MEMC_CLASS_CONST_LONG(RES_MAXIMUM_RETURN,            MEMCACHED_MAXIMUM_RETURN);
4146 	REGISTER_MEMC_CLASS_CONST_LONG(RES_MEMORY_ALLOCATION_FAILURE, MEMCACHED_MEMORY_ALLOCATION_FAILURE);
4147 	REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_SOCKET_CREATE_FAILURE, MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE);
4148 
4149 	REGISTER_MEMC_CLASS_CONST_LONG(RES_E2BIG,                            MEMCACHED_E2BIG);
4150 	REGISTER_MEMC_CLASS_CONST_LONG(RES_KEY_TOO_BIG,                      MEMCACHED_KEY_TOO_BIG);
4151 	REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_TEMPORARILY_DISABLED,      MEMCACHED_SERVER_TEMPORARILY_DISABLED);
4152 
4153 #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000008
4154 	REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_MEMORY_ALLOCATION_FAILURE, MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE);
4155 #endif
4156 
4157 #ifdef HAVE_MEMCACHED_SASL
4158 	REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_PROBLEM,  MEMCACHED_AUTH_PROBLEM);
4159 	REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_FAILURE,  MEMCACHED_AUTH_FAILURE);
4160 	REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_CONTINUE, MEMCACHED_AUTH_CONTINUE);
4161 #endif
4162 
4163 	/*
4164 	 * Our result codes.
4165 	 */
4166 
4167 	REGISTER_MEMC_CLASS_CONST_LONG(RES_PAYLOAD_FAILURE, MEMC_RES_PAYLOAD_FAILURE);
4168 
4169 	/*
4170 	 * Serializer types.
4171 	 */
4172 	REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_PHP,        SERIALIZER_PHP);
4173 	REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_IGBINARY,   SERIALIZER_IGBINARY);
4174 	REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_JSON,       SERIALIZER_JSON);
4175 	REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_JSON_ARRAY, SERIALIZER_JSON_ARRAY);
4176 	REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_MSGPACK,    SERIALIZER_MSGPACK);
4177 
4178 	/*
4179 	 * Compression types
4180 	 */
4181 	REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_FASTLZ, COMPRESSION_TYPE_FASTLZ);
4182 	REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_ZLIB,   COMPRESSION_TYPE_ZLIB);
4183 
4184 	/*
4185 	 * Flags.
4186 	 */
4187 	REGISTER_MEMC_CLASS_CONST_LONG(GET_PRESERVE_ORDER, MEMC_GET_PRESERVE_ORDER);
4188 	REGISTER_MEMC_CLASS_CONST_LONG(GET_EXTENDED,       MEMC_GET_EXTENDED);
4189 
4190 #ifdef HAVE_MEMCACHED_PROTOCOL
4191 	/*
4192 	 * Server callbacks
4193 	 */
4194 	REGISTER_MEMC_CLASS_CONST_LONG(ON_CONNECT,   MEMC_SERVER_ON_CONNECT);
4195 	REGISTER_MEMC_CLASS_CONST_LONG(ON_ADD,       MEMC_SERVER_ON_ADD);
4196 	REGISTER_MEMC_CLASS_CONST_LONG(ON_APPEND,    MEMC_SERVER_ON_APPEND);
4197 	REGISTER_MEMC_CLASS_CONST_LONG(ON_DECREMENT, MEMC_SERVER_ON_DECREMENT);
4198 	REGISTER_MEMC_CLASS_CONST_LONG(ON_DELETE,    MEMC_SERVER_ON_DELETE);
4199 	REGISTER_MEMC_CLASS_CONST_LONG(ON_FLUSH,     MEMC_SERVER_ON_FLUSH);
4200 	REGISTER_MEMC_CLASS_CONST_LONG(ON_GET,       MEMC_SERVER_ON_GET);
4201 	REGISTER_MEMC_CLASS_CONST_LONG(ON_INCREMENT, MEMC_SERVER_ON_INCREMENT);
4202 	REGISTER_MEMC_CLASS_CONST_LONG(ON_NOOP,      MEMC_SERVER_ON_NOOP);
4203 	REGISTER_MEMC_CLASS_CONST_LONG(ON_PREPEND,   MEMC_SERVER_ON_PREPEND);
4204 	REGISTER_MEMC_CLASS_CONST_LONG(ON_QUIT,      MEMC_SERVER_ON_QUIT);
4205 	REGISTER_MEMC_CLASS_CONST_LONG(ON_REPLACE,   MEMC_SERVER_ON_REPLACE);
4206 	REGISTER_MEMC_CLASS_CONST_LONG(ON_SET,       MEMC_SERVER_ON_SET);
4207 	REGISTER_MEMC_CLASS_CONST_LONG(ON_STAT,      MEMC_SERVER_ON_STAT);
4208 	REGISTER_MEMC_CLASS_CONST_LONG(ON_VERSION,   MEMC_SERVER_ON_VERSION);
4209 
4210 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_SUCCESS,         PROTOCOL_BINARY_RESPONSE_SUCCESS);
4211 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_KEY_ENOENT,      PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
4212 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_KEY_EEXISTS,     PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
4213 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_E2BIG,           PROTOCOL_BINARY_RESPONSE_E2BIG);
4214 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_EINVAL,          PROTOCOL_BINARY_RESPONSE_EINVAL);
4215 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_NOT_STORED,      PROTOCOL_BINARY_RESPONSE_NOT_STORED);
4216 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_DELTA_BADVAL,    PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL);
4217 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_NOT_MY_VBUCKET,  PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET);
4218 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_AUTH_ERROR,      PROTOCOL_BINARY_RESPONSE_AUTH_ERROR);
4219 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_AUTH_CONTINUE,   PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE);
4220 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_UNKNOWN_COMMAND, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND);
4221 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_ENOMEM,          PROTOCOL_BINARY_RESPONSE_ENOMEM);
4222 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_NOT_SUPPORTED,   PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED);
4223 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_EINTERNAL,       PROTOCOL_BINARY_RESPONSE_EINTERNAL);
4224 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_EBUSY,           PROTOCOL_BINARY_RESPONSE_EBUSY);
4225 	REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_ETMPFAIL,        PROTOCOL_BINARY_RESPONSE_ETMPFAIL);
4226 #endif
4227 
4228 	/*
4229 	 * Return value from simple get errors
4230 	 */
4231 	REGISTER_MEMC_CLASS_CONST_BOOL(GET_ERROR_RETURN_VALUE, 0);
4232 
4233 	#undef REGISTER_MEMC_CLASS_CONST_LONG
4234 	#undef REGISTER_MEMC_CLASS_CONST_BOOL
4235 	#undef REGISTER_MEMC_CLASS_CONST_NULL
4236 
4237 }
4238 /* }}} */
4239 
4240 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(memcached)4241 PHP_MINIT_FUNCTION(memcached)
4242 {
4243 	zend_class_entry ce;
4244 
4245 	memcpy(&memcached_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
4246 	memcached_object_handlers.offset    = XtOffsetOf(php_memc_object_t, zo);
4247 	memcached_object_handlers.clone_obj = NULL;
4248 	memcached_object_handlers.free_obj  = php_memc_object_free_storage;
4249 
4250 	le_memc = zend_register_list_destructors_ex(NULL, php_memc_dtor, "Memcached persistent connection", module_number);
4251 
4252 	INIT_CLASS_ENTRY(ce, "Memcached", class_Memcached_methods);
4253 	memcached_ce = zend_register_internal_class(&ce);
4254 	memcached_ce->create_object = php_memc_object_new;
4255 
4256 #ifdef HAVE_MEMCACHED_PROTOCOL
4257 	memcpy(&memcached_server_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
4258 	memcached_server_object_handlers.offset = XtOffsetOf(php_memc_server_t, zo);
4259 	memcached_server_object_handlers.clone_obj = NULL;
4260 	memcached_server_object_handlers.free_obj = php_memc_server_free_storage;
4261 
4262 	INIT_CLASS_ENTRY(ce, "MemcachedServer", class_MemcachedServer_methods);
4263 	memcached_server_ce = zend_register_internal_class(&ce);
4264 	memcached_server_ce->create_object = php_memc_server_new;
4265 #endif
4266 
4267 	INIT_CLASS_ENTRY(ce, "MemcachedException", NULL);
4268 	memcached_exception_ce = zend_register_internal_class_ex(&ce, php_memc_get_exception_base(0));
4269 	/* TODO
4270 	 * possibly declare custom exception property here
4271 	 */
4272 
4273 	php_memc_register_constants(INIT_FUNC_ARGS_PASSTHRU);
4274 	REGISTER_INI_ENTRIES();
4275 
4276 #ifdef HAVE_MEMCACHED_SESSION
4277 	php_memc_session_minit(module_number);
4278 #endif
4279 	return SUCCESS;
4280 }
4281 /* }}} */
4282 
4283 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(memcached)4284 PHP_MSHUTDOWN_FUNCTION(memcached)
4285 {
4286 #ifdef HAVE_MEMCACHED_SASL
4287 	if (MEMC_G(sasl_initialised)) {
4288 		sasl_done();
4289 	}
4290 #endif
4291 
4292 	UNREGISTER_INI_ENTRIES();
4293 	return SUCCESS;
4294 }
4295 /* }}} */
4296 
4297 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(memcached)4298 PHP_MINFO_FUNCTION(memcached)
4299 {
4300 	php_info_print_table_start();
4301 	php_info_print_table_header(2, "memcached support", "enabled");
4302 	php_info_print_table_row(2, "Version", PHP_MEMCACHED_VERSION);
4303 	php_info_print_table_row(2, "libmemcached version", memcached_lib_version());
4304 
4305 #ifdef HAVE_MEMCACHED_SASL
4306 	php_info_print_table_row(2, "SASL support", "yes");
4307 #else
4308 	php_info_print_table_row(2, "SASL support", "no");
4309 #endif
4310 
4311 #ifdef HAVE_MEMCACHED_SESSION
4312 	php_info_print_table_row(2, "Session support", "yes");
4313 #else
4314 	php_info_print_table_row(2, "Session support ", "no");
4315 #endif
4316 
4317 #ifdef HAVE_MEMCACHED_IGBINARY
4318 	php_info_print_table_row(2, "igbinary support", "yes");
4319 #else
4320 	php_info_print_table_row(2, "igbinary support", "no");
4321 #endif
4322 
4323 #ifdef HAVE_JSON_API
4324 	php_info_print_table_row(2, "json support", "yes");
4325 #else
4326 	php_info_print_table_row(2, "json support", "no");
4327 #endif
4328 
4329 #ifdef HAVE_MEMCACHED_MSGPACK
4330 	php_info_print_table_row(2, "msgpack support", "yes");
4331 #else
4332 	php_info_print_table_row(2, "msgpack support", "no");
4333 #endif
4334 
4335 	php_info_print_table_end();
4336 
4337 	DISPLAY_INI_ENTRIES();
4338 }
4339 /* }}} */
4340 
4341 
4342 /*
4343  * Local variables:
4344  * tab-width: 4
4345  * c-basic-offset: 4
4346  * End:
4347  * vim: noet sw=4 ts=4 fdm=marker:
4348  */
4349