Permalink
Fetching contributors…
Cannot retrieve contributors at this time
4689 lines (3888 sloc) 133 KB
/*
+----------------------------------------------------------------------+
| Copyright (c) 2009-2010 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andrei Zmievski <andrei@php.net> |
+----------------------------------------------------------------------+
*/
/* TODO
* - set LIBKETAMA_COMPATIBLE as the default?
* - fix unserialize(serialize($memc))
*/
#include "php_memcached.h"
#include "php_memcached_private.h"
#include "php_memcached_server.h"
#include "g_fmt.h"
#include <ctype.h>
#include <limits.h>
#ifdef HAVE_MEMCACHED_SESSION
# include "php_memcached_session.h"
#endif
#ifdef HAVE_FASTLZ_H
#include <fastlz.h>
#else
#include "fastlz/fastlz.h"
#endif
#include <zlib.h>
#ifdef HAVE_JSON_API
# include "ext/json/php_json.h"
#endif
#ifdef HAVE_MEMCACHED_IGBINARY
#ifdef PHP_WIN32
//Windows extensions are generally built together,
//so it wont be in the installed location
#include "igbinary.h"
#else
# include "ext/igbinary/igbinary.h"
#endif
#endif
#ifdef HAVE_MEMCACHED_MSGPACK
# include "ext/msgpack/php_msgpack.h"
#endif
static int le_memc;
static int php_memc_list_entry(void) {
return le_memc;
}
/****************************************
Protocol parameters
****************************************/
#define MEMC_OBJECT_KEY_MAX_LENGTH 250
/****************************************
Custom options
****************************************/
#define MEMC_OPT_COMPRESSION -1001
#define MEMC_OPT_PREFIX_KEY -1002
#define MEMC_OPT_SERIALIZER -1003
#define MEMC_OPT_COMPRESSION_TYPE -1004
#define MEMC_OPT_STORE_RETRY_COUNT -1005
#define MEMC_OPT_USER_FLAGS -1006
/****************************************
Custom result codes
****************************************/
#define MEMC_RES_PAYLOAD_FAILURE -1001
/****************************************
Payload value flags
****************************************/
#define MEMC_CREATE_MASK(start, n_bits) (((1 << n_bits) - 1) << start)
#define MEMC_MASK_TYPE MEMC_CREATE_MASK(0, 4)
#define MEMC_MASK_INTERNAL MEMC_CREATE_MASK(4, 12)
#define MEMC_MASK_USER MEMC_CREATE_MASK(16, 16)
#define MEMC_VAL_GET_TYPE(flags) ((flags) & MEMC_MASK_TYPE)
#define MEMC_VAL_SET_TYPE(flags, type) ((flags) |= ((type) & MEMC_MASK_TYPE))
#define MEMC_VAL_IS_STRING 0
#define MEMC_VAL_IS_LONG 1
#define MEMC_VAL_IS_DOUBLE 2
#define MEMC_VAL_IS_BOOL 3
#define MEMC_VAL_IS_SERIALIZED 4
#define MEMC_VAL_IS_IGBINARY 5
#define MEMC_VAL_IS_JSON 6
#define MEMC_VAL_IS_MSGPACK 7
#define MEMC_VAL_COMPRESSED (1<<0)
#define MEMC_VAL_COMPRESSION_ZLIB (1<<1)
#define MEMC_VAL_COMPRESSION_FASTLZ (1<<2)
#define MEMC_VAL_GET_FLAGS(internal_flags) (((internal_flags) & MEMC_MASK_INTERNAL) >> 4)
#define MEMC_VAL_SET_FLAG(internal_flags, internal_flag) ((internal_flags) |= (((internal_flag) << 4) & MEMC_MASK_INTERNAL))
#define MEMC_VAL_HAS_FLAG(internal_flags, internal_flag) ((MEMC_VAL_GET_FLAGS(internal_flags) & (internal_flag)) == (internal_flag))
#define MEMC_VAL_DEL_FLAG(internal_flags, internal_flag) (internal_flags &= (~(((internal_flag) << 4) & MEMC_MASK_INTERNAL)))
/****************************************
User-defined flags
****************************************/
#define MEMC_VAL_GET_USER_FLAGS(flags) ((flags & MEMC_MASK_USER) >> 16)
#define MEMC_VAL_SET_USER_FLAGS(flags, udf_flags) ((flags) |= ((udf_flags << 16) & MEMC_MASK_USER))
#define MEMC_VAL_USER_FLAGS_MAX ((1 << 16) - 1)
/****************************************
"get" operation flags
****************************************/
#define MEMC_GET_PRESERVE_ORDER 1
#define MEMC_GET_EXTENDED 2
/****************************************
Helper macros
****************************************/
#define RETURN_FROM_GET RETURN_FALSE
/****************************************
Structures and definitions
****************************************/
typedef enum {
MEMC_OP_SET,
MEMC_OP_TOUCH,
MEMC_OP_ADD,
MEMC_OP_REPLACE,
MEMC_OP_APPEND,
MEMC_OP_PREPEND
} php_memc_write_op;
typedef struct {
zend_bool is_persistent;
zend_bool compression_enabled;
zend_bool encoding_enabled;
zend_long serializer;
zend_long compression_type;
zend_long store_retry_count;
zend_long set_udf_flags;
#ifdef HAVE_MEMCACHED_SASL
zend_bool has_sasl_data;
#endif
} php_memc_user_data_t;
typedef struct {
memcached_st *memc;
zend_bool is_pristine;
int rescode;
int memc_errno;
zend_object zo;
} php_memc_object_t;
typedef struct {
size_t num_valid_keys;
const char **mkeys;
size_t *mkeys_len;
zend_string **strings;
} php_memc_keys_t;
typedef struct {
zval *object;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
} php_memc_result_callback_ctx_t;
static inline php_memc_object_t *php_memc_fetch_object(zend_object *obj) {
return (php_memc_object_t *)((char *)obj - XtOffsetOf(php_memc_object_t, zo));
}
#define Z_MEMC_OBJ_P(zv) php_memc_fetch_object(Z_OBJ_P(zv));
#define MEMC_METHOD_INIT_VARS \
zval* object = getThis(); \
php_memc_object_t* intern = NULL; \
php_memc_user_data_t* memc_user_data = NULL;
#define MEMC_METHOD_FETCH_OBJECT \
intern = Z_MEMC_OBJ_P(object); \
if (!intern->memc) { \
php_error_docref(NULL, E_WARNING, "Memcached constructor was not called"); \
return; \
} \
memc_user_data = (php_memc_user_data_t *) memcached_get_user_data(intern->memc); \
(void)memc_user_data; /* avoid unused variable warning */
static
zend_bool s_memc_valid_key_binary(zend_string *key)
{
return memchr(ZSTR_VAL(key), '\n', ZSTR_LEN(key)) == NULL;
}
static
zend_bool s_memc_valid_key_ascii(zend_string *key)
{
const char *str = ZSTR_VAL(key);
size_t i, len = ZSTR_LEN(key);
for (i = 0; i < len; i++) {
if (iscntrl(str[i]) || isspace(str[i]))
return 0;
}
return 1;
}
#define MEMC_CHECK_KEY(intern, key) \
if (UNEXPECTED(ZSTR_LEN(key) == 0 || \
ZSTR_LEN(key) > MEMC_OBJECT_KEY_MAX_LENGTH || \
(memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) \
? !s_memc_valid_key_binary(key) \
: !s_memc_valid_key_ascii(key) \
))) { \
intern->rescode = MEMCACHED_BAD_KEY_PROVIDED; \
RETURN_FALSE; \
}
#ifdef HAVE_MEMCACHED_PROTOCOL
typedef struct {
php_memc_proto_handler_t *handler;
zend_object zo;
} php_memc_server_t;
static inline php_memc_server_t *php_memc_server_fetch_object(zend_object *obj) {
return (php_memc_server_t *)((char *)obj - XtOffsetOf(php_memc_server_t, zo));
}
#define Z_MEMC_SERVER_P(zv) php_memc_server_fetch_object(Z_OBJ_P(zv))
static zend_object_handlers memcached_server_object_handlers;
static zend_class_entry *memcached_server_ce = NULL;
#endif
static zend_class_entry *memcached_ce = NULL;
static zend_class_entry *memcached_exception_ce = NULL;
static zend_object_handlers memcached_object_handlers;
#ifdef HAVE_SPL
static zend_class_entry *spl_ce_RuntimeException = NULL;
#endif
ZEND_DECLARE_MODULE_GLOBALS(php_memcached)
#ifdef COMPILE_DL_MEMCACHED
ZEND_GET_MODULE(memcached)
#endif
static PHP_INI_MH(OnUpdateCompressionType)
{
if (!new_value) {
MEMC_G(compression_type) = COMPRESSION_TYPE_FASTLZ;
} else if (!strcmp(ZSTR_VAL(new_value), "fastlz")) {
MEMC_G(compression_type) = COMPRESSION_TYPE_FASTLZ;
} else if (!strcmp(ZSTR_VAL(new_value), "zlib")) {
MEMC_G(compression_type) = COMPRESSION_TYPE_ZLIB;
} else {
return FAILURE;
}
return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
static PHP_INI_MH(OnUpdateSerializer)
{
if (!new_value) {
MEMC_G(serializer_type) = SERIALIZER_DEFAULT;
} else if (!strcmp(ZSTR_VAL(new_value), "php")) {
MEMC_G(serializer_type) = SERIALIZER_PHP;
#ifdef HAVE_MEMCACHED_IGBINARY
} else if (!strcmp(ZSTR_VAL(new_value), "igbinary")) {
MEMC_G(serializer_type) = SERIALIZER_IGBINARY;
#endif // IGBINARY
#ifdef HAVE_JSON_API
} else if (!strcmp(ZSTR_VAL(new_value), "json")) {
MEMC_G(serializer_type) = SERIALIZER_JSON;
} else if (!strcmp(ZSTR_VAL(new_value), "json_array")) {
MEMC_G(serializer_type) = SERIALIZER_JSON_ARRAY;
#endif // JSON
#ifdef HAVE_MEMCACHED_MSGPACK
} else if (!strcmp(ZSTR_VAL(new_value), "msgpack")) {
MEMC_G(serializer_type) = SERIALIZER_MSGPACK;
#endif // msgpack
} else {
return FAILURE;
}
return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
static
PHP_INI_MH(OnUpdateDeprecatedLockValue)
{
if (ZSTR_LEN(new_value) > 0 && strcmp(ZSTR_VAL(new_value), "not set")) {
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");
}
return FAILURE;
}
static
PHP_INI_MH(OnUpdateSessionPrefixString)
{
if (new_value && ZSTR_LEN(new_value) > 0) {
if (ZSTR_LEN(new_value) > MEMCACHED_MAX_KEY) {
php_error_docref(NULL, E_WARNING, "memcached.sess_prefix too long (max: %d)", MEMCACHED_MAX_KEY - 1);
return FAILURE;
}
if (!s_memc_valid_key_ascii(new_value)) {
php_error_docref(NULL, E_WARNING, "memcached.sess_prefix cannot contain whitespace or control characters");
return FAILURE;
}
}
return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
static
PHP_INI_MH(OnUpdateConsistentHash)
{
if (!new_value) {
MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA;
} else if (!strcmp(ZSTR_VAL(new_value), "ketama")) {
MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA;
} else if (!strcmp(ZSTR_VAL(new_value), "ketama_weighted")) {
MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED;
} else {
php_error_docref(NULL, E_WARNING, "memcached.sess_consistent_hash_type must be ketama or ketama_weighted");
return FAILURE;
}
return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
#define MEMC_INI_ENTRY(key, default_value, update_fn, gkey) \
STD_PHP_INI_ENTRY("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals)
#define MEMC_INI_BOOL(key, default_value, update_fn, gkey) \
STD_PHP_INI_BOOLEAN("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals)
#define MEMC_INI_LINK(key, default_value, update_fn, gkey) \
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)
#define MEMC_SESSION_INI_ENTRY(key, default_value, update_fn, gkey) \
STD_PHP_INI_ENTRY("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals)
#define MEMC_SESSION_INI_BOOL(key, default_value, update_fn, gkey) \
STD_PHP_INI_BOOLEAN("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals)
#define MEMC_SESSION_INI_LINK(key, default_value, update_fn, gkey) \
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)
/* {{{ INI entries */
PHP_INI_BEGIN()
#ifdef HAVE_MEMCACHED_SESSION
MEMC_SESSION_INI_BOOL ("locking", "1", OnUpdateBool, lock_enabled)
MEMC_SESSION_INI_ENTRY("lock_wait_min", "150", OnUpdateLongGEZero, lock_wait_min)
MEMC_SESSION_INI_ENTRY("lock_wait_max", "150", OnUpdateLongGEZero, lock_wait_max)
MEMC_SESSION_INI_ENTRY("lock_retries", "5", OnUpdateLong, lock_retries)
MEMC_SESSION_INI_ENTRY("lock_expire", "0", OnUpdateLongGEZero, lock_expiration)
#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018
MEMC_SESSION_INI_BOOL ("binary_protocol", "0", OnUpdateBool, binary_protocol_enabled)
#else
MEMC_SESSION_INI_BOOL ("binary_protocol", "1", OnUpdateBool, binary_protocol_enabled)
#endif
MEMC_SESSION_INI_BOOL ("consistent_hash", "1", OnUpdateBool, consistent_hash_enabled)
MEMC_SESSION_INI_ENTRY("consistent_hash_type", "ketama", OnUpdateConsistentHash, consistent_hash_name)
MEMC_SESSION_INI_ENTRY("number_of_replicas", "0", OnUpdateLongGEZero, number_of_replicas)
MEMC_SESSION_INI_BOOL ("randomize_replica_read", "0", OnUpdateBool, randomize_replica_read_enabled)
MEMC_SESSION_INI_BOOL ("remove_failed_servers", "0", OnUpdateBool, remove_failed_servers_enabled)
MEMC_SESSION_INI_ENTRY("server_failure_limit", "0", OnUpdateLongGEZero, server_failure_limit)
MEMC_SESSION_INI_LINK ("connect_timeout", "0", OnUpdateLong, connect_timeout)
MEMC_SESSION_INI_ENTRY("sasl_username", "", OnUpdateString, sasl_username)
MEMC_SESSION_INI_ENTRY("sasl_password", "", OnUpdateString, sasl_password)
MEMC_SESSION_INI_BOOL ("persistent", "0", OnUpdateBool, persistent_enabled)
MEMC_SESSION_INI_ENTRY("prefix", "memc.sess.key.", OnUpdateSessionPrefixString, prefix)
/* Deprecated */
STD_PHP_INI_ENTRY("memcached.sess_lock_wait", "not set", PHP_INI_ALL, OnUpdateDeprecatedLockValue, no_effect, zend_php_memcached_globals, php_memcached_globals)
STD_PHP_INI_ENTRY("memcached.sess_lock_max_wait", "not set", PHP_INI_ALL, OnUpdateDeprecatedLockValue, no_effect, zend_php_memcached_globals, php_memcached_globals)
#endif
MEMC_INI_ENTRY("compression_type", "fastlz", OnUpdateCompressionType, compression_name)
MEMC_INI_ENTRY("compression_factor", "1.3", OnUpdateReal, compression_factor)
MEMC_INI_ENTRY("compression_threshold", "2000", OnUpdateLong, compression_threshold)
MEMC_INI_ENTRY("serializer", SERIALIZER_DEFAULT_NAME, OnUpdateSerializer, serializer_name)
MEMC_INI_ENTRY("store_retry_count", "2", OnUpdateLong, store_retry_count)
MEMC_INI_BOOL ("default_consistent_hash", "0", OnUpdateBool, default_behavior.consistent_hash_enabled)
MEMC_INI_BOOL ("default_binary_protocol", "0", OnUpdateBool, default_behavior.binary_protocol_enabled)
MEMC_INI_LINK ("default_connect_timeout", "0", OnUpdateLong, default_behavior.connect_timeout)
PHP_INI_END()
/* }}} */
#undef MEMC_INI_BOOL
#undef MEMC_INI_LINK
#undef MEMC_INI_ENTRY
#undef MEMC_SESSION_INI_BOOL
#undef MEMC_SESSION_INI_LINK
#undef MEMC_SESSION_INI_ENTRY
/****************************************
Forward declarations
****************************************/
static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key);
static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key);
/* Invoke PHP functions */
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);
/* Iterate result sets */
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);
static
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);
static
zend_bool 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);
/* Callback functions for different server list iterations */
static
memcached_return s_server_cursor_list_servers_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context);
static
memcached_return s_server_cursor_version_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context);
static
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);
static
void php_memc_destroy(memcached_st *memc, php_memc_user_data_t *memc_user_data);
static
zend_bool s_memcached_result_to_zval(memcached_st *memc, memcached_result_st *result, zval *return_value);
static
zend_string *s_zval_to_payload(php_memc_object_t *intern, zval *value, uint32_t *flags);
static
void s_hash_to_keys(php_memc_keys_t *keys_out, HashTable *hash_in, zend_bool preserve_order, zval *return_value);
static
void s_clear_keys(php_memc_keys_t *keys);
/****************************************
Exported helper functions
****************************************/
zend_bool php_memc_init_sasl_if_needed()
{
#ifdef HAVE_MEMCACHED_SASL
if (MEMC_G(sasl_initialised)) {
return 1;
}
if (sasl_client_init(NULL) != SASL_OK) {
php_error_docref(NULL, E_ERROR, "Failed to initialize SASL library");
return 0;
}
return 1;
#else
php_error_docref(NULL, E_ERROR, "Memcached not built with sasl support");
return 0;
#endif
}
char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache)
{
char *buffer = NULL;
if (fci->object) {
spprintf (&buffer, 0, "%s::%s", ZSTR_VAL(fci->object->ce->name), fci_cache->function_handler->common.function_name);
} else {
if (Z_TYPE (fci->function_name) == IS_OBJECT) {
spprintf (&buffer, 0, "%s", ZSTR_VAL(Z_OBJCE(fci->function_name)->name));
}
else {
spprintf (&buffer, 0, "%s", Z_STRVAL(fci->function_name));
}
}
return buffer;
}
/****************************************
Handling error codes
****************************************/
static
zend_bool s_memcached_return_is_error(memcached_return status, zend_bool strict)
{
zend_bool result = 0;
switch (status) {
case MEMCACHED_SUCCESS:
case MEMCACHED_STORED:
case MEMCACHED_DELETED:
case MEMCACHED_STAT:
case MEMCACHED_END:
case MEMCACHED_BUFFERED:
result = 0;
break;
case MEMCACHED_SOME_ERRORS:
result = strict;
break;
default:
result = 1;
break;
}
return result;
}
static
int s_memc_status_handle_result_code(php_memc_object_t *intern, memcached_return status)
{
intern->rescode = status;
intern->memc_errno = 0;
if (s_memcached_return_is_error(status, 1)) {
intern->memc_errno = memcached_last_error_errno(intern->memc);
return FAILURE;
}
return SUCCESS;
}
static
void s_memc_set_status(php_memc_object_t *intern, memcached_return status, int memc_errno)
{
intern->rescode = status;
intern->memc_errno = memc_errno;
}
static
zend_bool s_memc_status_has_error(php_memc_object_t *intern)
{
return s_memcached_return_is_error(intern->rescode, 1);
}
static
zend_bool s_memc_status_has_result_code(php_memc_object_t *intern, memcached_return status)
{
return intern->rescode == status;
}
/****************************************
Marshall cas tokens
****************************************/
static
void s_uint64_to_zval (zval *target, uint64_t value)
{
if (value >= ((uint64_t) LONG_MAX)) {
zend_string *buffer;
#ifdef PRIu64
buffer = strpprintf (0, "%" PRIu64, value);
#else
/* Best effort */
buffer = strpprintf (0, "%llu", value);
#endif
ZVAL_STR(target, buffer);
}
else {
ZVAL_LONG (target, (zend_long) value);
}
}
static
uint64_t s_zval_to_uint64 (zval *cas)
{
switch (Z_TYPE_P (cas)) {
case IS_LONG:
return (uint64_t) zval_get_long (cas);
break;
case IS_DOUBLE:
if (Z_DVAL_P (cas) < 0.0)
return 0;
return (uint64_t) zval_get_double (cas);
break;
case IS_STRING:
{
uint64_t val;
char *end;
errno = 0;
val = (uint64_t) strtoull (Z_STRVAL_P (cas), &end, 0);
if (*end || (errno == ERANGE && val == UINT64_MAX) || (errno != 0 && val == 0)) {
php_error_docref(NULL, E_ERROR, "Failed to unmarshall cas token");
return 0;
}
return val;
}
break;
}
return 0;
}
/****************************************
Iterate over memcached results and mget
****************************************/
static
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)
{
memcached_result_st result, *result_ptr;
memcached_return rc, status = MEMCACHED_SUCCESS;
memcached_result_create(intern->memc, &result);
do {
result_ptr = memcached_fetch_result(intern->memc, &result, &rc);
if (s_memcached_return_is_error(rc, 0)) {
status = rc;
}
/* nothing returned */
if (!result_ptr) {
break;
}
else {
zend_string *key;
zval val, zcas;
zend_bool retval;
uint64_t cas;
uint32_t flags;
const char *res_key;
size_t res_key_len;
if (!s_memcached_result_to_zval(intern->memc, &result, &val)) {
if (EG(exception)) {
status = MEMC_RES_PAYLOAD_FAILURE;
memcached_quit(intern->memc);
break;
}
status = MEMCACHED_SOME_ERRORS;
continue;
}
res_key = memcached_result_key_value(&result);
res_key_len = memcached_result_key_length(&result);
cas = memcached_result_cas(&result);
flags = memcached_result_flags(&result);
s_uint64_to_zval(&zcas, cas);
key = zend_string_init (res_key, res_key_len, 0);
retval = result_apply_fn(intern, key, &val, &zcas, flags, context);
zend_string_release(key);
zval_ptr_dtor(&val);
zval_ptr_dtor(&zcas);
/* Stop iterating on false */
if (!retval) {
if (!fetch_delay) {
/* Make sure we clear our results */
while (memcached_fetch_result(intern->memc, &result, &rc)) {}
}
break;
}
}
} while (result_ptr != NULL);
memcached_result_free(&result);
return status;
}
static
zend_bool 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)
{
memcached_return status;
int mget_status;
uint64_t orig_cas_flag = 0;
// Reset status code
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
if (!keys->num_valid_keys) {
intern->rescode = MEMCACHED_BAD_KEY_PROVIDED;
return 0;
}
if (with_cas) {
orig_cas_flag = memcached_behavior_get (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS);
if (!orig_cas_flag) {
memcached_behavior_set (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1);
}
}
if (server_key) {
status = memcached_mget_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), keys->mkeys, keys->mkeys_len, keys->num_valid_keys);
} else {
status = memcached_mget(intern->memc, keys->mkeys, keys->mkeys_len, keys->num_valid_keys);
}
/* Need to handle result code before restoring cas flags, would mess up errno */
mget_status = s_memc_status_handle_result_code(intern, status);
if (with_cas && !orig_cas_flag) {
memcached_behavior_set (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag);
}
/* Return on failure codes */
if (mget_status == FAILURE) {
return 0;
}
if (!result_apply_fn) {
/* no callback, for example getDelayed */
return 1;
}
status = php_memc_result_apply(intern, result_apply_fn, 0, context);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
return 0;
}
return 1;
}
/****************************************
Invoke callbacks
****************************************/
static
zend_bool s_invoke_new_instance_cb(zval *object, zend_fcall_info *fci, zend_fcall_info_cache *fci_cache, zend_string *persistent_id)
{
zend_bool ret = 1;
zval retval;
zval params[2];
ZVAL_COPY(&params[0], object);
if (persistent_id) {
ZVAL_STR(&params[1], zend_string_copy(persistent_id));
} else {
ZVAL_NULL(&params[1]);
}
fci->retval = &retval;
fci->params = params;
fci->param_count = 2;
if (zend_call_function(fci, fci_cache) == FAILURE) {
char *buf = php_memc_printable_func (fci, fci_cache);
php_error_docref(NULL, E_WARNING, "Failed to invoke 'on_new' callback %s()", buf);
efree (buf);
ret = 0;
}
zval_ptr_dtor(&params[0]);
zval_ptr_dtor(&params[1]);
zval_ptr_dtor(&retval);
return ret;
}
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)
{
zend_bool status = 0;
zval params[4];
zval retval;
php_memc_object_t *intern = Z_MEMC_OBJ_P(zobject);
/* Prepare params */
ZVAL_COPY(&params[0], zobject);
ZVAL_STR_COPY(&params[1], key); /* key */
ZVAL_NEW_REF(&params[2], value); /* value */
if (with_cas) {
fci->param_count = 3;
} else {
ZVAL_NEW_EMPTY_REF(&params[3]); /* expiration */
ZVAL_NULL(Z_REFVAL(params[3]));
fci->param_count = 4;
}
fci->retval = &retval;
fci->params = params;
if (zend_call_function(fci, fcc) == SUCCESS) {
if (zend_is_true(&retval)) {
time_t expiration;
zval *val = Z_REFVAL(params[2]);
if (with_cas) {
if (Z_TYPE_P(val) == IS_ARRAY) {
zval *rv = zend_hash_str_find(Z_ARRVAL_P(val), "value", sizeof("value") - 1);
if (rv) {
zval *cas = zend_hash_str_find(Z_ARRVAL_P(val), "cas", sizeof("cas") -1);
expiration = cas? Z_LVAL_P(cas) : 0;
status = s_memc_write_zval (intern, MEMC_OP_SET, NULL, key, rv, expiration);
}
/* memleak? zval_ptr_dtor(value); */
ZVAL_COPY(value, val);
}
} else {
expiration = zval_get_long(Z_REFVAL(params[3]));
status = s_memc_write_zval (intern, MEMC_OP_SET, NULL, key, val, expiration);
/* memleak? zval_ptr_dtor(value); */
ZVAL_COPY(value, val);
}
}
}
else {
s_memc_set_status(intern, MEMCACHED_NOTFOUND, 0);
}
zval_ptr_dtor(&params[0]);
zval_ptr_dtor(&params[1]);
zval_ptr_dtor(&params[2]);
if (!with_cas) {
zval_ptr_dtor(&params[3]);
}
zval_ptr_dtor(&retval);
return status;
}
/****************************************
Wrapper for setting from zval
****************************************/
static
zend_bool s_compress_value (php_memc_compression_type compression_type, zend_string **payload_in, uint32_t *flags)
{
/* status */
zend_bool compress_status = 0;
zend_string *payload = *payload_in;
uint32_t compression_type_flag = 0;
/* Additional 5% for the data */
size_t buffer_size = (size_t) (((double) ZSTR_LEN(payload) * 1.05) + 1.0);
char *buffer = emalloc(buffer_size);
/* Store compressed size here */
size_t compressed_size = 0;
uint32_t original_size = ZSTR_LEN(payload);
switch (compression_type) {
case COMPRESSION_TYPE_FASTLZ:
{
compressed_size = fastlz_compress(ZSTR_VAL(payload), ZSTR_LEN(payload), buffer);
if (compressed_size > 0) {
compress_status = 1;
compression_type_flag = MEMC_VAL_COMPRESSION_FASTLZ;
}
}
break;
case COMPRESSION_TYPE_ZLIB:
{
compressed_size = buffer_size;
int status = compress((Bytef *) buffer, &compressed_size, (Bytef *) ZSTR_VAL(payload), ZSTR_LEN(payload));
if (status == Z_OK) {
compress_status = 1;
compression_type_flag = MEMC_VAL_COMPRESSION_ZLIB;
}
}
break;
default:
compress_status = 0;
break;
}
/* This means the value was too small to be compressed and ended up larger */
if (ZSTR_LEN(payload) <= (compressed_size * MEMC_G(compression_factor))) {
compress_status = 0;
}
/* Replace the payload with the compressed copy */
if (compress_status) {
MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSED | compression_type_flag);
payload = zend_string_realloc(payload, compressed_size + sizeof(uint32_t), 0);
/* Copy the uin32_t at the beginning */
memcpy(ZSTR_VAL(payload), &original_size, sizeof(uint32_t));
memcpy(ZSTR_VAL(payload) + sizeof (uint32_t), buffer, compressed_size);
efree(buffer);
zend_string_forget_hash_val(payload);
*payload_in = payload;
return 1;
}
/* Original payload was not modified */
efree(buffer);
return 0;
}
static
zend_bool s_serialize_value (php_memc_serializer_type serializer, zval *value, smart_str *buf, uint32_t *flags)
{
switch (serializer) {
/*
Igbinary serialization
*/
#ifdef HAVE_MEMCACHED_IGBINARY
case SERIALIZER_IGBINARY:
{
uint8_t *buffer;
size_t buffer_len;
if (igbinary_serialize(&buffer, &buffer_len, value) != 0) {
php_error_docref(NULL, E_WARNING, "could not serialize value with igbinary");
return 0;
}
smart_str_appendl (buf, (char *)buffer, buffer_len);
efree(buffer);
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_IGBINARY);
}
break;
#endif
/*
JSON serialization
*/
#ifdef HAVE_JSON_API
case SERIALIZER_JSON:
case SERIALIZER_JSON_ARRAY:
{
php_json_encode(buf, value, 0);
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_JSON);
}
break;
#endif
/*
msgpack serialization
*/
#ifdef HAVE_MEMCACHED_MSGPACK
case SERIALIZER_MSGPACK:
php_msgpack_serialize(buf, value);
if (!buf->s) {
php_error_docref(NULL, E_WARNING, "could not serialize value with msgpack");
return 0;
}
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_MSGPACK);
break;
#endif
/*
PHP serialization
*/
default:
{
php_serialize_data_t var_hash;
PHP_VAR_SERIALIZE_INIT(var_hash);
php_var_serialize(buf, value, &var_hash);
PHP_VAR_SERIALIZE_DESTROY(var_hash);
if (!buf->s) {
php_error_docref(NULL, E_WARNING, "could not serialize value");
return 0;
}
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_SERIALIZED);
}
break;
}
/* Check for exceptions caused by serializers */
if (EG(exception) && buf->s->len) {
return 0;
}
return 1;
}
static
zend_string *s_zval_to_payload(php_memc_object_t *intern, zval *value, uint32_t *flags)
{
zend_string *payload;
php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
zend_bool should_compress = memc_user_data->compression_enabled;
switch (Z_TYPE_P(value)) {
case IS_STRING:
payload = zval_get_string(value);
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_STRING);
break;
case IS_LONG:
{
smart_str buffer = {0};
smart_str_append_long (&buffer, Z_LVAL_P(value));
smart_str_0(&buffer);
payload = buffer.s;
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_LONG);
should_compress = 0;
}
break;
case IS_DOUBLE:
{
char buffer[40];
php_memcached_g_fmt(buffer, Z_DVAL_P(value));
payload = zend_string_init (buffer, strlen (buffer), 0);
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_DOUBLE);
should_compress = 0;
}
break;
case IS_TRUE:
payload = zend_string_init ("1", 1, 0);
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL);
should_compress = 0;
break;
case IS_FALSE:
payload = zend_string_alloc (0, 0);
MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL);
should_compress = 0;
break;
default:
{
smart_str buffer = {0};
if (!s_serialize_value (memc_user_data->serializer, value, &buffer, flags)) {
smart_str_free(&buffer);
return NULL;
}
payload = buffer.s;
}
break;
}
/* turn off compression for values below the threshold */
if (ZSTR_LEN(payload) == 0 || ZSTR_LEN(payload) < MEMC_G(compression_threshold)) {
should_compress = 0;
}
/* If we have compression flag, compress the value */
if (should_compress) {
/* s_compress_value() will always leave a valid payload, even if that payload
* did not actually get compressed. The flags will be set according to the
* to the compression type or no compression.
*
* No need to check the return value because the payload is always valid.
*/
(void)s_compress_value (memc_user_data->compression_type, &payload, flags);
}
if (memc_user_data->set_udf_flags >= 0) {
MEMC_VAL_SET_USER_FLAGS(*flags, ((uint32_t) memc_user_data->set_udf_flags));
}
return payload;
}
static
zend_bool s_should_retry_write (php_memc_object_t *intern, memcached_return status)
{
if (memcached_server_count (intern->memc) == 0) {
return 0;
}
return s_memcached_return_is_error (status, 1);
}
static
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)
{
uint32_t flags = 0;
zend_string *payload = NULL;
memcached_return status = 0;
php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
zend_long retries = memc_user_data->store_retry_count;
if (value) {
payload = s_zval_to_payload(intern, value, &flags);
if (!payload) {
s_memc_set_status(intern, MEMC_RES_PAYLOAD_FAILURE, 0);
return 0;
}
}
#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;
#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;
if (server_key) {
switch (op) {
case MEMC_OP_SET:
status = memc_write_using_fn_by_key(memcached_set_by_key);
break;
case MEMC_OP_TOUCH:
status = php_memcached_touch_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), expiration);
break;
case MEMC_OP_ADD:
status = memc_write_using_fn_by_key(memcached_add_by_key);
break;
case MEMC_OP_REPLACE:
status = memc_write_using_fn_by_key(memcached_replace_by_key);
break;
case MEMC_OP_APPEND:
status = memc_write_using_fn_by_key(memcached_append_by_key);
break;
case MEMC_OP_PREPEND:
status = memc_write_using_fn_by_key(memcached_prepend_by_key);
break;
}
if (status == MEMCACHED_END) {
status = MEMCACHED_SUCCESS;
}
}
else {
retry:
switch (op) {
case MEMC_OP_SET:
status = memc_write_using_fn(memcached_set);
break;
case MEMC_OP_TOUCH:
status = php_memcached_touch(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), expiration);
break;
case MEMC_OP_ADD:
status = memc_write_using_fn(memcached_add);
break;
case MEMC_OP_REPLACE:
status = memc_write_using_fn(memcached_replace);
break;
case MEMC_OP_APPEND:
status = memc_write_using_fn(memcached_append);
break;
case MEMC_OP_PREPEND:
status = memc_write_using_fn(memcached_prepend);
break;
}
if (status == MEMCACHED_END) {
status = MEMCACHED_SUCCESS;
}
}
if (s_should_retry_write (intern, status) && retries-- > 0) {
goto retry;
}
#undef memc_write_using_fn
#undef memc_write_using_fn_by_key
if (payload) {
zend_string_release(payload);
}
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
return 0;
}
return 1;
}
/****************************************
Methods
****************************************/
/* {{{ Memcached::__construct([string persistent_id[, callback on_new[, string connection_str]]]))
Creates a Memcached object, optionally using persistent memcache connection */
static PHP_METHOD(Memcached, __construct)
{
php_memc_object_t *intern;
php_memc_user_data_t *memc_user_data;
zend_string *persistent_id = NULL;
zend_string *conn_str = NULL;
zend_string *plist_key = NULL;
zend_fcall_info fci = {0};
zend_fcall_info_cache fci_cache;
zend_bool is_persistent = 0;
/* "|S!f!S" */
ZEND_PARSE_PARAMETERS_START(0, 3)
Z_PARAM_OPTIONAL
Z_PARAM_STR_EX(persistent_id, 1, 0)
Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
Z_PARAM_STR(conn_str)
ZEND_PARSE_PARAMETERS_END();
intern = Z_MEMC_OBJ_P(getThis());
intern->is_pristine = 1;
if (persistent_id && persistent_id->len) {
zend_resource *le;
plist_key = zend_string_alloc(sizeof("memcached:id=") + persistent_id->len - 1, 0);
snprintf(ZSTR_VAL(plist_key), plist_key->len + 1, "memcached:id=%s", ZSTR_VAL(persistent_id));
if ((le = zend_hash_find_ptr(&EG(persistent_list), plist_key)) != NULL) {
if (le->type == php_memc_list_entry()) {
intern->memc = le->ptr;
intern->is_pristine = 0;
zend_string_release (plist_key);
return;
}
}
is_persistent = 1;
}
if (conn_str && conn_str->len > 0) {
intern->memc = memcached (ZSTR_VAL(conn_str), ZSTR_LEN(conn_str));
}
else {
intern->memc = memcached (NULL, 0);
}
if (!intern->memc) {
php_error_docref(NULL, E_ERROR, "Failed to allocate memory for memcached structure");
/* never reached */
}
memc_user_data = pecalloc (1, sizeof(*memc_user_data), is_persistent);
memc_user_data->serializer = MEMC_G(serializer_type);
memc_user_data->compression_type = MEMC_G(compression_type);
memc_user_data->compression_enabled = 1;
memc_user_data->encoding_enabled = 0;
memc_user_data->store_retry_count = MEMC_G(store_retry_count);
memc_user_data->set_udf_flags = -1;
memc_user_data->is_persistent = is_persistent;
memcached_set_user_data(intern->memc, memc_user_data);
/* Set default behaviors */
if (MEMC_G(default_behavior.consistent_hash_enabled)) {
memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, MEMCACHED_DISTRIBUTION_CONSISTENT);
if (rc != MEMCACHED_SUCCESS) {
php_error_docref(NULL, E_WARNING, "Failed to turn on consistent hash: %s", memcached_strerror(intern->memc, rc));
}
}
if (MEMC_G(default_behavior.binary_protocol_enabled)) {
memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1);
if (rc != MEMCACHED_SUCCESS) {
php_error_docref(NULL, E_WARNING, "Failed to turn on binary protocol: %s", memcached_strerror(intern->memc, rc));
}
}
if (MEMC_G(default_behavior.connect_timeout)) {
memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, MEMC_G(default_behavior.connect_timeout));
if (rc != MEMCACHED_SUCCESS) {
php_error_docref(NULL, E_WARNING, "Failed to set connect timeout: %s", memcached_strerror(intern->memc, rc));
}
}
if (fci.size) {
if (!s_invoke_new_instance_cb(getThis(), &fci, &fci_cache, persistent_id) || EG(exception)) {
/* error calling or exception thrown from callback */
if (plist_key) {
zend_string_release(plist_key);
}
/*
Setting intern->memc null prevents object destruction from freeing the memcached_st
We free it manually here because it might be persistent and has not been
registered to persistent_list yet
*/
php_memc_destroy(intern->memc, memc_user_data);
intern->memc = NULL;
return;
}
}
if (plist_key) {
zend_resource le;
le.type = php_memc_list_entry();
le.ptr = intern->memc;
GC_SET_REFCOUNT(&le, 1);
/* plist_key is not a persistent allocated key, thus we use str_update here */
if (zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(plist_key), ZSTR_LEN(plist_key), &le, sizeof(le)) == NULL) {
zend_string_release(plist_key);
php_error_docref(NULL, E_ERROR, "could not register persistent entry");
/* not reached */
}
zend_string_release(plist_key);
}
}
/* }}} */
static
void s_hash_to_keys(php_memc_keys_t *keys_out, HashTable *hash_in, zend_bool preserve_order, zval *return_value)
{
size_t idx = 0, alloc_count;
zval *zv;
keys_out->num_valid_keys = 0;
alloc_count = zend_hash_num_elements(hash_in);
if (!alloc_count) {
return;
}
keys_out->mkeys = ecalloc (alloc_count, sizeof (char *));
keys_out->mkeys_len = ecalloc (alloc_count, sizeof (size_t));
keys_out->strings = ecalloc (alloc_count, sizeof (zend_string *));
ZEND_HASH_FOREACH_VAL(hash_in, zv) {
zend_string *key = zval_get_string(zv);
if (preserve_order && return_value) {
add_assoc_null_ex(return_value, ZSTR_VAL(key), ZSTR_LEN(key));
}
if (ZSTR_LEN(key) > 0 && ZSTR_LEN(key) < MEMCACHED_MAX_KEY) {
keys_out->mkeys[idx] = ZSTR_VAL(key);
keys_out->mkeys_len[idx] = ZSTR_LEN(key);
keys_out->strings[idx] = key;
idx++;
}
else {
zend_string_release (key);
}
} ZEND_HASH_FOREACH_END();
if (!idx) {
efree (keys_out->mkeys);
efree (keys_out->mkeys_len);
efree (keys_out->strings);
}
keys_out->num_valid_keys = idx;
}
static
void s_key_to_keys(php_memc_keys_t *keys_out, zend_string *key)
{
zval zv_keys;
array_init(&zv_keys);
add_next_index_str(&zv_keys, zend_string_copy(key));
s_hash_to_keys(keys_out, Z_ARRVAL(zv_keys), 0, NULL);
zval_ptr_dtor(&zv_keys);
}
static
void s_clear_keys(php_memc_keys_t *keys)
{
size_t i;
if (!keys->num_valid_keys) {
return;
}
for (i = 0; i < keys->num_valid_keys; i++) {
zend_string_release (keys->strings[i]);
}
efree(keys->strings);
efree(keys->mkeys);
efree(keys->mkeys_len);
}
typedef struct {
zend_bool extended;
zval *return_value;
} php_memc_get_ctx_t;
static
zend_bool s_get_apply_fn(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
{
php_memc_get_ctx_t *context = (php_memc_get_ctx_t *) in_context;
if (context->extended) {
Z_TRY_ADDREF_P(value);
Z_TRY_ADDREF_P(cas);
array_init (context->return_value);
add_assoc_zval (context->return_value, "value", value);
add_assoc_zval (context->return_value, "cas", cas);
add_assoc_long (context->return_value, "flags", (zend_long) MEMC_VAL_GET_USER_FLAGS(flags));
}
else {
ZVAL_COPY(context->return_value, value);
}
return 0; /* Stop after one */
}
static
void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
php_memc_get_ctx_t context = {0};
php_memc_keys_t keys = {0};
zend_long get_flags = 0;
zend_string *key;
zend_string *server_key = NULL;
zend_bool mget_status;
memcached_return status = MEMCACHED_SUCCESS;
zend_fcall_info fci = empty_fcall_info;
zend_fcall_info_cache fcc = empty_fcall_info_cache;
MEMC_METHOD_INIT_VARS;
if (by_key) {
/* "SS|f!l" */
ZEND_PARSE_PARAMETERS_START(2, 4)
Z_PARAM_STR(server_key)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
Z_PARAM_LONG(get_flags)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "S|f!l" */
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
Z_PARAM_LONG(get_flags)
ZEND_PARSE_PARAMETERS_END();
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
MEMC_CHECK_KEY(intern, key);
context.extended = (get_flags & MEMC_GET_EXTENDED);
context.return_value = return_value;
s_key_to_keys(&keys, key);
mget_status = php_memc_mget_apply(intern, server_key, &keys, s_get_apply_fn, context.extended, &context);
s_clear_keys(&keys);
if (!mget_status) {
if (s_memc_status_has_result_code(intern, MEMCACHED_NOTFOUND) && fci.size > 0) {
status = s_invoke_cache_callback(object, &fci, &fcc, context.extended, key, return_value);
if (!status) {
zval_ptr_dtor(return_value);
RETURN_FROM_GET;
}
}
}
if (s_memc_status_has_error(intern)) {
zval_ptr_dtor(return_value);
RETURN_FROM_GET;
}
}
/* {{{ Memcached::get(string key [, mixed callback [, int get_flags = 0])
Returns a value for the given key or false */
PHP_METHOD(Memcached, get)
{
php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ Memcached::getByKey(string server_key, string key [, mixed callback [, int get_flags = 0])
Returns a value for key from the server identified by the server key or false */
PHP_METHOD(Memcached, getByKey)
{
php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
static
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)
{
php_memc_get_ctx_t *context = (php_memc_get_ctx_t *) in_context;
Z_TRY_ADDREF_P(value);
if (context->extended) {
zval node;
Z_TRY_ADDREF_P(cas);
array_init(&node);
add_assoc_zval(&node, "value", value);
add_assoc_zval(&node, "cas", cas);
add_assoc_long(&node, "flags", (zend_long) MEMC_VAL_GET_USER_FLAGS(flags));
zend_symtable_update(Z_ARRVAL_P(context->return_value), key, &node);
}
else {
zend_symtable_update(Z_ARRVAL_P(context->return_value), key, value);
}
return 1;
}
/* {{{ -- php_memc_getMulti_impl */
static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
php_memc_get_ctx_t context;
php_memc_keys_t keys_out;
zval *keys = NULL;
zend_string *server_key = NULL;
zend_long flags = 0;
MEMC_METHOD_INIT_VARS;
zend_bool retval, preserve_order;
if (by_key) {
/* "Sa|l" */
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(server_key)
Z_PARAM_ARRAY(keys)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(flags)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "a|l" */
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_ARRAY(keys)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(flags)
ZEND_PARSE_PARAMETERS_END();
}
MEMC_METHOD_FETCH_OBJECT;
array_init(return_value);
if (zend_hash_num_elements(Z_ARRVAL_P(keys)) == 0) {
/* BC compatible */
s_memc_set_status(intern, MEMCACHED_NOTFOUND, 0);
return;
}
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
preserve_order = (flags & MEMC_GET_PRESERVE_ORDER);
s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), preserve_order, return_value);
context.extended = (flags & MEMC_GET_EXTENDED);
context.return_value = return_value;
retval = php_memc_mget_apply(intern, server_key, &keys_out, s_get_multi_apply_fn, context.extended, &context);
s_clear_keys(&keys_out);
if (!retval && (s_memc_status_has_result_code(intern, MEMCACHED_NOTFOUND) || s_memc_status_has_result_code(intern, MEMCACHED_SOME_ERRORS))) {
return;
}
if (!retval || EG(exception)) {
zval_dtor(return_value);
RETURN_FROM_GET;
}
}
/* }}} */
/* {{{ Memcached::getMulti(array keys[, long flags = 0 ])
Returns values for the given keys or false */
PHP_METHOD(Memcached, getMulti)
{
php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ Memcached::getMultiByKey(string server_key, array keys[, long flags = 0 ])
Returns values for the given keys from the server identified by the server key or false */
PHP_METHOD(Memcached, getMultiByKey)
{
php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ Memcached::getDelayed(array keys [, bool with_cas [, mixed callback ] ])
Sends a request for the given keys and returns immediately */
PHP_METHOD(Memcached, getDelayed)
{
php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ Memcached::getDelayedByKey(string server_key, array keys [, bool with_cas [, mixed callback ] ])
Sends a request for the given keys from the server identified by the server key and returns immediately */
PHP_METHOD(Memcached, getDelayedByKey)
{
php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
static
void s_create_result_array(zend_string *key, zval *value, zval *cas, uint32_t flags, zval *return_value)
{
Z_TRY_ADDREF_P(value);
Z_TRY_ADDREF_P(cas);
add_assoc_str_ex(return_value, ZEND_STRL("key"), zend_string_copy(key));
add_assoc_zval_ex(return_value, ZEND_STRL("value"), value);
if (Z_LVAL_P(cas)) {
/* BC compatible */
add_assoc_zval_ex(return_value, ZEND_STRL("cas"), cas);
add_assoc_long_ex(return_value, ZEND_STRL("flags"), MEMC_VAL_GET_USER_FLAGS(flags));
}
}
static
zend_bool s_result_callback_apply(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
{
zend_bool status = 1;
zval params[2];
zval retval;
php_memc_result_callback_ctx_t *context = (php_memc_result_callback_ctx_t *) in_context;
ZVAL_COPY(&params[0], context->object);
array_init(&params[1]);
s_create_result_array(key, value, cas, flags, &params[1]);
context->fci.retval = &retval;
context->fci.params = params;
context->fci.param_count = 2;
if (zend_call_function(&context->fci, &context->fcc) == FAILURE) {
php_error_docref(NULL, E_WARNING, "could not invoke result callback");
status = 0;
}
zval_ptr_dtor(&retval);
zval_ptr_dtor(&params[0]);
zval_ptr_dtor(&params[1]);
return status;
}
/* {{{ -- php_memc_getDelayed_impl */
static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
php_memc_keys_t keys_out = {0};
zval *keys = NULL;
zend_string *server_key = NULL;
zend_bool with_cas = 0;
zend_fcall_info fci = empty_fcall_info;
zend_fcall_info_cache fcc = empty_fcall_info_cache;
memcached_return status = MEMCACHED_SUCCESS;
MEMC_METHOD_INIT_VARS;
if (by_key) {
/* "Sa/|bf!" */
ZEND_PARSE_PARAMETERS_START(2, 4)
Z_PARAM_STR(server_key)
Z_PARAM_ARRAY_EX(keys, 0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(with_cas)
Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "a/|bf!" */
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_ARRAY_EX(keys, 0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(with_cas)
Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
ZEND_PARSE_PARAMETERS_END();
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), 0, NULL);
if (fci.size > 0) {
php_memc_result_callback_ctx_t context = {
getThis(), fci, fcc
};
status = php_memc_mget_apply(intern, server_key, &keys_out, &s_result_callback_apply, with_cas, (void *) &context);
}
else {
status = php_memc_mget_apply(intern, server_key, &keys_out, NULL, with_cas, NULL);
}
s_clear_keys(&keys_out);
RETURN_BOOL(status);
}
/* }}} */
static
zend_bool s_fetch_apply(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
{
zval *return_value = (zval *) in_context;
s_create_result_array(key, value, cas, flags, return_value);
return 0; // stop iterating after one
}
/* {{{ Memcached::fetch()
Returns the next result from a previous delayed request */
PHP_METHOD(Memcached, fetch)
{
memcached_return status = MEMCACHED_SUCCESS;
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
array_init(return_value);
status = php_memc_result_apply(intern, s_fetch_apply, 1, return_value);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
zval_ptr_dtor(return_value);
RETURN_FROM_GET;
}
}
/* }}} */
static
zend_bool s_fetch_all_apply(php_memc_object_t *intern, zend_string *key, zval *value, zval *cas, uint32_t flags, void *in_context)
{
zval zv;
zval *return_value = (zval *) in_context;
array_init (&zv);
s_create_result_array(key, value, cas, flags, &zv);
add_next_index_zval(return_value, &zv);
return 1;
}
/* {{{ Memcached::fetchAll()
Returns all the results from a previous delayed request */
PHP_METHOD(Memcached, fetchAll)
{
memcached_return status = MEMCACHED_SUCCESS;
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
array_init(return_value);
status = php_memc_result_apply(intern, s_fetch_all_apply, 0, return_value);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
/* }}} */
/* {{{ Memcached::set(string key, mixed value [, int expiration ])
Sets the value for the given key */
PHP_METHOD(Memcached, set)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 0);
}
/* }}} */
/* {{{ Memcached::setByKey(string server_key, string key, mixed value [, int expiration ])
Sets the value for the given key on the server identified by the server key */
PHP_METHOD(Memcached, setByKey)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 1);
}
/* }}} */
/* {{{ Memcached::touch(string key, [, int expiration ])
Sets a new expiration for the given key */
PHP_METHOD(Memcached, touch)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 0);
}
/* }}} */
/* {{{ Memcached::touchbyKey(string key, [, int expiration ])
Sets a new expiration for the given key */
PHP_METHOD(Memcached, touchByKey)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 1);
}
/* }}} */
/* {{{ Memcached::setMulti(array items [, int expiration ])
Sets the keys/values specified in the items array */
PHP_METHOD(Memcached, setMulti)
{
php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ Memcached::setMultiByKey(string server_key, array items [, int expiration ])
Sets the keys/values specified in the items array on the server identified by the given server key */
PHP_METHOD(Memcached, setMultiByKey)
{
php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ -- php_memc_setMulti_impl */
static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
zval *entries;
zend_string *server_key = NULL;
zend_long expiration = 0, ignored;
zval *value;
zend_string *skey;
zend_ulong num_key;
int tmp_len = 0;
MEMC_METHOD_INIT_VARS;
if (by_key) {
/* "Sa|ll" */
ZEND_PARSE_PARAMETERS_START(2, 4)
Z_PARAM_STR(server_key)
Z_PARAM_ARRAY(entries)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
Z_PARAM_LONG(ignored)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "a|ll" */
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_ARRAY(entries)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
Z_PARAM_LONG(ignored)
ZEND_PARSE_PARAMETERS_END();
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
ZEND_HASH_FOREACH_KEY_VAL (Z_ARRVAL_P(entries), num_key, skey, value) {
zend_string *str_key = NULL;
if (skey) {
str_key = skey;
}
else {
char tmp_key[64];
tmp_len = snprintf(tmp_key, sizeof(tmp_key) - 1, "%ld", (long)num_key);
str_key = zend_string_init(tmp_key, tmp_len, 0);
}
if (!s_memc_write_zval (intern, MEMC_OP_SET, server_key, str_key, value, expiration)) {
php_error_docref(NULL, E_WARNING, "failed to set key %s", ZSTR_VAL(str_key));
}
if (!skey) {
zend_string_release (str_key);
}
} ZEND_HASH_FOREACH_END();
RETURN_BOOL(!s_memc_status_has_error(intern));
}
/* }}} */
/* {{{ Memcached::add(string key, mixed value [, int expiration ])
Sets the value for the given key, failing if the key already exists */
PHP_METHOD(Memcached, add)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 0);
}
/* }}} */
/* {{{ Memcached::addByKey(string server_key, string key, mixed value [, int expiration ])
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)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 1);
}
/* }}} */
/* {{{ Memcached::append(string key, mixed value)
Appends the value to existing one for the key */
PHP_METHOD(Memcached, append)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 0);
}
/* }}} */
/* {{{ Memcached::appendByKey(string server_key, string key, mixed value)
Appends the value to existing one for the key on the server identified by the server key */
PHP_METHOD(Memcached, appendByKey)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 1);
}
/* }}} */
/* {{{ Memcached::prepend(string key, mixed value)
Prepends the value to existing one for the key */
PHP_METHOD(Memcached, prepend)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 0);
}
/* }}} */
/* {{{ Memcached::prependByKey(string server_key, string key, mixed value)
Prepends the value to existing one for the key on the server identified by the server key */
PHP_METHOD(Memcached, prependByKey)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 1);
}
/* }}} */
/* {{{ Memcached::replace(string key, mixed value [, int expiration ])
Replaces the value for the given key, failing if the key doesn't exist */
PHP_METHOD(Memcached, replace)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 0);
}
/* }}} */
/* {{{ Memcached::replaceByKey(string server_key, string key, mixed value [, int expiration ])
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)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 1);
}
/* }}} */
/* {{{ -- php_memc_store_impl */
static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key)
{
zend_string *key;
zend_string *server_key = NULL;
zend_string *s_value;
zval s_zvalue;
zval *value = NULL;
zend_long expiration = 0;
MEMC_METHOD_INIT_VARS;
if (by_key) {
if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) {
/* "SSS" */
ZEND_PARSE_PARAMETERS_START(3, 3)
Z_PARAM_STR(server_key)
Z_PARAM_STR(key)
Z_PARAM_STR(s_value)
ZEND_PARSE_PARAMETERS_END();
value = &s_zvalue;
ZVAL_STR(value, s_value);
} else if (op == MEMC_OP_TOUCH) {
/* "SS|l" */
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(server_key)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "SSz|l" */
ZEND_PARSE_PARAMETERS_START(3, 4)
Z_PARAM_STR(server_key)
Z_PARAM_STR(key)
Z_PARAM_ZVAL(value)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
}
} else {
if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) {
/* "SS" */
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_STR(key)
Z_PARAM_STR(s_value)
ZEND_PARSE_PARAMETERS_END();
value = &s_zvalue;
ZVAL_STR(value, s_value);
} else if (op == MEMC_OP_TOUCH) {
/* "S|l */
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "Sz|l" */
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(key)
Z_PARAM_ZVAL(value)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
}
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
MEMC_CHECK_KEY(intern, key);
if (memc_user_data->compression_enabled) {
/*
* When compression is enabled, we cannot do appends/prepends because that would
* corrupt the compressed values. It is up to the user to fetch the value,
* append/prepend new data, and store it again.
*/
if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) {
php_error_docref(NULL, E_WARNING, "cannot append/prepend with compression turned on");
RETURN_NULL();
}
}
if (!s_memc_write_zval (intern, op, server_key, key, value, expiration)) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ -- php_memc_cas_impl */
static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
zval *zv_cas;
uint64_t cas;
zend_string *key;
zend_string *server_key = NULL;
zval *value;
zend_long expiration = 0;
zend_long ignored;
zend_string *payload;
uint32_t flags = 0;
memcached_return status;
MEMC_METHOD_INIT_VARS;
if (by_key) {
/* "zSSz|ll" */
ZEND_PARSE_PARAMETERS_START(4, 6)
Z_PARAM_ZVAL(zv_cas)
Z_PARAM_STR(server_key)
Z_PARAM_STR(key)
Z_PARAM_ZVAL(value)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
Z_PARAM_LONG(ignored)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "zSz|ll" */
ZEND_PARSE_PARAMETERS_START(3, 5)
Z_PARAM_ZVAL(zv_cas)
Z_PARAM_STR(key)
Z_PARAM_ZVAL(value)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
Z_PARAM_LONG(ignored)
ZEND_PARSE_PARAMETERS_END();
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
MEMC_CHECK_KEY(intern, key);
cas = s_zval_to_uint64(zv_cas);
payload = s_zval_to_payload(intern, value, &flags);
if (payload == NULL) {
intern->rescode = MEMC_RES_PAYLOAD_FAILURE;
RETURN_FALSE;
}
if (by_key) {
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);
} else {
status = memcached_cas(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas);
}
zend_string_release(payload);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ])
Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */
PHP_METHOD(Memcached, cas)
{
php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ])
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)
{
php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ Memcached::delete(string key [, int time ])
Deletes the given key */
PHP_METHOD(Memcached, delete)
{
php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ Memcached::deleteMulti(array keys [, int time ])
Deletes the given keys */
PHP_METHOD(Memcached, deleteMulti)
{
php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ Memcached::deleteByKey(string server_key, string key [, int time ])
Deletes the given key from the server identified by the server key */
PHP_METHOD(Memcached, deleteByKey)
{
php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ Memcached::deleteMultiByKey(array keys [, int time ])
Deletes the given key from the server identified by the server key */
PHP_METHOD(Memcached, deleteMultiByKey)
{
php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ -- php_memc_delete_impl */
static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
zend_string *key, *server_key;
zend_long expiration = 0;
memcached_return status;
MEMC_METHOD_INIT_VARS;
if (by_key) {
/* "SS|l" */
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(server_key)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "S|l" */
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
server_key = key;
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
MEMC_CHECK_KEY(intern, key);
if (by_key) {
status = memcached_delete_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key),
ZSTR_LEN(key), expiration);
} else {
status = memcached_delete(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), expiration);
}
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ -- php_memc_deleteMulti_impl */
static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
zval *entries, *zv, ret;
zend_string *server_key = NULL;
zend_long expiration = 0;
zend_string *entry;
memcached_return status;
MEMC_METHOD_INIT_VARS;
if (by_key) {
/* "Sa/|l" */
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(server_key)
Z_PARAM_ARRAY_EX(entries, 0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "a/|l" */
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_ARRAY_EX(entries, 0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(expiration)
ZEND_PARSE_PARAMETERS_END();
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
array_init(return_value);
ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P(entries), zv) {
entry = zval_get_string(zv);
if (ZSTR_LEN(entry) == 0) {
zend_string_release(entry);
continue;
}
if (by_key) {
status = memcached_delete_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(entry), ZSTR_LEN(entry), expiration);
} else {
status = memcached_delete_by_key(intern->memc, ZSTR_VAL(entry), ZSTR_LEN(entry), ZSTR_VAL(entry), ZSTR_LEN(entry), expiration);
}
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
ZVAL_LONG(&ret, status);
} else {
ZVAL_TRUE(&ret);
}
zend_symtable_update(Z_ARRVAL_P(return_value), entry, &ret);
zend_string_release(entry);
} ZEND_HASH_FOREACH_END();
return;
}
/* }}} */
/* {{{ -- php_memc_incdec_impl */
static void php_memc_incdec_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key, zend_bool incr)
{
zend_string *key, *server_key = NULL;
zend_long offset = 1;
zend_long expiry = 0;
zend_long initial = 0;
uint64_t value = UINT64_MAX;
memcached_return status;
int n_args = ZEND_NUM_ARGS();
MEMC_METHOD_INIT_VARS;
if (!by_key) {
/* "S|lll" */
ZEND_PARSE_PARAMETERS_START(1, 4)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(offset)
Z_PARAM_LONG(initial)
Z_PARAM_LONG(expiry)
ZEND_PARSE_PARAMETERS_END();
} else {
/* "SS|lll" */
ZEND_PARSE_PARAMETERS_START(2, 5)
Z_PARAM_STR(server_key)
Z_PARAM_STR(key)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(offset)
Z_PARAM_LONG(initial)
Z_PARAM_LONG(expiry)
ZEND_PARSE_PARAMETERS_END();
}
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
MEMC_CHECK_KEY(intern, key);
if (offset < 0) {
php_error_docref(NULL, E_WARNING, "offset cannot be a negative value");
RETURN_FALSE;
}
if ((!by_key && n_args < 3) || (by_key && n_args < 4)) {
if (by_key) {
if (incr) {
status = memcached_increment_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
} else {
status = memcached_decrement_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
}
} else {
/* The libmemcached API has a quirk that memcached_increment() takes only a 32-bit
* offset, but memcached_increment_by_key() and all other increment and decrement
* functions take a 64-bit offset. The memcached protocol allows increment/decrement
* greater than UINT_MAX, so we just work around memcached_increment() here.
*/
if (incr) {
status = memcached_increment_by_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
} else {
status = memcached_decrement_by_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value);
}
}
} else {
zend_long retries = memc_user_data->store_retry_count;
retry_inc_dec:
if (!memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) {
php_error_docref(NULL, E_WARNING, "Initial value is only supported with binary protocol");
RETURN_FALSE;
}
if (by_key) {
if (incr) {
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);
} else {
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);
}
} else {
if (incr) {
status = memcached_increment_with_initial(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), offset, initial, (time_t)expiry, &value);
} else {
status = memcached_decrement_with_initial(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), offset, initial, (time_t)expiry, &value);
}
}
if (s_should_retry_write(intern, status) && retries-- > 0) {
goto retry_inc_dec;
}
}
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
if (value == UINT64_MAX) {
RETURN_FALSE;
}
RETURN_LONG((long)value);
}
/* }}} */
/* {{{ Memcached::increment(string key [, int delta [, initial_value [, expiry time ] ] ])
Increments the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached, increment)
{
php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
}
/* }}} */
/* {{{ Memcached::decrement(string key [, int delta [, initial_value [, expiry time ] ] ])
Decrements the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached, decrement)
{
php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
}
/* }}} */
/* {{{ Memcached::decrementByKey(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ])
Decrements by server the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached, decrementByKey)
{
php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
}
/* }}} */
/* {{{ Memcached::increment(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ])
Increments by server the value for the given key by delta, defaulting to 1 */
PHP_METHOD(Memcached, incrementByKey)
{
php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1);
}
/* }}} */
/* {{{ Memcached::addServer(string hostname, int port [, int weight ])
Adds the given memcache server to the list */
PHP_METHOD(Memcached, addServer)
{
zend_string *host;
zend_long port, weight = 0;
memcached_return status;
MEMC_METHOD_INIT_VARS;
/* "Sa/|l" */
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(host)
Z_PARAM_LONG(port)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(weight)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
status = memcached_server_add_with_weight(intern->memc, ZSTR_VAL(host), port, weight);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ Memcached::addServers(array servers)
Adds the given memcache servers to the server list */
PHP_METHOD(Memcached, addServers)
{
zval *servers;
zval *entry;
zval *z_host, *z_port, *z_weight = NULL;
HashPosition pos;
int entry_size, i = 0;
memcached_server_st *list = NULL;
memcached_return status;
MEMC_METHOD_INIT_VARS;
/* "a/" */
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_EX(servers, 0, 1)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P(servers), entry) {
if (Z_TYPE_P(entry) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "server list entry #%d is not an array", i+1);
i++;
continue;
}
entry_size = zend_hash_num_elements(Z_ARRVAL_P(entry));
if (entry_size > 1) {
zend_string *host;
zend_long port;
uint32_t weight;
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(entry), &pos);
/* Check that we have a host */
if ((z_host = zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), &pos)) == NULL) {
php_error_docref(NULL, E_WARNING, "could not get server host for entry #%d", i+1);
i++;
continue;
}
/* Check that we have a port */
if (zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos) == FAILURE ||
(z_port = zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), &pos)) == NULL) {
php_error_docref(NULL, E_WARNING, "could not get server port for entry #%d", i+1);
i++;
continue;
}
host = zval_get_string(z_host);
port = zval_get_long(z_port);
weight = 0;
if (entry_size > 2) {
/* Try to get weight */
if (zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos) == FAILURE ||
(z_weight = zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), &pos)) == NULL) {
php_error_docref(NULL, E_WARNING, "could not get server weight for entry #%d", i+1);
}
weight = zval_get_long(z_weight);
}
list = memcached_server_list_append_with_weight(list, ZSTR_VAL(host), port, weight, &status);
zend_string_release(host);
if (s_memc_status_handle_result_code(intern, status) == SUCCESS) {
i++;
continue;
}
}
i++;
/* catch-all for all errors */
php_error_docref(NULL, E_WARNING, "could not add entry #%d to the server list", i + 1);
} ZEND_HASH_FOREACH_END();
status = memcached_server_push(intern->memc, list);
memcached_server_list_free(list);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ Memcached::getServerList()
Returns the list of the memcache servers in use */
PHP_METHOD(Memcached, getServerList)
{
memcached_server_function callbacks[1];
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
callbacks[0] = s_server_cursor_list_servers_cb;
array_init(return_value);
memcached_server_cursor(intern->memc, callbacks, return_value, 1);
}
/* }}} */
/* {{{ Memcached::getServerByKey(string server_key)
Returns the server identified by the given server key */
PHP_METHOD(Memcached, getServerByKey)
{
zend_string *server_key;
php_memcached_instance_st server_instance;
memcached_return error;
MEMC_METHOD_INIT_VARS;
/* "S" */
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(server_key)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
server_instance = memcached_server_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), &error);
if (server_instance == NULL) {
s_memc_status_handle_result_code(intern, error);
RETURN_FALSE;
}
array_init(return_value);
add_assoc_string(return_value, "host", (char*) memcached_server_name(server_instance));
add_assoc_long(return_value, "port", memcached_server_port(server_instance));
add_assoc_long(return_value, "weight", 0);
}
/* }}} */
/* {{{ Memcached::resetServerList()
Reset the server list in use */
PHP_METHOD(Memcached, resetServerList)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
memcached_servers_reset(intern->memc);
RETURN_TRUE;
}
/* }}} */
/* {{{ Memcached::quit()
Close any open connections */
PHP_METHOD(Memcached, quit)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
memcached_quit(intern->memc);
RETURN_TRUE;
}
/* }}} */
/* {{{ Memcached::flushBuffers()
Flush and senf buffered commands */
PHP_METHOD(Memcached, flushBuffers)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
RETURN_BOOL(memcached_flush_buffers(intern->memc) == MEMCACHED_SUCCESS);
}
/* }}} */
/* {{{ Memcached::getLastErrorMessage()
Returns the last error message that occurred */
PHP_METHOD(Memcached, getLastErrorMessage)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
RETURN_STRING(memcached_last_error_message(intern->memc));
}
/* }}} */
/* {{{ Memcached::getLastErrorCode()
Returns the last error code that occurred */
PHP_METHOD(Memcached, getLastErrorCode)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
RETURN_LONG(memcached_last_error(intern->memc));
}
/* }}} */
/* {{{ Memcached::getLastErrorErrno()
Returns the last error errno that occurred */
PHP_METHOD(Memcached, getLastErrorErrno)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
RETURN_LONG(memcached_last_error_errno(intern->memc));
}
/* }}} */
/* {{{ Memcached::getLastDisconnectedServer()
Returns the last disconnected server
Was added in 0.34 according to libmemcached's Changelog */
PHP_METHOD(Memcached, getLastDisconnectedServer)
{
php_memcached_instance_st server_instance;
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
server_instance = memcached_server_get_last_disconnect(intern->memc);
if (server_instance == NULL) {
RETURN_FALSE;
}
array_init(return_value);
add_assoc_string(return_value, "host", (char*) memcached_server_name(server_instance));
add_assoc_long(return_value, "port", memcached_server_port(server_instance));
}
/* }}} */
static
zend_bool s_long_value(const char *str, zend_long *value)
{
char *end = (char *) str;
errno = 0;
*value = strtol(str, &end, 10);
if (errno || str == end || *end != '\0') {
return 0;
}
return 1;
}
static
zend_bool s_double_value(const char *str, double *value)
{
char *end = (char *) str;
errno = 0;
*value = strtod(str, &end);
if (errno || str == end || *end != '\0') {
return 0;
}
return 1;
}
static
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)
{
zend_string *server_key;
zend_long long_val;
double d_val;
char *buffer;
zval *return_value = (zval *) context;
zval *server_values;
server_key = strpprintf(0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance));
server_values = zend_hash_find(Z_ARRVAL_P(return_value), server_key);
if (!server_values) {
zval zv;
array_init(&zv);
server_values = zend_hash_add(Z_ARRVAL_P(return_value), server_key, &zv);
}
spprintf (&buffer, 0, "%.*s", value_length, value);
/* Check type */
if (s_long_value (buffer, &long_val)) {
add_assoc_long(server_values, key, long_val);
}
else if (s_double_value (buffer, &d_val)) {
add_assoc_double(server_values, key, d_val);
}
else {
add_assoc_stringl_ex(server_values, key, key_length, (char*)value, value_length);
}
efree (buffer);
zend_string_release(server_key);
return MEMCACHED_SUCCESS;
}
/* {{{ Memcached::getStats()
Returns statistics for the memcache servers */
PHP_METHOD(Memcached, getStats)
{
memcached_return status;
char *args = NULL;
zend_string *args_string = NULL;
uint64_t orig_no_block, orig_protocol;
MEMC_METHOD_INIT_VARS;
/* "|S!" */
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_STR_EX(args_string, 1, 0)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
if (args_string)
args = ZSTR_VAL(args_string);
/* stats hangs in nonblocking mode, turn off during the call. Only change the
* value if needed, because libmemcached reconnects for this behavior_set. */
orig_no_block = memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK);
orig_protocol = memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL);
if (orig_no_block && orig_protocol)
memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 0);
array_init(return_value);
status = memcached_stat_execute(intern->memc, args, s_stat_execute_cb, return_value);
if (orig_no_block && orig_protocol)
memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, orig_no_block);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
zval_ptr_dtor(return_value);
RETURN_FALSE;
}
}
/* }}} */
/* {{{ Memcached::getVersion()
Returns the version of each memcached server in the pool */
PHP_METHOD(Memcached, getVersion)
{
memcached_return status;
memcached_server_function callbacks[1];
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
status = memcached_version(intern->memc);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
callbacks[0] = s_server_cursor_version_cb;
array_init(return_value);
status = memcached_server_cursor(intern->memc, callbacks, return_value, 1);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
/* }}} */
/* {{{ Memcached::getAllKeys()
Returns the keys stored on all the servers */
static
memcached_return s_dump_keys_cb(const memcached_st *ptr, const char *key, size_t key_length, void *in_context)
{
zval *return_value = (zval*) in_context;
add_next_index_stringl(return_value, key, key_length);
return MEMCACHED_SUCCESS;
}
PHP_METHOD(Memcached, getAllKeys)
{
memcached_return rc;
memcached_dump_func callback[1];
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
callback[0] = s_dump_keys_cb;
MEMC_METHOD_FETCH_OBJECT;
array_init(return_value);
rc = memcached_dump(intern->memc, callback, return_value, 1);
/* Ignore two errors. libmemcached has a hardcoded loop of 200 slab
* classes that matches memcached < 1.4.24, at which version the server
* has only 63 slabs and throws an error when requesting the 64th slab.
*
* In multi-server some non-deterministic number of elements will be dropped.
*/
if (rc != MEMCACHED_CLIENT_ERROR && rc != MEMCACHED_SERVER_ERROR
&& s_memc_status_handle_result_code(intern, rc) == FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
/* }}} */
/* {{{ Memcached::flush([ int delay ])
Flushes the data on all the servers */
static PHP_METHOD(Memcached, flush)
{
zend_long delay = 0;
memcached_return status;
MEMC_METHOD_INIT_VARS;
/* "|l" */
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(delay)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
status = memcached_flush(intern->memc, delay);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ Memcached::getOption(int option)
Returns the value for the given option constant */
static PHP_METHOD(Memcached, getOption)
{
zend_long option;
uint64_t result;
memcached_behavior flag;
MEMC_METHOD_INIT_VARS;
/* "l" */
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_LONG(option)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
switch (option) {
case MEMC_OPT_COMPRESSION_TYPE:
RETURN_LONG(memc_user_data->compression_type);
case MEMC_OPT_COMPRESSION:
RETURN_BOOL(memc_user_data->compression_enabled);
case MEMC_OPT_PREFIX_KEY:
{
memcached_return retval;
char *result;
result = memcached_callback_get(intern->memc, MEMCACHED_CALLBACK_PREFIX_KEY, &retval);
if (retval == MEMCACHED_SUCCESS && result) {
RETURN_STRING(result);
} else {
RETURN_EMPTY_STRING();
}
}
case MEMC_OPT_SERIALIZER:
RETURN_LONG((long)memc_user_data->serializer);
break;
case MEMC_OPT_USER_FLAGS:
RETURN_LONG(memc_user_data->set_udf_flags);
break;
case MEMC_OPT_STORE_RETRY_COUNT:
RETURN_LONG((long)memc_user_data->store_retry_count);
break;
case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE:
case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE:
if (memcached_server_count(intern->memc) == 0) {
php_error_docref(NULL, E_WARNING, "no servers defined");
return;
}
default:
/*
* Assume that it's a libmemcached behavior option.
*/
flag = (memcached_behavior) option;
result = memcached_behavior_get(intern->memc, flag);
RETURN_LONG((long)result);
}
}
/* }}} */
static
int php_memc_set_option(php_memc_object_t *intern, long option, zval *value)
{
zend_long lval;
memcached_return rc = MEMCACHED_FAILURE;
memcached_behavior flag;
php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
switch (option) {
case MEMC_OPT_COMPRESSION:
memc_user_data->compression_enabled = zval_get_long(value) ? 1 : 0;
break;
case MEMC_OPT_COMPRESSION_TYPE:
lval = zval_get_long(value);
if (lval == COMPRESSION_TYPE_FASTLZ ||
lval == COMPRESSION_TYPE_ZLIB) {
memc_user_data->compression_type = lval;
} else {
/* invalid compression type */
intern->rescode = MEMCACHED_INVALID_ARGUMENTS;
return 0;
}
break;
case MEMC_OPT_PREFIX_KEY:
{
zend_string *str;
char *key;
str = zval_get_string(value);
if (ZSTR_LEN(str) == 0) {
key = NULL;
} else {
key = ZSTR_VAL(str);
}
if (memcached_callback_set(intern->memc, MEMCACHED_CALLBACK_PREFIX_KEY, key) == MEMCACHED_BAD_KEY_PROVIDED) {
zend_string_release(str);
intern->rescode = MEMCACHED_INVALID_ARGUMENTS;
php_error_docref(NULL, E_WARNING, "bad key provided");
return 0;
}
zend_string_release(str);
}
break;
case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED:
flag = (memcached_behavior) option;
lval = zval_get_long(value);
rc = memcached_behavior_set(intern->memc, flag, (uint64_t)lval);
if (s_memc_status_handle_result_code(intern, rc) == FAILURE) {
php_error_docref(NULL, E_WARNING, "error setting memcached option: %s", memcached_strerror (intern->memc, rc));
return 0;
}
/*
* This is necessary because libmemcached does not reset hash/distribution
* options on false case, like it does for MEMCACHED_BEHAVIOR_KETAMA
* (non-weighted) case. We have to clean up ourselves.
*/
if (!lval) {
(void)memcached_behavior_set_key_hash(intern->memc, MEMCACHED_HASH_DEFAULT);
(void)memcached_behavior_set_distribution_hash(intern->memc, MEMCACHED_HASH_DEFAULT);
(void)memcached_behavior_set_distribution(intern->memc, MEMCACHED_DISTRIBUTION_MODULA);
}
break;
case MEMC_OPT_SERIALIZER:
{
lval = zval_get_long(value);
/* igbinary serializer */
#ifdef HAVE_MEMCACHED_IGBINARY
if (lval == SERIALIZER_IGBINARY) {
memc_user_data->serializer = SERIALIZER_IGBINARY;
} else
#endif
#ifdef HAVE_JSON_API
if (lval == SERIALIZER_JSON) {
memc_user_data->serializer = SERIALIZER_JSON;
} else if (lval == SERIALIZER_JSON_ARRAY) {
memc_user_data->serializer = SERIALIZER_JSON_ARRAY;
} else
#endif
/* msgpack serializer */
#ifdef HAVE_MEMCACHED_MSGPACK
if (lval == SERIALIZER_MSGPACK) {
memc_user_data->serializer = SERIALIZER_MSGPACK;
} else
#endif
/* php serializer */
if (lval == SERIALIZER_PHP) {
memc_user_data->serializer = SERIALIZER_PHP;
} else {
memc_user_data->serializer = SERIALIZER_PHP;
intern->rescode = MEMCACHED_INVALID_ARGUMENTS;
php_error_docref(NULL, E_WARNING, "invalid serializer provided");
return 0;
}
break;
}
case MEMC_OPT_USER_FLAGS:
lval = zval_get_long(value);
if (lval < 0) {
memc_user_data->set_udf_flags = -1;
return 1;
}
if (lval > MEMC_VAL_USER_FLAGS_MAX) {
php_error_docref(NULL, E_WARNING, "MEMC_OPT_USER_FLAGS must be < %u", MEMC_VAL_USER_FLAGS_MAX);
return 0;
}
memc_user_data->set_udf_flags = lval;
break;
case MEMC_OPT_STORE_RETRY_COUNT:
lval = zval_get_long(value);
memc_user_data->store_retry_count = lval;
break;
default:
/*
* Assume that it's a libmemcached behavior option.
*/
if (option < 0) {
rc = MEMCACHED_INVALID_ARGUMENTS;
}
else {
flag = (memcached_behavior) option;
lval = zval_get_long(value);
if (flag < MEMCACHED_BEHAVIOR_MAX) {
rc = memcached_behavior_set(intern->memc, flag, (uint64_t)lval);
}
else {
rc = MEMCACHED_INVALID_ARGUMENTS;
}
}
if (s_memc_status_handle_result_code(intern, rc) == FAILURE) {
php_error_docref(NULL, E_WARNING, "error setting memcached option: %s", memcached_strerror (intern->memc, rc));
return 0;
}
break;
}
return 1;
}
static
uint32_t *s_zval_to_uint32_array (zval *input, size_t *num_elements)
{
zval *pzval;
uint32_t *retval;
size_t i = 0;
*num_elements = zend_hash_num_elements(Z_ARRVAL_P(input));
if (!*num_elements) {
return NULL;
}
retval = ecalloc(*num_elements, sizeof(uint32_t));
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), pzval) {
zend_long value = 0;
value = zval_get_long(pzval);
if (value < 0) {
php_error_docref(NULL, E_WARNING, "the map must contain positive integers");
efree (retval);
*num_elements = 0;
return NULL;
}
retval [i] = (uint32_t) value;
i++;
} ZEND_HASH_FOREACH_END();
return retval;
}
/* {{{ Memcached::setBucket(array host_map, array forward_map, integer replicas)
Sets the memcached virtual buckets */
PHP_METHOD(Memcached, setBucket)
{
zval *zserver_map;
zval *zforward_map = NULL;
zend_long replicas = 0;
zend_bool retval = 1;
uint32_t *server_map = NULL, *forward_map = NULL;
size_t server_map_len = 0, forward_map_len = 0;
memcached_return rc;
MEMC_METHOD_INIT_VARS;
/* "aa!l" */
ZEND_PARSE_PARAMETERS_START(3, 3)
Z_PARAM_ARRAY(zserver_map)
Z_PARAM_ARRAY_EX(zforward_map, 1, 0)
Z_PARAM_LONG(replicas)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
if (zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) == 0) {
php_error_docref(NULL, E_WARNING, "server map cannot be empty");
RETURN_FALSE;
}
if (zforward_map && zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) != zend_hash_num_elements (Z_ARRVAL_P(zforward_map))) {
php_error_docref(NULL, E_WARNING, "forward_map length must match the server_map length");
RETURN_FALSE;
}
if (replicas < 0) {
php_error_docref(NULL, E_WARNING, "replicas must be larger than zero");
RETURN_FALSE;
}
server_map = s_zval_to_uint32_array (zserver_map, &server_map_len);
if (!server_map) {
RETURN_FALSE;
}
if (zforward_map) {
forward_map = s_zval_to_uint32_array (zforward_map, &forward_map_len);
if (!forward_map) {
efree (server_map);
RETURN_FALSE;
}
}
rc = memcached_bucket_set (intern->memc, server_map, forward_map, (uint32_t) server_map_len, replicas);
if (s_memc_status_handle_result_code(intern, rc) == FAILURE) {
retval = 0;;
}
efree(server_map);
if (forward_map) {
efree(forward_map);
}
RETURN_BOOL(retval);
}
/* }}} */
/* {{{ Memcached::setOptions(array options)
Sets the value for the given option constant */
static PHP_METHOD(Memcached, setOptions)
{
zval *options;
zend_bool ok = 1;
zend_string *key;
ulong key_index;
zval *value;
MEMC_METHOD_INIT_VARS;
/* "a" */
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(options)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), key_index, key, value) {
if (key) {
php_error_docref(NULL, E_WARNING, "invalid configuration option");
ok = 0;
} else {
if (!php_memc_set_option(intern, (long) key_index, value)) {
ok = 0;
}
}
} ZEND_HASH_FOREACH_END();
RETURN_BOOL(ok);
}
/* }}} */
/* {{{ Memcached::setOption(int option, mixed value)
Sets the value for the given option constant */
static PHP_METHOD(Memcached, setOption)
{
zend_long option;
zval *value;
MEMC_METHOD_INIT_VARS;
/* "lz/" */
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_LONG(option)
Z_PARAM_ZVAL_EX(value, 0, 1)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
RETURN_BOOL(php_memc_set_option(intern, option, value));
}
/* }}} */
#ifdef HAVE_MEMCACHED_SASL
/* {{{ Memcached::setSaslAuthData(string user, string pass)
Sets sasl credentials */
static PHP_METHOD(Memcached, setSaslAuthData)
{
MEMC_METHOD_INIT_VARS;
memcached_return status;
zend_string *user, *pass;
/* "SS/" */
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_STR(user)
Z_PARAM_STR(pass)
ZEND_PARSE_PARAMETERS_END();
if (!php_memc_init_sasl_if_needed()) {
RETURN_FALSE;
}
MEMC_METHOD_FETCH_OBJECT;
if (!memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) {
php_error_docref(NULL, E_WARNING, "SASL is only supported with binary protocol");
RETURN_FALSE;
}
memc_user_data->has_sasl_data = 1;
status = memcached_set_sasl_auth_data(intern->memc, ZSTR_VAL(user), ZSTR_VAL(pass));
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
#endif /* HAVE_MEMCACHED_SASL */
#ifdef HAVE_MEMCACHED_SET_ENCODING_KEY
/* {{{ Memcached::setEncodingKey(string key)
Sets AES encryption key (libmemcached 1.0.6 and higher) */
static PHP_METHOD(Memcached, setEncodingKey)
{
MEMC_METHOD_INIT_VARS;
memcached_return status;
zend_string *key;
/* "S" */
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(key)
ZEND_PARSE_PARAMETERS_END();
MEMC_METHOD_FETCH_OBJECT;
// libmemcached < 1.0.18 cannot handle a change of encoding key. Warn about this and return false.
#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018
if (memc_user_data->encoding_enabled) {
php_error_docref(NULL, E_WARNING, "libmemcached versions less than 1.0.18 cannot change encoding key");
RETURN_FALSE;
}
#endif
status = memcached_set_encoding_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key));
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}
memc_user_data->encoding_enabled = 1;
RETURN_TRUE;
}
/* }}} */
#endif /* HAVE_MEMCACHED_SET_ENCODING_KEY */
/* {{{ Memcached::getResultCode()
Returns the result code from the last operation */
static PHP_METHOD(Memcached, getResultCode)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
RETURN_LONG(intern->rescode);
}
/* }}} */
/* {{{ Memcached::getResultMessage()
Returns the result message from the last operation */
static PHP_METHOD(Memcached, getResultMessage)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
switch (intern->rescode) {
case MEMC_RES_PAYLOAD_FAILURE:
RETURN_STRING("PAYLOAD FAILURE");
break;
case MEMCACHED_ERRNO:
case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE:
case MEMCACHED_UNKNOWN_READ_FAILURE:
if (intern->memc_errno) {
zend_string *str = strpprintf(0, "%s: %s",
memcached_strerror(intern->memc, (memcached_return)intern->rescode), strerror(intern->memc_errno));
RETURN_STR(str);
}
/* Fall through */
default:
RETURN_STRING(memcached_strerror(intern->memc, (memcached_return)intern->rescode));
break;
}
}
/* }}} */
/* {{{ Memcached::isPersistent()
Returns the true if instance uses a persistent connection */
static PHP_METHOD(Memcached, isPersistent)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
RETURN_BOOL(memc_user_data->is_persistent);
}
/* }}} */
/* {{{ Memcached::isPristine()
Returns the true if instance is recently created */
static PHP_METHOD(Memcached, isPristine)
{
MEMC_METHOD_INIT_VARS;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
MEMC_METHOD_FETCH_OBJECT;
RETURN_BOOL(intern->is_pristine);
}
/* }}} */
/****************************************
Internal support code
****************************************/
/* {{{ constructor/destructor */
static
void php_memc_destroy(memcached_st *memc, php_memc_user_data_t *memc_user_data)
{
#ifdef HAVE_MEMCACHED_SASL
if (memc_user_data->has_sasl_data) {
memcached_destroy_sasl_auth_data(memc);
}
#endif
memcached_free(memc);
pefree(memc_user_data, memc_user_data->is_persistent);
}
static
void php_memc_object_free_storage(zend_object *object)
{
php_memc_object_t *intern = php_memc_fetch_object(object);
if (intern->memc) {
php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc);
if (!memc_user_data->is_persistent) {
php_memc_destroy(intern->memc, memc_user_data);
}
}
intern->memc = NULL;
zend_object_std_dtor(&intern->zo);
}
static
zend_object *php_memc_object_new(zend_class_entry *ce)
{
php_memc_object_t *intern = ecalloc(1, sizeof(php_memc_object_t) + zend_object_properties_size(ce));
zend_object_std_init(&intern->zo, ce);
object_properties_init(&intern->zo, ce);
intern->zo.handlers = &memcached_object_handlers;
return &intern->zo;
}
#ifdef HAVE_MEMCACHED_PROTOCOL
static
void php_memc_server_free_storage(zend_object *object)
{
php_memc_server_t *intern = php_memc_server_fetch_object(object);
zend_object_std_dtor(&intern->zo);
}
zend_object *php_memc_server_new(zend_class_entry *ce)
{
php_memc_server_t *intern;
intern = ecalloc(1, sizeof(php_memc_server_t) + zend_object_properties_size(ce));
zend_object_std_init(&intern->zo, ce);
object_properties_init(&intern->zo, ce);
intern->zo.handlers = &memcached_server_object_handlers;
return &intern->zo;
}
#endif
ZEND_RSRC_DTOR_FUNC(php_memc_dtor)
{
if (res->ptr) {
memcached_st *memc = (memcached_st *) res->ptr;
php_memc_destroy(memc, memcached_get_user_data(memc));
res->ptr = NULL;
}
}
/* }}} */
/* {{{ internal API functions */
static
memcached_return s_server_cursor_list_servers_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context)
{
zval array;
zval *return_value = (zval *) in_context;
array_init(&array);
add_assoc_string(&array, "host", (char*)memcached_server_name(instance));
add_assoc_long(&array, "port", memcached_server_port(instance));
add_assoc_string(&array, "type", (char*)memcached_server_type(instance));
/*
* API does not allow to get at this field.
add_assoc_long(array, "weight", instance->weight);
*/
add_next_index_zval(return_value, &array);
return MEMCACHED_SUCCESS;
}
static
memcached_return s_server_cursor_version_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context)
{
zend_string *address, *version;
zval rv, *return_value = (zval *)in_context;
#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000009
version = strpprintf(0, "%d.%d.%d",
memcached_server_major_version(instance),
memcached_server_minor_version(instance),
memcached_server_micro_version(instance));
#else
version = strpprintf(0, "%d.%d.%d",
instance->major_version,
instance->minor_version,
instance->micro_version);
#endif
address = strpprintf(0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance));
ZVAL_STR(&rv, version);
zend_hash_add(Z_ARRVAL_P(return_value), address, &rv);
zend_string_release(address);
return MEMCACHED_SUCCESS;
}
static
zend_string *s_decompress_value (const char *payload, size_t payload_len, uint32_t flags)
{
zend_string *buffer;
uint32_t stored_length;
unsigned long length;
zend_bool decompress_status = 0;
zend_bool is_fastlz = 0, is_zlib = 0;
if (payload_len < sizeof (uint32_t)) {
return NULL;
}
is_fastlz = MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ);
is_zlib = MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB);
if (!is_fastlz && !is_zlib) {
php_error_docref(NULL, E_WARNING, "could not decompress value: unrecognised compression type");
return NULL;
}
memcpy(&stored_length, payload, sizeof (uint32_t));
payload += sizeof (uint32_t);
payload_len -= sizeof (uint32_t);
buffer = zend_string_alloc (stored_length, 0);
if (is_fastlz) {
decompress_status = ((length = fastlz_decompress(payload, payload_len, &buffer->val, buffer->len)) > 0);
}
else if (is_zlib) {
decompress_status = (uncompress((Bytef *) buffer->val, &buffer->len, (Bytef *)payload, payload_len) == Z_OK);
}
ZSTR_VAL(buffer)[stored_length] = '\0';
if (!decompress_status) {
php_error_docref(NULL, E_WARNING, "could not decompress value");
zend_string_release (buffer);
return NULL;
}
zend_string_forget_hash_val(buffer);
return buffer;
}
static
zend_bool s_unserialize_value (memcached_st *memc, int val_type, zend_string *payload, zval *return_value)
{
switch (val_type) {
case MEMC_VAL_IS_SERIALIZED:
{
php_unserialize_data_t var_hash;
const unsigned char *p, *max;
p = (const unsigned char *) ZSTR_VAL(payload);
max = p + ZSTR_LEN(payload);
PHP_VAR_UNSERIALIZE_INIT(var_hash);
if (!php_var_unserialize(return_value, &p, max, &var_hash)) {
zval_ptr_dtor(return_value);
ZVAL_FALSE(return_value);
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
php_error_docref(NULL, E_WARNING, "could not unserialize value");
return 0;
}
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
}
break;
case MEMC_VAL_IS_IGBINARY:
#ifdef HAVE_MEMCACHED_IGBINARY
if (igbinary_unserialize((uint8_t *) ZSTR_VAL(payload), ZSTR_LEN(payload), return_value)) {
ZVAL_FALSE(return_value);
php_error_docref(NULL, E_WARNING, "could not unserialize value with igbinary");
return 0;
}
#else
ZVAL_FALSE(return_value);
php_error_docref(NULL, E_WARNING, "could not unserialize value, no igbinary support");
return 0;
#endif
break;
case MEMC_VAL_IS_JSON:
#ifdef HAVE_JSON_API
{
php_memc_user_data_t *memc_user_data = memcached_get_user_data(memc);
php_json_decode(return_value, ZSTR_VAL(payload), ZSTR_LEN(payload), (memc_user_data->serializer == SERIALIZER_JSON_ARRAY), PHP_JSON_PARSER_DEFAULT_DEPTH);
}
#else
ZVAL_FALSE(return_value);
php_error_docref(NULL, E_WARNING, "could not unserialize value, no json support");
return 0;
#endif
break;
case MEMC_VAL_IS_MSGPACK:
#ifdef HAVE_MEMCACHED_MSGPACK
php_msgpack_unserialize(return_value, ZSTR_VAL(payload), ZSTR_LEN(payload));
#else
ZVAL_FALSE(return_value);
php_error_docref(NULL, E_WARNING, "could not unserialize value, no msgpack support");
return 0;
#endif
break;
}
return 1;
}
static
zend_bool s_memcached_result_to_zval(memcached_st *memc, memcached_result_st *result, zval *return_value)
{
zend_string *data;
const char *payload;
size_t payload_len;
uint32_t flags;
zend_bool retval = 1;
payload = memcached_result_value(result);
payload_len = memcached_result_length(result);
flags = memcached_result_flags(result);
if (!payload && payload_len > 0) {
php_error_docref(NULL, E_WARNING, "Could not handle non-existing value of length %zu", payload_len);
return 0;
}
if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSED)) {
data = s_decompress_value (payload, payload_len, flags);
if (!data) {
return 0;
}
} else {
data = zend_string_init(payload, payload_len, 0);
}
switch (MEMC_VAL_GET_TYPE(flags)) {
case MEMC_VAL_IS_STRING:
ZVAL_STR_COPY(return_value, data);
break;
case MEMC_VAL_IS_LONG:
ZVAL_LONG(return_value, strtol(ZSTR_VAL(data), NULL, 10));
break;
case MEMC_VAL_IS_DOUBLE:
{
if (zend_string_equals_literal(data, "Infinity")) {
ZVAL_DOUBLE(return_value, php_get_inf());
}
else if (zend_string_equals_literal(data, "-Infinity")) {
ZVAL_DOUBLE(return_value, -php_get_inf());
}
else if (zend_string_equals_literal(data, "NaN")) {
ZVAL_DOUBLE(return_value, php_get_nan());
}
else {
ZVAL_DOUBLE(return_value, zend_strtod(ZSTR_VAL(data), NULL));
}
}
break;
case MEMC_VAL_IS_BOOL:
ZVAL_BOOL(return_value, payload_len > 0 && ZSTR_VAL(data)[0] == '1');
break;
case MEMC_VAL_IS_SERIALIZED:
case MEMC_VAL_IS_IGBINARY:
case MEMC_VAL_IS_JSON:
case MEMC_VAL_IS_MSGPACK:
retval = s_unserialize_value (memc, MEMC_VAL_GET_TYPE(flags), data, return_value);
break;
default:
php_error_docref(NULL, E_WARNING, "unknown payload type");
break;
}
zend_string_release(data);
return retval;
}
PHP_MEMCACHED_API
zend_class_entry *php_memc_get_ce(void)
{
return memcached_ce;
}
PHP_MEMCACHED_API
zend_class_entry *php_memc_get_exception(void)
{
return memcached_exception_ce;
}
PHP_MEMCACHED_API
zend_class_entry *php_memc_get_exception_base(int root)
{
#ifdef HAVE_SPL
if (!root) {
if (!spl_ce_RuntimeException) {
zend_class_entry *pce;
zval *pce_z;
if ((pce_z = zend_hash_str_find(CG(class_table),
"runtimeexception",
sizeof("RuntimeException") - 1)) != NULL) {
pce = Z_CE_P(pce_z);
spl_ce_RuntimeException = pce;
return pce;
}
} else {
return spl_ce_RuntimeException;
}
}
#endif
return zend_exception_get_default();
}
#ifdef HAVE_MEMCACHED_PROTOCOL
static
void s_destroy_cb (zend_fcall_info *fci)
{
if (fci->size > 0) {
zval_ptr_dtor(&fci->function_name);
if (fci->object) {
OBJ_RELEASE(fci->object);
}
}
}
static
PHP_METHOD(MemcachedServer, run)
{
int i;
zend_bool rc;
zend_string *address;
php_memc_server_t *intern;
intern = Z_MEMC_SERVER_P(getThis());
/* "S" */
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(address)
ZEND_PARSE_PARAMETERS_END();
rc = php_memc_proto_handler_run(intern->handler, address);
for (i = MEMC_SERVER_ON_MIN + 1; i < MEMC_SERVER_ON_MAX; i++) {
s_destroy_cb(&MEMC_SERVER_G(callbacks) [i].fci);
}
RETURN_BOOL(rc);
}
static
PHP_METHOD(MemcachedServer, on)
{
zend_long event;
zend_fcall_info fci;
zend_fcall_info_cache fci_cache;
zend_bool rc = 0;
/* "lf!" */
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_LONG(event)
Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
ZEND_PARSE_PARAMETERS_END();
if (event <= MEMC_SERVER_ON_MIN || event >= MEMC_SERVER_ON_MAX) {
RETURN_FALSE;
}
if (fci.size > 0) {
s_destroy_cb (&MEMC_SERVER_G(callbacks) [event].fci);
MEMC_SERVER_G(callbacks) [event].fci = fci;
MEMC_SERVER_G(callbacks) [event].fci_cache = fci_cache;
Z_TRY_ADDREF(fci.function_name);
if (fci.object) {
GC_ADDREF(fci.object);
}
}
RETURN_BOOL(rc);
}
#endif
/* {{{ methods arginfo */
ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0)
ZEND_ARG_INFO(0, persistent_id)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_getResultCode, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_getResultMessage, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_get, 0, 0, 1)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, cache_cb)
ZEND_ARG_INFO(0, get_flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getByKey, 0, 0, 2)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, cache_cb)
ZEND_ARG_INFO(0, get_flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getMulti, 0, 0, 1)
ZEND_ARG_ARRAY_INFO(0, keys, 0)
ZEND_ARG_INFO(0, get_flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getMultiByKey, 0, 0, 2)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_ARRAY_INFO(0, keys, 0)
ZEND_ARG_INFO(0, get_flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayed, 0, 0, 1)
ZEND_ARG_ARRAY_INFO(0, keys, 0)
ZEND_ARG_INFO(0, with_cas)
ZEND_ARG_INFO(0, value_cb)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayedByKey, 0, 0, 2)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_ARRAY_INFO(0, keys, 0)
ZEND_ARG_INFO(0, with_cas)
ZEND_ARG_INFO(0, value_cb)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_fetch, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_fetchAll, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_touch, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_touchByKey, 0, 0, 3)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_setMulti, 0, 0, 1)
ZEND_ARG_ARRAY_INFO(0, items, 0)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_setMultiByKey, 0, 0, 2)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_ARRAY_INFO(0, items, 0)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_add, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_addByKey, 0, 0, 3)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_replace, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_replaceByKey, 0, 0, 3)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_append, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_appendByKey, 0, 0, 3)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_prepend, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_prependByKey, 0, 0, 3)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_cas, 0, 0, 3)
ZEND_ARG_INFO(0, cas_token)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4)
ZEND_ARG_INFO(0, cas_token)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expiration)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_delete, 0, 0, 1)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, time)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMulti, 0, 0, 1)
ZEND_ARG_INFO(0, keys)
ZEND_ARG_INFO(0, time)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteByKey, 0, 0, 2)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, time)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMultiByKey, 0, 0, 2)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, keys)
ZEND_ARG_INFO(0, time)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_increment, 0, 0, 1)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, offset)
ZEND_ARG_INFO(0, initial_value)
ZEND_ARG_INFO(0, expiry)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_decrement, 0, 0, 1)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, offset)
ZEND_ARG_INFO(0, initial_value)
ZEND_ARG_INFO(0, expiry)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_incrementByKey, 0, 0, 2)
ZEND_ARG_INFO(0, server_key)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, offset)
ZEND_ARG_INFO(0, initial_value)
ZEND_ARG_INFO(0, expiry)
ZEND_END_ARG_INFO()
<