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(¶ms[0], object);
780 if (persistent_id) {
781 ZVAL_STR(¶ms[1], zend_string_copy(persistent_id));
782 } else {
783 ZVAL_NULL(¶ms[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(¶ms[0]);
798 zval_ptr_dtor(¶ms[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(¶ms[0], zobject);
814 ZVAL_STR_COPY(¶ms[1], key); /* key */
815 ZVAL_NEW_REF(¶ms[2], value); /* value */
816
817 if (with_cas) {
818 fci->param_count = 3;
819 } else {
820 ZVAL_NEW_EMPTY_REF(¶ms[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(¶ms[0]);
857 zval_ptr_dtor(¶ms[1]);
858 zval_ptr_dtor(¶ms[2]);
859 if (!with_cas) {
860 zval_ptr_dtor(¶ms[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(¶ms[0], context->object);
1672 array_init(¶ms[1]);
1673
1674 s_create_result_array(key, value, cas, flags, ¶ms[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(¶ms[0]);
1688 zval_ptr_dtor(¶ms[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