⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.19
Server IP:
178.33.27.10
Server:
Linux cpanel.dev-unit.com 3.10.0-1160.108.1.el7.x86_64 #1 SMP Thu Jan 25 16:17:31 UTC 2024 x86_64
Server Software:
Apache/2.4.57 (Unix) OpenSSL/1.0.2k-fips
PHP Version:
8.2.11
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
proc
/
self
/
root
/
usr
/
local
/
src
/
memcache-8.0
/
src
/
View File Name :
memcache.c
/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 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_0.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: Antony Dovgal <tony2001@phpclub.net> | | Mikael Johansson <mikael AT synd DOT info> | +----------------------------------------------------------------------+ */ /* $Id$ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "ext/standard/php_string.h" #include "php_memcache.h" /* True global resources - no need for thread safety here */ static int le_memcache_pool, le_memcache_server; static zend_class_entry *memcache_pool_ce; static zend_class_entry *memcache_ce; ZEND_EXTERN_MODULE_GLOBALS(memcache) /* {{{ memcache_functions[] */ #ifndef ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, ref, num, type) \ ZEND_BEGIN_ARG_INFO_EX(name, 0u, ref, num) #endif #ifndef ZEND_ARG_TYPE_MASK #define ZEND_ARG_TYPE_MASK(ref, name, mask, def) \ ZEND_ARG_INFO(ref, name) #endif ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_connect, 0, 0, 1) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, unused4) ZEND_ARG_INFO(0, unused5) ZEND_ARG_INFO(0, unused6) ZEND_ARG_INFO(0, unused7) ZEND_ARG_INFO(0, unugsed8) ZEND_END_ARG_INFO() #define arginfo_memcache_pconnect arginfo_memcache_connect ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_add_server, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_ARG_INFO(0, tcp_port) ZEND_ARG_INFO(0, persistent) ZEND_ARG_INFO(0, weight) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, retry_interval) ZEND_ARG_INFO(0, status) ZEND_ARG_INFO(0, failure_callback) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_server_params, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, retry_interval, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, status, _IS_BOOL, 0) ZEND_ARG_INFO(0, failure_callback) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_failure_callback, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_INFO(0, failure_callback, IS_CALLABLE, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_get_server_status, 0, 2, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_get_version, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_add, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0) #ifdef IS_MIXED ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) #else ZEND_ARG_INFO(0, value) #endif ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, cas, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_memcache_set arginfo_memcache_add #define arginfo_memcache_replace arginfo_memcache_add #define arginfo_memcache_cas arginfo_memcache_add #define arginfo_memcache_append arginfo_memcache_add #define arginfo_memcache_prepend arginfo_memcache_add #ifdef IS_MIXED ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_get, 0, 2, IS_MIXED, 0) #else ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_get, 0, 0, 1) #endif ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(1, flags) ZEND_ARG_INFO(1, cas) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_delete, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, exptime) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_debug, 0, 1, _IS_BOOL, 0) ZEND_ARG_INFO(0, on_off) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_get_stats, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_INFO(0, type, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, slabid, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_memcache_get_extended_stats arginfo_memcache_get_stats ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_compress_threshold, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_INFO(0, threshold, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, min_savings, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_increment, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY|MAY_BE_LONG) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, defval, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_memcache_decrement arginfo_memcache_increment ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_close, 0, 1, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_flush, 0, 1, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_INFO(0, delay, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_sasl_auth_data, 0, 3, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0) ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0) ZEND_END_ARG_INFO() zend_function_entry memcache_functions[] = { PHP_FE(memcache_connect, arginfo_memcache_connect) PHP_FE(memcache_pconnect, arginfo_memcache_pconnect) PHP_FE(memcache_add_server, arginfo_memcache_add_server) PHP_FE(memcache_set_server_params, arginfo_memcache_set_server_params) PHP_FE(memcache_set_failure_callback, arginfo_memcache_set_failure_callback) PHP_FE(memcache_get_server_status, arginfo_memcache_get_server_status) PHP_FE(memcache_get_version, arginfo_memcache_get_version) PHP_FE(memcache_add, arginfo_memcache_add) PHP_FE(memcache_set, arginfo_memcache_set) PHP_FE(memcache_replace, arginfo_memcache_replace) PHP_FE(memcache_cas, arginfo_memcache_cas) PHP_FE(memcache_append, arginfo_memcache_append) PHP_FE(memcache_prepend, arginfo_memcache_prepend) PHP_FE(memcache_get, arginfo_memcache_get) PHP_FE(memcache_delete, arginfo_memcache_delete) PHP_FE(memcache_debug, arginfo_memcache_debug) PHP_FE(memcache_get_stats, arginfo_memcache_get_stats) PHP_FE(memcache_get_extended_stats, arginfo_memcache_get_extended_stats) PHP_FE(memcache_set_compress_threshold, arginfo_memcache_set_compress_threshold) PHP_FE(memcache_increment, arginfo_memcache_increment) PHP_FE(memcache_decrement, arginfo_memcache_decrement) PHP_FE(memcache_close, arginfo_memcache_close) PHP_FE(memcache_flush, arginfo_memcache_flush) PHP_FE(memcache_set_sasl_auth_data, arginfo_memcache_set_sasl_auth_data) ZEND_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_pool_object_connect, 0, 0, 1) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, tcp_port) ZEND_ARG_INFO(0, udp_port) ZEND_ARG_INFO(0, persistent) ZEND_ARG_INFO(0, weight) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, retry_interval) ZEND_END_ARG_INFO() #define arginfo_memcache_object_connect arginfo_memcache_connect #define arginfo_memcache_object_pconnect arginfo_memcache_connect ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_pool_object_addserver, 0, 1, _IS_BOOL, 0) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, tcp_port) ZEND_ARG_INFO(0, udp_port) ZEND_ARG_INFO(0, persistent) ZEND_ARG_INFO(0, weight) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, retry_interval) ZEND_ARG_INFO(0, status) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_addserver, 0, 1, _IS_BOOL, 0) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, tcp_port) ZEND_ARG_INFO(0, persistent) ZEND_ARG_INFO(0, weight) ZEND_ARG_INFO(0, timeout) ZEND_ARG_INFO(0, retry_interval) ZEND_ARG_INFO(0, status) ZEND_ARG_INFO(0, failure_callback) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setserverparams, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, retry_interval, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, status, _IS_BOOL, 0) ZEND_ARG_INFO(0, failure_callback) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setfailurecallback, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, failure_callback, IS_CALLABLE, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_getserverstatus, 0, 1, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_findserver, 0, 1, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_getversion, 0, 0, MAY_BE_STRING|MAY_BE_BOOL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_add, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0) #ifdef IS_MIXED ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) #else ZEND_ARG_INFO(0, value) #endif ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, cas, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_memcache_object_set arginfo_memcache_object_add #define arginfo_memcache_object_replace arginfo_memcache_object_add #define arginfo_memcache_object_set arginfo_memcache_object_add #define arginfo_memcache_object_cas arginfo_memcache_object_add #define arginfo_memcache_object_append arginfo_memcache_object_add #define arginfo_memcache_object_prepend arginfo_memcache_object_add #ifdef IS_MIXED ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_get, 0, 1, IS_MIXED, 0) #else ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_object_get, 0, 0, 1) #endif ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0) #ifdef IS_MIXED ZEND_ARG_TYPE_INFO(1, flags, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(1, cas, IS_MIXED, 0) #else ZEND_ARG_INFO(1, flags) ZEND_ARG_INFO(1, cas) #endif ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_delete, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_getstats, 0, 0, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, type, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, slabid, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_memcache_object_getextendedstats arginfo_memcache_object_getstats ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setcompressthreshold, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, threshold, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, min_savings, IS_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_increment, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY|MAY_BE_LONG) ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, defval, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_memcache_object_decrement arginfo_memcache_object_increment ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_close, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_flush, 0, 0, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, delay, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setSaslAuthData, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0) ZEND_END_ARG_INFO() static zend_function_entry php_memcache_pool_class_functions[] = { PHP_NAMED_FE(connect, zif_memcache_pool_connect, arginfo_memcache_pool_object_connect) PHP_NAMED_FE(addserver, zif_memcache_pool_addserver, arginfo_memcache_pool_object_addserver) PHP_FALIAS(setserverparams, memcache_set_server_params, arginfo_memcache_object_setserverparams) PHP_FALIAS(setfailurecallback, memcache_set_failure_callback, arginfo_memcache_object_setfailurecallback) PHP_FALIAS(getserverstatus, memcache_get_server_status, arginfo_memcache_object_getserverstatus) PHP_NAMED_FE(findserver, zif_memcache_pool_findserver, arginfo_memcache_object_findserver) PHP_FALIAS(getversion, memcache_get_version, arginfo_memcache_object_getversion) PHP_FALIAS(add, memcache_add, arginfo_memcache_object_add) PHP_FALIAS(set, memcache_set, arginfo_memcache_object_set) PHP_FALIAS(replace, memcache_replace, arginfo_memcache_object_replace) PHP_FALIAS(cas, memcache_cas, arginfo_memcache_object_cas) PHP_FALIAS(append, memcache_append, arginfo_memcache_object_append) PHP_FALIAS(prepend, memcache_prepend, arginfo_memcache_object_prepend) PHP_FALIAS(get, memcache_get, arginfo_memcache_object_get) PHP_FALIAS(delete, memcache_delete, arginfo_memcache_object_delete) PHP_FALIAS(getstats, memcache_get_stats, arginfo_memcache_object_getstats) PHP_FALIAS(getextendedstats, memcache_get_extended_stats, arginfo_memcache_object_getextendedstats) PHP_FALIAS(setcompressthreshold, memcache_set_compress_threshold, arginfo_memcache_object_setcompressthreshold) PHP_FALIAS(increment, memcache_increment, arginfo_memcache_object_increment) PHP_FALIAS(decrement, memcache_decrement, arginfo_memcache_object_decrement) PHP_FALIAS(close, memcache_close, arginfo_memcache_object_close) PHP_FALIAS(flush, memcache_flush, arginfo_memcache_object_flush) PHP_FALIAS(setSaslAuthData, memcache_set_sasl_auth_data, arginfo_memcache_object_setSaslAuthData) ZEND_FE_END }; static zend_function_entry php_memcache_class_functions[] = { PHP_ME_MAPPING(connect, memcache_connect, arginfo_memcache_object_connect, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(pconnect, memcache_pconnect, arginfo_memcache_object_pconnect, ZEND_ACC_PUBLIC) PHP_ME_MAPPING(addserver, memcache_add_server, arginfo_memcache_object_addserver, ZEND_ACC_PUBLIC) ZEND_FE_END }; /* }}} */ /* {{{ memcache_module_entry */ zend_module_entry memcache_module_entry = { STANDARD_MODULE_HEADER, "memcache", memcache_functions, PHP_MINIT(memcache), PHP_MSHUTDOWN(memcache), PHP_RINIT(memcache), NULL, PHP_MINFO(memcache), PHP_MEMCACHE_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_MEMCACHE ZEND_GET_MODULE(memcache) #endif static PHP_INI_MH(OnUpdateChunkSize) /* {{{ */ { zend_long val; char *endptr = NULL; val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10); if (!endptr || (*endptr != '\0') || val <= 0) { php_error_docref(NULL, E_WARNING, "memcache.chunk_size must be a positive integer ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } /* }}} */ static PHP_INI_MH(OnUpdateFailoverAttempts) /* {{{ */ { zend_long val; char *endptr = NULL; val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10); if (!endptr || (*endptr != '\0') || val <= 0) { php_error_docref(NULL, E_WARNING, "memcache.max_failover_attempts must be a positive integer ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } /* }}} */ static PHP_INI_MH(OnUpdateProtocol) /* {{{ */ { if (!strcasecmp(ZSTR_VAL(new_value), "ascii")) { MEMCACHE_G(protocol) = MMC_ASCII_PROTOCOL; } else if (!strcasecmp(ZSTR_VAL(new_value), "binary")) { MEMCACHE_G(protocol) = MMC_BINARY_PROTOCOL; } else { php_error_docref(NULL, E_WARNING, "memcache.protocol must be in set {ascii, binary} ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return SUCCESS; } /* }}} */ static PHP_INI_MH(OnUpdateHashStrategy) /* {{{ */ { if (!strcasecmp(ZSTR_VAL(new_value), "standard")) { MEMCACHE_G(hash_strategy) = MMC_STANDARD_HASH; } else if (!strcasecmp(ZSTR_VAL(new_value), "consistent")) { MEMCACHE_G(hash_strategy) = MMC_CONSISTENT_HASH; } else { php_error_docref(NULL, E_WARNING, "memcache.hash_strategy must be in set {standard, consistent} ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return SUCCESS; } /* }}} */ static PHP_INI_MH(OnUpdateHashFunction) /* {{{ */ { if (!strcasecmp(ZSTR_VAL(new_value), "crc32")) { MEMCACHE_G(hash_function) = MMC_HASH_CRC32; } else if (!strcasecmp(ZSTR_VAL(new_value), "fnv")) { MEMCACHE_G(hash_function) = MMC_HASH_FNV1A; } else { php_error_docref(NULL, E_WARNING, "memcache.hash_function must be in set {crc32, fnv} ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return SUCCESS; } /* }}} */ static PHP_INI_MH(OnUpdateRedundancy) /* {{{ */ { zend_long val; char *endptr = NULL; val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10); if (!endptr || (*endptr != '\0') || val <= 0) { php_error_docref(NULL, E_WARNING, "memcache.redundancy must be a positive integer ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } /* }}} */ static PHP_INI_MH(OnUpdateCompressThreshold) /* {{{ */ { zend_long val; char *endptr = NULL; val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10); if (!endptr || (*endptr != '\0') || val < 0) { php_error_docref(NULL, E_WARNING, "memcache.compress_threshold must be a positive integer ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } /* }}} */ static PHP_INI_MH(OnUpdateLockTimeout) /* {{{ */ { zend_long val; char *endptr = NULL; val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10); if (!endptr || (*endptr != '\0') || val <= 0) { php_error_docref(NULL, E_WARNING, "memcache.lock_timeout must be a positive integer ('%s' given)", ZSTR_VAL(new_value)); return FAILURE; } return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } /* }}} */ static PHP_INI_MH(OnUpdatePrefixStaticKey) /* {{{ */ { int i; if (new_value) { for (i=0 ; i<ZSTR_LEN(new_value) ; i++) { if (ZSTR_VAL(new_value)[i]=='.') { php_error_docref(NULL, E_WARNING, "memcache.session_prefix_static_key cannot have dot inside (.)"); return FAILURE; } } } return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } /* }}} */ /* {{{ PHP_INI */ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("memcache.allow_failover", "1", PHP_INI_ALL, OnUpdateLong, allow_failover, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.max_failover_attempts", "20", PHP_INI_ALL, OnUpdateFailoverAttempts, max_failover_attempts, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.default_port", "11211", PHP_INI_ALL, OnUpdateLong, default_port, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.chunk_size", "32768", PHP_INI_ALL, OnUpdateChunkSize, chunk_size, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.protocol", "ascii", PHP_INI_ALL, OnUpdateProtocol, protocol, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.hash_strategy", "consistent", PHP_INI_ALL, OnUpdateHashStrategy, hash_strategy, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.hash_function", "crc32", PHP_INI_ALL, OnUpdateHashFunction, hash_function, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.redundancy", "1", PHP_INI_ALL, OnUpdateRedundancy, redundancy, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.session_redundancy", "2", PHP_INI_ALL, OnUpdateRedundancy, session_redundancy, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.compress_threshold", "20000", PHP_INI_ALL, OnUpdateCompressThreshold, compress_threshold, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.lock_timeout", "15", PHP_INI_ALL, OnUpdateLockTimeout, lock_timeout, zend_memcache_globals, memcache_globals) STD_PHP_INI_BOOLEAN("memcache.session_prefix_host_key", "0", PHP_INI_ALL, OnUpdateBool, session_prefix_host_key, zend_memcache_globals, memcache_globals) STD_PHP_INI_BOOLEAN("memcache.session_prefix_host_key_remove_www", "1", PHP_INI_ALL, OnUpdateBool, session_prefix_host_key_remove_www, zend_memcache_globals, memcache_globals) STD_PHP_INI_BOOLEAN("memcache.session_prefix_host_key_remove_subdomain", "0", PHP_INI_ALL, OnUpdateBool, session_prefix_host_key_remove_subdomain, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.session_prefix_static_key", NULL, PHP_INI_ALL, OnUpdatePrefixStaticKey, session_prefix_static_key, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.session_save_path", NULL, PHP_INI_ALL, OnUpdateString, session_save_path, zend_memcache_globals, memcache_globals) STD_PHP_INI_BOOLEAN("memcache.prefix_host_key", "0", PHP_INI_ALL, OnUpdateBool, prefix_host_key, zend_memcache_globals, memcache_globals) STD_PHP_INI_BOOLEAN("memcache.prefix_host_key_remove_www", "1", PHP_INI_ALL, OnUpdateBool, prefix_host_key_remove_www, zend_memcache_globals, memcache_globals) STD_PHP_INI_BOOLEAN("memcache.prefix_host_key_remove_subdomain", "0", PHP_INI_ALL, OnUpdateBool, prefix_host_key_remove_subdomain, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.prefix_static_key", NULL, PHP_INI_ALL, OnUpdatePrefixStaticKey, prefix_static_key, zend_memcache_globals, memcache_globals) PHP_INI_END() /* }}} */ /* {{{ internal function protos */ static void _mmc_pool_list_dtor(zend_resource*); static void _mmc_server_list_dtor(zend_resource*); static void php_mmc_set_failure_callback(mmc_pool_t *, zval *, zval *); static void php_mmc_failure_callback(mmc_pool_t *, mmc_t *, zval *); /* }}} */ /* {{{ php_memcache_init_globals() */ static void php_memcache_init_globals(zend_memcache_globals *memcache_globals_p) { MEMCACHE_G(hash_strategy) = MMC_STANDARD_HASH; MEMCACHE_G(hash_function) = MMC_HASH_CRC32; } /* }}} */ /* {{{ get_session_key_prefix */ static char *get_session_key_prefix() { char *server_name=NULL, *prefix=NULL; int static_key_len=0, server_name_len=0, i; zval *array, *token; if (MEMCACHE_G(session_prefix_static_key)) { static_key_len=strlen(MEMCACHE_G(session_prefix_static_key)); } zend_is_auto_global_str("_SERVER", sizeof("_SERVER")-1); if (MEMCACHE_G(session_prefix_host_key)) { if ((array = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1)) && Z_TYPE_P(array) == IS_ARRAY && (token = zend_hash_str_find(Z_ARRVAL_P(array), "SERVER_NAME", sizeof("SERVER_NAME")-1)) && Z_TYPE_P(token) == IS_STRING) { if (MEMCACHE_G(session_prefix_host_key_remove_www) && !strncasecmp("www.",Z_STRVAL_P(token),4)) { server_name=Z_STRVAL_P(token)+4; } else { server_name=Z_STRVAL_P(token); } if(MEMCACHE_G(session_prefix_host_key_remove_subdomain) && server_name) { int dots=0; char *dots_ptr[3]={NULL,NULL,NULL}; for (i=strlen(server_name) ; i>0 ; i--) { if (dots==sizeof(dots_ptr)) { break; } if (server_name[i]=='.') { dots_ptr[dots]=&server_name[i]; dots++; } } if (dots_ptr[1] && *(dots_ptr[1]+1)) { server_name=dots_ptr[1]+1; } } server_name_len=(strlen(server_name)); } } if (!static_key_len && !server_name_len) { return NULL; } prefix=emalloc(static_key_len + server_name_len + 1); if (static_key_len) memcpy(prefix, MEMCACHE_G(session_prefix_static_key), static_key_len); if (server_name_len) memcpy(prefix + static_key_len, server_name, server_name_len); prefix[static_key_len + server_name_len]='\0'; return prefix; } /* }}} */ /* get_key_prefix */ static char *get_key_prefix() { char *server_name=NULL, *prefix=NULL; int static_key_len=0, server_name_len=0, i; zval *array, *token; if (MEMCACHE_G(prefix_static_key)) { static_key_len=strlen(MEMCACHE_G(prefix_static_key)); } if (MEMCACHE_G(prefix_host_key)) { if ((array = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1)) && Z_TYPE_P(array) == IS_ARRAY && (token = zend_hash_str_find(Z_ARRVAL_P(array), "SERVER_NAME", sizeof("SERVER_NAME")-1)) && Z_TYPE_P(token) == IS_STRING) { if (MEMCACHE_G(prefix_host_key_remove_www) && !strncasecmp("www.",Z_STRVAL_P(token),4)) { server_name=Z_STRVAL_P(token)+4; } else { server_name=Z_STRVAL_P(token); } if(MEMCACHE_G(prefix_host_key_remove_subdomain) && server_name) { int dots=0; char *dots_ptr[3]={NULL,NULL,NULL}; for (i=strlen(server_name) ; i>0 ; i--) { if (dots==sizeof(dots_ptr)) { break; } if (server_name[i]=='.') { dots_ptr[dots]=&server_name[i]; dots++; } } if (dots_ptr[1] && *(dots_ptr[1]+1)) { server_name=dots_ptr[1]+1; } } server_name_len=(strlen(server_name)); } } if (!static_key_len && !server_name_len) { return NULL; } prefix=emalloc(static_key_len + server_name_len + 1); if (static_key_len) memcpy(prefix, MEMCACHE_G(prefix_static_key), static_key_len); if (server_name_len) memcpy(prefix + static_key_len, server_name, server_name_len); prefix[static_key_len + server_name_len]='\0'; return prefix; } /* }}} */ /* {{{ PHP_RINIT_FUNCTION */ PHP_RINIT_FUNCTION(memcache) { MEMCACHE_G(session_key_prefix) = get_session_key_prefix(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(memcache) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "MemcachePool", php_memcache_pool_class_functions); memcache_pool_ce = zend_register_internal_class(&ce); INIT_CLASS_ENTRY(ce, "Memcache", php_memcache_class_functions); memcache_ce = zend_register_internal_class_ex(&ce, memcache_pool_ce); le_memcache_pool = zend_register_list_destructors_ex(_mmc_pool_list_dtor, NULL, "memcache connection", module_number); le_memcache_server = zend_register_list_destructors_ex(NULL, _mmc_server_list_dtor, "persistent memcache connection", module_number); #ifdef ZTS ts_allocate_id(&memcache_globals_id, sizeof(zend_memcache_globals), (ts_allocate_ctor) php_memcache_init_globals, NULL); #else php_memcache_init_globals(&memcache_globals); #endif REGISTER_LONG_CONSTANT("MEMCACHE_COMPRESSED", MMC_COMPRESSED, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER1", MMC_RESERVED_APPLICATIONDEFINEDFLAG_12, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER2", MMC_RESERVED_APPLICATIONDEFINEDFLAG_13, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER3", MMC_RESERVED_APPLICATIONDEFINEDFLAG_14, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER4", MMC_RESERVED_APPLICATIONDEFINEDFLAG_15, CONST_CS | CONST_PERSISTENT); REGISTER_INI_ENTRIES(); #if HAVE_MEMCACHE_SESSION REGISTER_LONG_CONSTANT("MEMCACHE_HAVE_SESSION", 1, CONST_CS | CONST_PERSISTENT); php_session_register_module(ps_memcache_ptr); #else REGISTER_LONG_CONSTANT("MEMCACHE_HAVE_SESSION", 0, CONST_CS | CONST_PERSISTENT); #endif return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(memcache) { UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(memcache) { php_info_print_table_start(); php_info_print_table_header(2, "memcache support", "enabled"); php_info_print_table_row(2, "Version", PHP_MEMCACHE_VERSION); php_info_print_table_row(2, "Revision", "$Revision$"); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* ------------------ internal functions ------------------ */ static void _mmc_pool_list_dtor(zend_resource *rsrc) /* {{{ */ { mmc_pool_t *pool = (mmc_pool_t *)rsrc->ptr; if (!Z_ISUNDEF(pool->failure_callback_param)) { Z_DELREF(pool->failure_callback_param); ZVAL_UNDEF(&pool->failure_callback_param); } mmc_pool_free(pool); } /* }}} */ static void _mmc_server_list_dtor(zend_resource *rsrc) /* {{{ */ { mmc_server_free((mmc_t *)rsrc->ptr); } /* }}} */ static int mmc_get_pool(zval *id, mmc_pool_t **pool) /* {{{ */ { zval *zv; if (Z_TYPE_P(id) != IS_OBJECT || (zv = zend_hash_str_find(Z_OBJPROP_P(id), "connection", sizeof("connection")-1)) == NULL) { php_error_docref(NULL, E_WARNING, "No servers added to memcache connection"); return 0; } if (Z_TYPE_P(zv) != IS_RESOURCE || (*pool = zend_fetch_resource_ex(zv, "connection", le_memcache_pool)) == NULL) { php_error_docref(NULL, E_WARNING, "Invalid MemcachePool->connection member variable"); return 0; } return 1; } /* }}} */ int mmc_stored_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /* handles SET/ADD/REPLACE response, param is a zval pointer to store result into {{{ */ { zval *result = (zval *)param; if (response == MMC_OK) { if (Z_TYPE_P(result) == IS_NULL) { ZVAL_TRUE(result); } return MMC_REQUEST_DONE; } /* return FALSE or catch memory errors without failover */ if (response == MMC_RESPONSE_EXISTS || response == MMC_RESPONSE_OUT_OF_MEMORY || response == MMC_RESPONSE_TOO_LARGE || response == MMC_RESPONSE_CLIENT_ERROR) { ZVAL_FALSE(result); if (response != MMC_RESPONSE_EXISTS) { /* trigger notice but no need for failover */ php_error_docref(NULL, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, message, response); } return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0); } /* }}} */ static void php_mmc_store(INTERNAL_FUNCTION_PARAMETERS, int op) /* {{{ */ { mmc_pool_t *pool; mmc_request_t *request; zval *keys, *value = 0, *mmc_object = getThis(); zend_long flags = 0, exptime = 0, cas = 0; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|zlll", &mmc_object, memcache_pool_ce, &keys, &value, &flags, &exptime, &cas) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|zlll", &keys, &value, &flags, &exptime, &cas) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) { RETURN_FALSE; } RETVAL_NULL(); if (Z_TYPE_P(keys) == IS_ARRAY) { zend_string *key; zval *val; zend_ulong index; int release; ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(keys), index, key, val ) { if (key == NULL) { key = strpprintf(0, ZEND_ULONG_FMT, index); release = 1; } else { release = 0; } /* allocate request */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, return_value, mmc_pool_failover_handler, NULL); if (mmc_prepare_key_ex(ZSTR_VAL(key), ZSTR_LEN(key), request->key, &(request->key_len), MEMCACHE_G(key_prefix)) != MMC_OK) { php_error_docref(NULL, E_WARNING, "Invalid key"); mmc_pool_release(pool, request); if (release) { zend_string_release(key); } continue; } if (release) { zend_string_release(key); } /* assemble command */ if (pool->protocol->store(pool, request, op, request->key, request->key_len, flags, exptime, cas, val) != MMC_OK) { mmc_pool_release(pool, request); continue; } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) { continue; } /* begin sending requests immediatly */ mmc_pool_select(pool); } ZEND_HASH_FOREACH_END(); } else if (value) { /* allocate request */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, return_value, mmc_pool_failover_handler, NULL); if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) { php_error_docref(NULL, E_WARNING, "Invalid key"); mmc_pool_release(pool, request); RETURN_FALSE; } /* assemble command */ if (pool->protocol->store(pool, request, op, request->key, request->key_len, flags, exptime, cas, value) != MMC_OK) { mmc_pool_release(pool, request); RETURN_FALSE; } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) { RETURN_FALSE; } } else { WRONG_PARAM_COUNT; } /* execute all requests */ mmc_pool_run(pool); if (Z_TYPE_P(return_value) == IS_NULL) { RETVAL_FALSE; } } /* }}} */ int mmc_numeric_response_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /* handles a mutate response line, param is a zval pointer to store result into {{{ */ { zval *result = (zval *)param; if (response == MMC_OK) { if (Z_TYPE_P(result) == IS_ARRAY) { add_assoc_bool_ex(result, request->key, request->key_len + 1, 1); } else if (Z_TYPE_P(result) == IS_NULL) { /* switch only from null to true, not from false to true */ ZVAL_TRUE(result); } return MMC_REQUEST_DONE; } if (response == MMC_RESPONSE_NOT_FOUND || response == MMC_RESPONSE_CLIENT_ERROR) { if (Z_TYPE_P(result) == IS_ARRAY) { add_assoc_bool_ex(result, request->key, request->key_len + 1, 0); } else { ZVAL_FALSE(result); } if (response != MMC_RESPONSE_NOT_FOUND) { php_error_docref(NULL, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, message, response); } return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0); } /* }}} */ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int invert) /* sends one or several commands which have a single optional numeric parameter (incr, decr, delete) {{{ */ { mmc_pool_t *pool; zval *mmc_object = getThis(); zval *keys; zend_long value = 1, defval = 0, exptime = 0; mmc_request_t *request; void *value_handler_param[3]; int defval_used = 0; if (mmc_object == NULL) { if (deleted) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|l", &mmc_object, memcache_pool_ce, &keys, &value) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|lll", &mmc_object, memcache_pool_ce, &keys, &value, &defval, &exptime) == FAILURE) { return; } defval_used = ZEND_NUM_ARGS() >= 4; } } else { if (deleted) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &keys, &value) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|lll", &keys, &value, &defval, &exptime) == FAILURE) { return; } defval_used = ZEND_NUM_ARGS() >= 3; } } if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) { RETURN_FALSE; } value_handler_param[0] = return_value; value_handler_param[1] = NULL; value_handler_param[2] = NULL; if (Z_TYPE_P(keys) == IS_ARRAY) { zval *key; if (deleted) { /* changed to true/false by mmc_numeric_response_handler */ RETVAL_NULL(); } else { /* populated with responses by mmc_numeric_response_handler and mmc_value_handler_multi */ array_init(return_value); } ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), key) { /* allocate request */ request = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_numeric_response_handler, return_value, mmc_pool_failover_handler, NULL); request->value_handler = mmc_value_handler_multi; request->value_handler_param = value_handler_param; if (mmc_prepare_key(key, request->key, &(request->key_len)) != MMC_OK) { mmc_pool_release(pool, request); php_error_docref(NULL, E_WARNING, "Invalid key"); continue; } if (deleted) { pool->protocol->delete(request, request->key, request->key_len, exptime); } else { pool->protocol->mutate(request, key, request->key, request->key_len, invert ? -value : value, defval, defval_used, exptime); } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) { continue; } /* begin sending requests immediatly */ mmc_pool_select(pool); } ZEND_HASH_FOREACH_END(); } else { /* changed to true/false by mmc_numeric_response_handler or set to a value * by mmc_value_handler_single if incr/decr returns one */ RETVAL_NULL(); /* allocate request */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_numeric_response_handler, return_value, mmc_pool_failover_handler, NULL); request->value_handler = mmc_value_handler_single; request->value_handler_param = value_handler_param; if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) { mmc_pool_release(pool, request); php_error_docref(NULL, E_WARNING, "Invalid key"); RETURN_FALSE; } if (deleted) { pool->protocol->delete(request, request->key, request->key_len, exptime); } else { pool->protocol->mutate(request, keys, request->key, request->key_len, invert ? -value : value, defval, defval_used, exptime); } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) { RETURN_FALSE; } } /* execute all requests */ mmc_pool_run(pool); if (Z_TYPE_P(return_value) == IS_NULL) { RETURN_FALSE; } } /* }}} */ /*TODO: in php73, we should use zend_register_persistent_resource , e.g.: char *persistent_id; persistent_id = pemalloc(key_len + 1, 1); memcpy((char *)persistent_id, key, key_len+1); if (zend_register_persistent_resource ( (char*) persistent_id, key_len, mmc, le_memcache_server) == NULL) ; then not forget to pefree, check refcounts in _mmc_server_free / _mmc_server_list_dtor , etc. otherwise we will leak mem with persistent connections /run into other trouble with later versions */ mmc_t *mmc_find_persistent(const char *host, int host_len, unsigned short port, unsigned short udp_port, double timeout, int retry_interval) /* {{{ */ { mmc_t *mmc; zend_resource *le; char *key; int key_len; key_len = spprintf(&key, 0, "memcache:server:%s:%u:%u", host, port, udp_port); if ((le = zend_hash_str_find_ptr(&EG(persistent_list), key, key_len)) == NULL) { mmc = mmc_server_new(host, host_len, port, udp_port, 1, timeout, retry_interval); le = zend_register_resource(mmc, le_memcache_server); /* register new persistent connection */ if (zend_hash_str_update_mem(&EG(persistent_list), key, key_len, le, sizeof(*le)) == NULL) { mmc_server_free(mmc); mmc = NULL; } else { MEMCACHE_LIST_INSERT(mmc, le_memcache_server); } } else if (le->type != le_memcache_server || le->ptr == NULL) { zend_hash_str_del(&EG(persistent_list), key, key_len); mmc = mmc_server_new(host, host_len, port, udp_port, 1, timeout, retry_interval); le->type = le_memcache_server; le->ptr = mmc; #if PHP_VERSION_ID < 70300 GC_REFCOUNT(le) = 1; #else GC_SET_REFCOUNT(le, 1); #endif /* register new persistent connection */ if (zend_hash_str_update_mem(&EG(persistent_list), key, key_len, le, sizeof(*le)) == NULL) { mmc_server_free(mmc); mmc = NULL; } else { MEMCACHE_LIST_INSERT(mmc, le_memcache_server); } } else { mmc = (mmc_t *)le->ptr; mmc->timeout = double_to_timeval(timeout); mmc->tcp.retry_interval = retry_interval; /* attempt to reconnect this node before failover in case connection has gone away */ if (mmc->tcp.status == MMC_STATUS_CONNECTED) { mmc->tcp.status = MMC_STATUS_UNKNOWN; } if (mmc->udp.status == MMC_STATUS_CONNECTED) { mmc->udp.status = MMC_STATUS_UNKNOWN; } } efree(key); return mmc; } /* }}} */ static mmc_t *php_mmc_pool_addserver( zval *mmc_object, const char *host, int host_len, long tcp_port, long udp_port, long weight, zend_bool persistent, double timeout, long retry_interval, zend_bool status, mmc_pool_t **pool_result) /* {{{ */ { zval *connection; mmc_pool_t *pool; mmc_t *mmc; zend_resource *list_res; if (weight < 1) { php_error_docref(NULL, E_WARNING, "weight must be a positive integer"); return NULL; } if (tcp_port > 65635 || tcp_port < 0) { php_error_docref(NULL, E_WARNING, "invalid tcp port number"); return NULL; } if (udp_port > 65635 || udp_port < 0) { php_error_docref(NULL, E_WARNING, "invalid udp port number"); return NULL; } /* initialize pool if need be */ if ((connection = zend_hash_str_find(Z_OBJPROP_P(mmc_object), "connection", sizeof("connection")-1)) == NULL) { pool = mmc_pool_new(); pool->failure_callback = (mmc_failure_callback) &php_mmc_failure_callback; list_res = zend_register_resource(pool, le_memcache_pool); add_property_resource(mmc_object, "connection", list_res); #if PHP_VERSION_ID < 70300 GC_REFCOUNT(list_res)++; #else GC_ADDREF(list_res); #endif } else { pool = zend_fetch_resource_ex(connection, "connection", le_memcache_pool); if (!pool) { php_error_docref(NULL, E_WARNING, "Unknown connection identifier"); return NULL; } } /* binary protocol isn't support over UDP yet */ if (udp_port && pool->protocol == &mmc_binary_protocol) { php_error_docref(NULL, E_NOTICE, "binary protocol isn't support over UDP, defaulting to TCP"); udp_port = 0; } /* lazy initialization of server struct */ if (persistent && status) { mmc = mmc_find_persistent(host, host_len, (unsigned short) tcp_port, (unsigned short) udp_port, timeout, retry_interval); } else { mmc = mmc_server_new(host, host_len, (unsigned short) tcp_port, (unsigned short) udp_port, 0, timeout, retry_interval); } /* add server in failed mode */ if (!status) { mmc->tcp.status = MMC_STATUS_FAILED; mmc->udp.status = MMC_STATUS_FAILED; } mmc_pool_add(pool, mmc, weight); if (pool_result != NULL) { *pool_result = pool; } if (pool->protocol == &mmc_binary_protocol) { zval rv1, rv2; #if PHP_VERSION_ID >= 80000 zval *username = zend_read_property(memcache_ce, Z_OBJ_P(mmc_object), "username", strlen("username"), 1, &rv1); zval *password = zend_read_property(memcache_ce, Z_OBJ_P(mmc_object), "password", strlen("password"), 1, &rv2); #else zval *username = zend_read_property(memcache_ce, mmc_object, "username", strlen("username"), 1, &rv1); zval *password = zend_read_property(memcache_ce, mmc_object, "password", strlen("password"), 1, &rv2); #endif if (Z_TYPE_P(username) == IS_STRING && Z_TYPE_P(password) == IS_STRING) { if (Z_STRLEN_P(username) > 1 && Z_STRLEN_P(password) > 1) { mmc_request_t *request; zval sasl_value; /* allocate request */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, &sasl_value, mmc_pool_failover_handler, NULL); pool->protocol->set_sasl_auth_data(pool, request, Z_STRVAL_P(username), Z_STRVAL_P(password)); /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) { return NULL; } } } } return mmc; } /* }}} */ static void php_mmc_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool persistent) /* {{{ */ { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc; char *host; size_t host_len; zend_long tcp_port = MEMCACHE_G(default_port); double timeout = MMC_DEFAULT_TIMEOUT; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ld", &host, &host_len, &tcp_port, &timeout) == FAILURE) { return; } /* initialize pool and object if need be */ if (!mmc_object) { zend_resource *list_res; pool = mmc_pool_new(); pool->failure_callback = (mmc_failure_callback) &php_mmc_failure_callback; list_res = zend_register_resource(pool, le_memcache_pool); mmc_object = return_value; object_init_ex(mmc_object, memcache_ce); add_property_resource(mmc_object, "connection", list_res); #if PHP_VERSION_ID < 70300 GC_REFCOUNT(list_res)++; #else GC_ADDREF(list_res); #endif } else { RETVAL_TRUE; } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, 0, 1, persistent, timeout, MMC_DEFAULT_RETRY, 1, NULL); if (mmc == NULL) { RETURN_FALSE; } /* force a reconnect attempt if stream EOF */ if (mmc->tcp.stream != NULL && php_stream_eof(mmc->tcp.stream)) { mmc_server_disconnect(mmc, &(mmc->tcp)); } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } /* force a tcp connect (if not persistently connected) */ if (mmc_pool_open(pool, mmc, &(mmc->tcp), 0) != MMC_OK) { php_error_docref(NULL, E_WARNING, "Can't connect to %s:%d, %s (%d)", host, mmc->tcp.port, mmc->error ? mmc->error : "Unknown error", mmc->errnum); RETURN_FALSE; } } /* }}} */ /* * STAT 6:chunk_size 64 */ static int mmc_stats_parse_stat(char *start, char *end, zval *result) /* {{{ */ { char *key; const char *space, *colon; zend_long index = 0; if (Z_TYPE_P(result) != IS_ARRAY) { array_init(result); } /* find space delimiting key and value */ if ((space = php_memnstr(start, " ", 1, end)) == NULL) { return 0; } /* find colon delimiting subkeys */ if ((colon = php_memnstr(start, ":", 1, space - 1)) != NULL) { zval *element, *elem; zval new_element; key = estrndup(start, colon - start); /* find existing or create subkey array in result */ if ((is_numeric_string(key, colon - start, &index, NULL, 0) && (elem = zend_hash_index_find(Z_ARRVAL_P(result), index)) != NULL) || (elem = zend_hash_str_find(Z_ARRVAL_P(result), key, colon - start)) != NULL) { element = elem; } else { array_init(&new_element); add_assoc_zval_ex(result, key, colon - start, &new_element); element = &new_element; } efree(key); return mmc_stats_parse_stat(((char *) colon) + 1, end, element); } /* no more subkeys, add value under last subkey */ key = estrndup(start, space - start); add_assoc_stringl_ex(result, key, ((char *) space) - start, ((char *) space) + 1, end - ((char *) space)); efree(key); return 1; } /* }}} */ /* * ITEM test_key [3 b; 1157099416 s] */ static int mmc_stats_parse_item(char *start, char *end, zval *result) /* {{{ */ { char *key; const char *space, *value, *value_end; zval element; if (Z_TYPE_P(result) != IS_ARRAY) { array_init(result); } /* find space delimiting key and value */ if ((space = php_memnstr(start, " ", 1, end)) == NULL) { return 0; } array_init(&element); /* parse each contained value */ for (value = php_memnstr(space, "[", 1, end); value != NULL && value <= end; value = php_memnstr(value + 1, ";", 1, end)) { do { value++; } while (*value == ' ' && value <= end); if (value <= end && (value_end = php_memnstr(value, " ", 1, end)) != NULL && value_end <= end) { add_next_index_stringl(&element, value, value_end - value); } } /* add parsed values under key */ key = estrndup(start, space - start); add_assoc_zval_ex(result, key, space - start, &element); efree(key); return 1; } /* }}} */ static int mmc_stats_parse_generic(char *start, char *end, zval *result) /* {{{ */ { const char *space; char *key; if (Z_TYPE_P(result) != IS_ARRAY) { array_init(result); } if (start < end) { if ((space = php_memnstr(start, " ", 1, end)) != NULL) { key = estrndup(start, space - start); add_assoc_stringl_ex(result, key, ((char *) space) - start + 1, ((char *) space) + 1, end - ((char *) space)); efree(key); } else { add_next_index_stringl(result, start, end - start); } } else { return 0; } return 1; } /* }}} */ static void php_mmc_failure_callback(mmc_pool_t *pool, mmc_t *mmc, zval *param) /* {{{ */ { zval *callback; /* check for userspace callback */ if (!Z_ISUNDEF_P(param) && (callback = zend_hash_str_find(Z_OBJPROP_P((zval *)param), "_failureCallback", sizeof("_failureCallback")-1)) != NULL && Z_TYPE_P(callback) != IS_NULL) { if (MEMCACHE_IS_CALLABLE(callback, 0, NULL)) { zval retval; zval *host, *tcp_port, *udp_port, *error, *errnum; zval params[5]; ZVAL_UNDEF(&retval); host = ¶ms[0]; tcp_port = ¶ms[1]; udp_port = ¶ms[2]; error = ¶ms[3]; errnum = ¶ms[4]; ZVAL_STRING(host, mmc->host); ZVAL_LONG(tcp_port, mmc->tcp.port); ZVAL_LONG(udp_port, mmc->udp.port); if (mmc->error != NULL) { ZVAL_STRING(error, mmc->error); } else { ZVAL_NULL(error); } ZVAL_LONG(errnum, mmc->errnum); call_user_function(EG(function_table), NULL, callback, &retval, 5, params); zval_ptr_dtor(host); zval_ptr_dtor(tcp_port); zval_ptr_dtor(udp_port); zval_ptr_dtor(error); zval_ptr_dtor(errnum); if (Z_TYPE(retval) != IS_UNDEF) { zval_ptr_dtor(&retval); } } else { php_mmc_set_failure_callback(pool, param, NULL); php_error_docref(NULL, E_WARNING, "Invalid failure callback"); } } else { php_error_docref(NULL, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, mmc->error, mmc->errnum); } } /* }}} */ static void php_mmc_set_failure_callback(mmc_pool_t *pool, zval *mmc_object, zval *callback) /* {{{ */ { // Decrease refcount of old mmc_object if (!Z_ISUNDEF(pool->failure_callback_param)) { Z_DELREF(pool->failure_callback_param); } if (callback != NULL) { zval callback_tmp; callback_tmp = *callback; zval_copy_ctor(&callback_tmp); add_property_zval(mmc_object, "_failureCallback", &callback_tmp); zval_ptr_dtor(&callback_tmp); pool->failure_callback_param = *mmc_object; Z_ADDREF_P(mmc_object); } else { add_property_null(mmc_object, "_failureCallback"); ZVAL_UNDEF(&pool->failure_callback_param); } } /* }}} */ /* ---------------- module functions ---------------- */ /* {{{ proto bool MemcachePool::connect(string host [, int tcp_port [, int udp_port [, bool persistent [, int weight [, double timeout [, int retry_interval] ] ] ] ] ]) Connects to server and returns a Memcache object */ PHP_NAMED_FUNCTION(zif_memcache_pool_connect) { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc; char *host; size_t host_len; zend_long tcp_port = MEMCACHE_G(default_port), udp_port = 0, weight = 1, retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool persistent = 1; MEMCACHE_G(key_prefix)=get_key_prefix(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llbldl", &host, &host_len, &tcp_port, &udp_port, &persistent, &weight, &timeout, &retry_interval) == FAILURE) { return; } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, udp_port, weight, persistent, timeout, retry_interval, 1, NULL); if (mmc == NULL) { RETURN_FALSE; } /* force a reconnect attempt if stream EOF */ if (mmc->tcp.stream != NULL && php_stream_eof(mmc->tcp.stream)) { mmc_server_disconnect(mmc, &(mmc->tcp)); } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } /* force a tcp connect (if not persistently connected) */ if (mmc_pool_open(pool, mmc, &(mmc->tcp), 0) != MMC_OK) { php_error_docref(NULL, E_WARNING, "Can't connect to %s:%d, %s (%d)", host, mmc->tcp.port, mmc->error ? mmc->error : "Unknown error", mmc->errnum); RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto object memcache_connect(string host [, int port [, double timeout ] ]) Connects to server and returns a Memcache object */ PHP_FUNCTION(memcache_connect) { MEMCACHE_G(key_prefix)=get_key_prefix(); php_mmc_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ proto object memcache_pconnect(string host [, int port [, double timeout ] ]) Connects to server and returns a Memcache object */ PHP_FUNCTION(memcache_pconnect) { MEMCACHE_G(key_prefix)=get_key_prefix(); php_mmc_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ proto bool MemcachePool::addServer(string host [, int tcp_port [, int udp_port [, bool persistent [, int weight [, double timeout [, int retry_interval [, bool status] ] ] ] ]) Adds a server to the pool */ PHP_NAMED_FUNCTION(zif_memcache_pool_addserver) { zval *mmc_object = getThis(); mmc_t *mmc; char *host; size_t host_len; zend_long tcp_port = MEMCACHE_G(default_port), udp_port = 0, weight = 1, retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool persistent = 1, status = 1; MEMCACHE_G(key_prefix)=get_key_prefix(); if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llbldlb", &host, &host_len, &tcp_port, &udp_port, &persistent, &weight, &timeout, &retry_interval, &status) == FAILURE) { return; } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, udp_port, weight, persistent, timeout, retry_interval, status, NULL); if (mmc == NULL) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto string MemcachePool::findServer(string key) Returns the server corresponding to a key */ PHP_NAMED_FUNCTION(zif_memcache_pool_findserver) { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc; zval *zkey; char key[MMC_MAX_KEY_LEN + 1]; unsigned int key_len; zend_string *hostname; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zkey) == FAILURE) { return; } if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) { RETURN_FALSE; } if (mmc_prepare_key(zkey, key, &key_len) != MMC_OK) { php_error_docref(NULL, E_WARNING, "Invalid key"); RETURN_FALSE; } mmc = mmc_pool_find(pool, key, key_len); hostname = strpprintf(0, "%s:%d", mmc->host, mmc->tcp.port); RETURN_STR(hostname); } /* }}} */ /* {{{ proto bool memcache_add_server(string host [, int port [, bool persistent [, int weight [, double timeout [, int retry_interval [, bool status [, callback failure_callback ] ] ] ] ] ] ]) Adds a connection to the pool. The order in which this function is called is significant */ PHP_FUNCTION(memcache_add_server) { zval *mmc_object = getThis(), *failure_callback = NULL; mmc_pool_t *pool; mmc_t *mmc; char *host; size_t host_len; zend_long tcp_port = MEMCACHE_G(default_port), weight = 1, retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool persistent = 1, status = 1; MEMCACHE_G(key_prefix)=get_key_prefix(); if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lbldlbz", &host, &host_len, &tcp_port, &persistent, &weight, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|lbldlbz", &mmc_object, memcache_ce, &host, &host_len, &tcp_port, &persistent, &weight, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) { if (!MEMCACHE_IS_CALLABLE(failure_callback, 0, NULL)) { php_error_docref(NULL, E_WARNING, "Invalid failure callback"); RETURN_FALSE; } } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, 0, weight, persistent, timeout, retry_interval, status, &pool); if (mmc == NULL) { RETURN_FALSE; } if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) { php_mmc_set_failure_callback(pool, mmc_object, failure_callback); } RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_set_server_params( string host [, int port [, double timeout [, int retry_interval [, bool status [, callback failure_callback ] ] ] ] ]) Changes server parameters at runtime */ PHP_FUNCTION(memcache_set_server_params) { zval *mmc_object = getThis(), *failure_callback = NULL; mmc_pool_t *pool; mmc_t *mmc = NULL; zend_long tcp_port = MEMCACHE_G(default_port), retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool status = 1; size_t host_len; int i; char *host; if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ldlbz", &host, &host_len, &tcp_port, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|ldlbz", &mmc_object, memcache_pool_ce, &host, &host_len, &tcp_port, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } for (i=0; i<pool->num_servers; i++) { if (!strcmp(pool->servers[i]->host, host) && pool->servers[i]->tcp.port == tcp_port) { mmc = pool->servers[i]; break; } } if (!mmc) { php_error_docref(NULL, E_WARNING, "Server not found in pool"); RETURN_FALSE; } if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) { if (!MEMCACHE_IS_CALLABLE(failure_callback, 0, NULL)) { php_error_docref(NULL, E_WARNING, "Invalid failure callback"); RETURN_FALSE; } } mmc->timeout = double_to_timeval(timeout); mmc->tcp.retry_interval = retry_interval; /* store the smallest timeout for any server */ if (timeval_to_double(mmc->timeout) < timeval_to_double(pool->timeout)) { pool->timeout = mmc->timeout; } if (!status) { mmc->tcp.status = MMC_STATUS_FAILED; mmc->udp.status = MMC_STATUS_FAILED; } else { if (mmc->tcp.status == MMC_STATUS_FAILED) { mmc->tcp.status = MMC_STATUS_DISCONNECTED; } if (mmc->udp.status == MMC_STATUS_FAILED) { mmc->udp.status = MMC_STATUS_DISCONNECTED; } } if (failure_callback != NULL) { if (Z_TYPE_P(failure_callback) != IS_NULL) { php_mmc_set_failure_callback(pool, mmc_object, failure_callback); } else { php_mmc_set_failure_callback(pool, mmc_object, NULL); } } RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_set_failure_callback( callback failure_callback ) Changes the failover callback */ PHP_FUNCTION(memcache_set_failure_callback) { zval *mmc_object = getThis(), *failure_callback; mmc_pool_t *pool; if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &failure_callback) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &mmc_object, memcache_pool_ce, &failure_callback) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } if (Z_TYPE_P(failure_callback) != IS_NULL) { if (!MEMCACHE_IS_CALLABLE(failure_callback, 0, NULL)) { php_error_docref(NULL, E_WARNING, "Invalid failure callback"); RETURN_FALSE; } } if (Z_TYPE_P(failure_callback) != IS_NULL) { php_mmc_set_failure_callback(pool, mmc_object, failure_callback); } else { php_mmc_set_failure_callback(pool, mmc_object, NULL); } RETURN_TRUE; } /* }}} */ /* {{{ proto int memcache_get_server_status( string host [, int port ]) Returns server status (0 if server is failed, otherwise non-zero) */ PHP_FUNCTION(memcache_get_server_status) { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc = NULL; zend_long tcp_port = MEMCACHE_G(default_port); size_t host_len; int i; char *host; if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &host, &host_len, &tcp_port) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &mmc_object, memcache_pool_ce, &host, &host_len, &tcp_port) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } for (i=0; i<pool->num_servers; i++) { if (!strcmp(pool->servers[i]->host, host) && pool->servers[i]->tcp.port == tcp_port) { mmc = pool->servers[i]; break; } } if (!mmc) { php_error_docref(NULL, E_WARNING, "Server not found in pool"); RETURN_FALSE; } RETURN_LONG(mmc->tcp.status > MMC_STATUS_FAILED ? 1 : 0); } /* }}} */ static int mmc_version_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /* parses the VERSION response line, param is a zval pointer to store version into {{{ */ { if (response != MMC_RESPONSE_ERROR) { char *version = emalloc(message_len + 1); if (sscanf(message, "VERSION %s", version) == 1) { ZVAL_STRING((zval *)param, version); efree(version); } else { efree(version); ZVAL_STRINGL((zval *)param, (char *)message, message_len); } return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0); } /* }}} */ /* {{{ proto string memcache_get_version( object memcache ) Returns server's version */ PHP_FUNCTION(memcache_get_version) { mmc_pool_t *pool; zval *mmc_object = getThis(); int i; mmc_request_t *request; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &mmc_object, memcache_pool_ce) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) { RETURN_FALSE; } RETVAL_FALSE; for (i=0; i<pool->num_servers; i++) { /* run command and check for valid return value */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_version_handler, return_value, NULL, NULL); pool->protocol->version(request); if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) { mmc_pool_run(pool); if (Z_TYPE_P(return_value) == IS_STRING) { break; } } } if (Z_TYPE_P(return_value) == IS_NULL) { RETURN_FALSE; } } /* }}} */ /* {{{ proto bool memcache_add(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Adds new item. Item with such key should not exist. */ PHP_FUNCTION(memcache_add) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_ADD); } /* }}} */ /* {{{ proto bool memcache_set(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Sets the value of an item. Item may exist or not */ PHP_FUNCTION(memcache_set) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_SET); } /* }}} */ /* {{{ proto bool memcache_replace(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ) Replaces existing item. Returns false if item doesn't exist */ PHP_FUNCTION(memcache_replace) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_REPLACE); } /* }}} */ /* {{{ proto bool memcache_cas(object memcache, mixed key [, mixed var [, int flag [, int exptime [, long cas ] ] ] ]) Sets the value of an item if the CAS value is the same (Compare-And-Swap) */ PHP_FUNCTION(memcache_cas) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_CAS); } /* }}} */ /* {{{ proto bool memcache_prepend(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Appends a value to the stored value, value must exist */ PHP_FUNCTION(memcache_append) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_APPEND); } /* }}} */ /* {{{ proto bool memcache_prepend(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Prepends a value to the stored value, value must exist */ PHP_FUNCTION(memcache_prepend) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_PREPEND); } /* }}} */ int mmc_value_handler_multi( const char *key, unsigned int key_len, zval *value, unsigned int flags, unsigned long cas, void *param) /* receives a multiple values, param is a zval** array to store value and flags in {{{ */ { zval **result = (zval **)param; /* add value to result */ if (Z_TYPE_P(result[0]) != IS_ARRAY) { array_init(result[0]); } ZEND_ASSERT(key_len > 0); add_assoc_zval_ex(result[0], (char *)key, key_len, value); /* add flags to result */ if (result[1] != NULL) { if (Z_TYPE_P(result[1]) != IS_ARRAY) { array_init(result[1]); } add_assoc_long_ex(result[1], (char *)key, key_len, flags); } /* add CAS value to result */ if (result[2] != NULL) { if (Z_TYPE_P(result[2]) != IS_ARRAY) { array_init(result[2]); } add_assoc_long_ex(result[2], (char *)key, key_len, cas); } return MMC_REQUEST_DONE; } /* }}} */ int mmc_value_handler_single( const char *key, unsigned int key_len, zval *value, unsigned int flags, unsigned long cas, void *param) /* receives a single value, param is a zval pointer to store value to {{{ */ { zval **result = (zval **)param; ZVAL_ZVAL(result[0], value, 1, 1); if (result[1] != NULL) { ZVAL_LONG(result[1], flags); } if (result[2] != NULL) { ZVAL_LONG(result[2], cas); } return MMC_REQUEST_DONE; } /* }}} */ static int mmc_value_failover_handler(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param) /* uses keys and return value to reschedule requests to other servers, param is a zval ** pointer {{{ */ { zval *keys = ((zval **)param)[0], **value_handler_param = (zval **)((void **)param)[1]; zval *key; if (!MEMCACHE_G(allow_failover) || request->failed_servers.len >= MEMCACHE_G(max_failover_attempts)) { mmc_pool_release(pool, request); return MMC_REQUEST_FAILURE; } ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), key) { if (Z_TYPE_P(value_handler_param[0]) != IS_ARRAY || !zend_hash_str_exists(Z_ARRVAL_P(value_handler_param[0]), Z_STRVAL_P(key), Z_STRLEN_P(key))) { mmc_pool_schedule_get(pool, MMC_PROTO_UDP, value_handler_param[2] != NULL ? MMC_OP_GETS : MMC_OP_GET, key, request->value_handler, request->value_handler_param, request->failover_handler, request->failover_handler_param, request); } } ZEND_HASH_FOREACH_END(); mmc_pool_release(pool, request); return MMC_OK; } /* }}}*/ /* {{{ proto mixed memcache_get( object memcache, mixed key [, mixed &flags [, mixed &cas ] ] ) Returns value of existing item or false */ PHP_FUNCTION(memcache_get) { mmc_pool_t *pool; zval *keys, *flags = NULL, *cas = NULL, *mmc_object = getThis(); void *value_handler_param[3], *failover_handler_param[2]; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|z/z/", &mmc_object, memcache_pool_ce, &keys, &flags, &cas) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|z/z/", &keys, &flags, &cas) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) { RETURN_FALSE; } value_handler_param[0] = return_value; value_handler_param[1] = flags; value_handler_param[2] = cas; if (Z_TYPE_P(keys) == IS_ARRAY) { zval *zv; /* return empty array if no keys found */ array_init(return_value); failover_handler_param[0] = keys; failover_handler_param[1] = value_handler_param; ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), zv) { /* schedule request */ mmc_pool_schedule_get(pool, MMC_PROTO_UDP, cas != NULL ? MMC_OP_GETS : MMC_OP_GET, zv, mmc_value_handler_multi, value_handler_param, mmc_value_failover_handler, failover_handler_param, NULL); } ZEND_HASH_FOREACH_END(); } else { mmc_request_t *request; /* return false if key isn't found */ ZVAL_FALSE(return_value); /* allocate request */ request = mmc_pool_request_get( pool, MMC_PROTO_TCP, mmc_value_handler_single, value_handler_param, mmc_pool_failover_handler, NULL); if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) { mmc_pool_release(pool, request); php_error_docref(NULL, E_WARNING, "Invalid key"); RETURN_FALSE; } pool->protocol->get(request, cas != NULL ? MMC_OP_GETS : MMC_OP_GET, keys, request->key, request->key_len); /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, 1) != MMC_OK) { RETURN_FALSE; } } /* execute all requests */ mmc_pool_run(pool); if (Z_TYPE_P(return_value) == IS_NULL) { RETURN_FALSE; } } /* }}} */ static int mmc_stats_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /* parses the stats response line, param is a zval pointer to store stats into {{{ */ { if (response != MMC_RESPONSE_ERROR) { char *line = (char *)message; if(!message_len) { return MMC_REQUEST_DONE; } if (mmc_str_left(line, "RESET", message_len, sizeof("RESET")-1)) { ZVAL_TRUE((zval *)param); return MMC_REQUEST_DONE; } else if (mmc_str_left(line, "STAT ", message_len, sizeof("STAT ")-1)) { if (mmc_stats_parse_stat(line + sizeof("STAT ")-1, line + message_len - 1, (zval *)param)) { return MMC_REQUEST_AGAIN; } } else if (mmc_str_left(line, "ITEM ", message_len, sizeof("ITEM ")-1)) { if (mmc_stats_parse_item(line + sizeof("ITEM ")-1, line + message_len - 1, (zval *)param)) { return MMC_REQUEST_AGAIN; } } else if (mmc_str_left(line, "END", message_len, sizeof("END")-1)) { return MMC_REQUEST_DONE; } else if (mmc_stats_parse_generic(line, line + message_len, (zval *)param)) { return MMC_REQUEST_AGAIN; } zval_dtor((zval *)param); ZVAL_FALSE((zval *)param); return MMC_REQUEST_FAILURE; } return mmc_request_failure(mmc, request->io, message, message_len, 0); } /* }}} */ static int mmc_stats_checktype(const char *type) { /* {{{ */ return type == NULL || !strcmp(type, "reset") || !strcmp(type, "malloc") || !strcmp(type, "slabs") || !strcmp(type, "cachedump") || !strcmp(type, "items") || !strcmp(type, "sizes"); } /* }}} */ /* {{{ proto array memcache_get_stats( object memcache [, string type [, int slabid [, int limit ] ] ]) Returns server's statistics */ PHP_FUNCTION(memcache_get_stats) { mmc_pool_t *pool; zval *mmc_object = getThis(); char *type = NULL; int i; size_t type_len = 0; zend_long slabid = 0, limit = MMC_DEFAULT_CACHEDUMP_LIMIT; mmc_request_t *request; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|sll", &mmc_object, memcache_pool_ce, &type, &type_len, &slabid, &limit) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sll", &type, &type_len, &slabid, &limit) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) { RETURN_FALSE; } if (!mmc_stats_checktype(type)) { php_error_docref(NULL, E_WARNING, "Invalid stats type"); RETURN_FALSE; } ZVAL_FALSE(return_value); for (i=0; i<pool->num_servers; i++) { request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stats_handler, return_value, NULL, NULL); pool->protocol->stats(request, type, slabid, limit); /* run command and check for valid return value */ if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) { mmc_pool_run(pool); if (Z_TYPE_P(return_value) != IS_FALSE) { break; } } } /* execute all requests */ mmc_pool_run(pool); if (Z_TYPE_P(return_value) == IS_NULL) { RETURN_FALSE; } } /* }}} */ /* {{{ proto array memcache_get_extended_stats( object memcache [, string type [, int slabid [, int limit ] ] ]) Returns statistics for each server in the pool */ PHP_FUNCTION(memcache_get_extended_stats) { mmc_pool_t *pool; zval *mmc_object = getThis(); char *host, *type = NULL; int i; size_t host_len, type_len = 0; zend_long slabid = 0, limit = MMC_DEFAULT_CACHEDUMP_LIMIT; mmc_request_t *request; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|sll", &mmc_object, memcache_pool_ce, &type, &type_len, &slabid, &limit) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sll", &type, &type_len, &slabid, &limit) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) { RETURN_FALSE; } if (!mmc_stats_checktype(type)) { php_error_docref(NULL, E_WARNING, "Invalid stats type"); RETURN_FALSE; } array_init(return_value); for (i=0; i<pool->num_servers; i++) { zval new_stats; zval *stats; ZVAL_FALSE(&new_stats); host_len = spprintf(&host, 0, "%s:%u", pool->servers[i]->host, pool->servers[i]->tcp.port); stats = zend_symtable_str_update(Z_ARRVAL_P(return_value), host, host_len, &new_stats); efree(host); request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stats_handler, stats, NULL, NULL); pool->protocol->stats(request, type, slabid, limit); if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) { mmc_pool_run(pool); } } /* execute all requests */ mmc_pool_run(pool); if (Z_TYPE_P(return_value) == IS_NULL) { RETURN_FALSE; } } /* }}} */ /* {{{ proto array memcache_set_compress_threshold( object memcache, int threshold [, float min_savings ] ) Set automatic compress threshold */ PHP_FUNCTION(memcache_set_compress_threshold) { mmc_pool_t *pool; zval *mmc_object = getThis(); zend_long threshold; double min_savings = MMC_DEFAULT_SAVINGS; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|d", &mmc_object, memcache_pool_ce, &threshold, &min_savings) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|d", &threshold, &min_savings) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } if (threshold < 0) { php_error_docref(NULL, E_WARNING, "threshold must be a positive integer"); RETURN_FALSE; } pool->compress_threshold = threshold; if (min_savings != MMC_DEFAULT_SAVINGS) { if (min_savings < 0 || min_savings > 1) { php_error_docref(NULL, E_WARNING, "min_savings must be a float in the 0..1 range"); RETURN_FALSE; } pool->min_compress_savings = min_savings; } else { pool->min_compress_savings = MMC_DEFAULT_SAVINGS; } RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_delete(object memcache, mixed key [, int exptime ]) Deletes existing item */ PHP_FUNCTION(memcache_delete) { php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0); } /* }}} */ /* {{{ proto mixed memcache_increment(object memcache, mixed key [, int value [, int defval [, int exptime ] ] ]) Increments existing variable */ PHP_FUNCTION(memcache_increment) { php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); } /* }}} */ /* {{{ proto mixed memcache_decrement(object memcache, mixed key [, int value [, int defval [, int exptime ] ] ]) Decrements existing variable */ PHP_FUNCTION(memcache_decrement) { php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1); } /* }}} */ /* {{{ proto bool memcache_close( object memcache ) Closes connection to memcached */ PHP_FUNCTION(memcache_close) { mmc_pool_t *pool; zval *mmc_object = getThis(); if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &mmc_object, memcache_pool_ce) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } mmc_pool_close(pool); RETURN_TRUE; } /* }}} */ static int mmc_flush_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /* parses the OK response line, param is an int pointer to increment on success {{{ */ { if (response == MMC_OK) { (*((int *)param))++; return MMC_REQUEST_DONE; } if (response == MMC_RESPONSE_CLIENT_ERROR) { ZVAL_FALSE((zval *)param); php_error_docref(NULL, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, message, response); return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0); } /* }}} */ /* {{{ proto bool memcache_flush( object memcache [, int delay ] ) Flushes cache, optionally at after the specified delay */ PHP_FUNCTION(memcache_flush) { mmc_pool_t *pool; zval *mmc_object = getThis(); mmc_request_t *request; unsigned int i, responses = 0; zend_long delay = 0; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &mmc_object, memcache_pool_ce, &delay) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &delay) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool)) { RETURN_FALSE; } for (i=0; i<pool->num_servers; i++) { request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_flush_handler, &responses, NULL, NULL); pool->protocol->flush(request, delay); if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) { /* begin sending requests immediatly */ mmc_pool_select(pool); } } /* execute all requests */ mmc_pool_run(pool); if (responses < pool->num_servers) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto string memcache_set_sasl_data(object memcache, string username, string password) Set credentials for sals authentification */ PHP_FUNCTION(memcache_set_sasl_auth_data) { zval *mmc_object = getThis(); char *user; size_t user_length; char *password; size_t password_length; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oss", &mmc_object, memcache_pool_ce, &user, &user_length, &password, &password_length) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &user, &user_length, &password, &password_length) == FAILURE) { return; } } if (user_length < 1 || password_length < 1) { RETURN_FALSE; } #if PHP_VERSION_ID >= 80000 zend_update_property_stringl(memcache_pool_ce, Z_OBJ_P(mmc_object), "username", strlen("username"), user, user_length); zend_update_property_stringl(memcache_pool_ce, Z_OBJ_P(mmc_object), "password", strlen("password"), password, password_length); #else zend_update_property_stringl(memcache_pool_ce, mmc_object, "username", strlen("username"), user, user_length); zend_update_property_stringl(memcache_pool_ce, mmc_object, "password", strlen("password"), password, password_length); #endif RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_debug( bool onoff ) */ PHP_FUNCTION(memcache_debug) { php_error_docref(NULL, E_WARNING, "memcache_debug() is deprecated, please use a debugger (like Eclipse + CDT)"); RETVAL_BOOL(PHP_DEBUG); } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */