diff --git a/.travis.yml b/.travis.yml index d529cf50..3c018f3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,30 @@ language: php php: + - 7.0 + - 5.6 - 5.5 - #- 5.4 - #- 5.3 -env: - - LIBMEMCACHED_VERSION=1.0.17 - - LIBMEMCACHED_VERSION=1.0.16 - - LIBMEMCACHED_VERSION=1.0.15 - - LIBMEMCACHED_VERSION=1.0.14 - - LIBMEMCACHED_VERSION=1.0.10 - - LIBMEMCACHED_VERSION=1.0.8 - - LIBMEMCACHED_VERSION=1.0.7 - - LIBMEMCACHED_VERSION=1.0.6 - - LIBMEMCACHED_VERSION=1.0.2 - - LIBMEMCACHED_VERSION=0.53 - - LIBMEMCACHED_VERSION=0.49 - - LIBMEMCACHED_VERSION=0.44 -services: - - memcached # will start memcached +env: + - LIBMEMCACHED_VERSION=1.0.18 # Debian Jessie / Ubuntu Xenial + - LIBMEMCACHED_VERSION=1.0.16 # RHEL / CentOS 7 + - LIBMEMCACHED_VERSION=1.0.8 # Debian Wheezy / Ubuntu Trusty + - LIBMEMCACHED_VERSION=0.44 # Ubuntu Precise +addons: + apt: + packages: + - sasl2-bin + - libsasl2-dev before_script: - ./.travis/travis.sh before_script $LIBMEMCACHED_VERSION script: - ./.travis/travis.sh script $LIBMEMCACHED_VERSION + +sudo: false + +cache: + directories: + - $HOME/cache + + diff --git a/.travis/travis.sh b/.travis/travis.sh index 05f3da95..264a7f3b 100755 --- a/.travis/travis.sh +++ b/.travis/travis.sh @@ -44,6 +44,12 @@ function validate_package_xml() { function install_libmemcached() { + if test -d "${LIBMEMCACHED_PREFIX}" + then + echo "Using cached libmemcached: ${LIBMEMCACHED_PREFIX}" + return + fi + wget "https://launchpad.net/libmemcached/1.0/${LIBMEMCACHED_VERSION}/+download/libmemcached-${LIBMEMCACHED_VERSION}.tar.gz" -O libmemcached-${LIBMEMCACHED_VERSION}.tar.gz tar xvfz libmemcached-${LIBMEMCACHED_VERSION}.tar.gz @@ -73,7 +79,7 @@ function install_igbinary() { function install_msgpack() { git clone https://github.com/msgpack/msgpack-php.git pushd msgpack-php - git checkout php5 + git checkout master phpize ./configure make @@ -81,35 +87,52 @@ function install_msgpack() { popd } -function install_sasl() { +function install_memcached() { + local prefix="${HOME}/cache/memcached-sasl-${MEMCACHED_VERSION}" + + if test -d "$prefix" + then + echo "Using cached memcached: ${prefix}" + return + fi - wget http://memcached.googlecode.com/files/memcached-1.4.15.tar.gz -O memcached-1.4.15.tar.gz - tar xfz memcached-1.4.15.tar.gz + wget http://www.memcached.org/files/memcached-${MEMCACHED_VERSION}.tar.gz -O memcached-${MEMCACHED_VERSION}.tar.gz + tar xfz memcached-${MEMCACHED_VERSION}.tar.gz - pushd memcached-1.4.15 - ./configure --enable-sasl --prefix="${HOME}/memcached" + pushd memcached-${MEMCACHED_VERSION} + ./configure --enable-sasl --enable-sasl-pwdb --prefix="${prefix}" make make install popd +} + +function run_memcached() { + local prefix="${HOME}/cache/memcached-sasl-${MEMCACHED_VERSION}" - sudo apt-get install sasl2-bin - export SASL_CONF_PATH="${HOME}/sasl2" + export SASL_CONF_PATH="/tmp/sasl2" + + if test -d "${SASL_CONF_PATH}" + then + rm -rf "${SASL_CONF_PATH}" + fi - # Create config path mkdir "${SASL_CONF_PATH}" + export MEMCACHED_SASL_PWDB="${SASL_CONF_PATH}/sasldb2" # Create configuration cat< "${SASL_CONF_PATH}/memcached.conf" mech_list: PLAIN plainlog_level: 5 -sasldb_path: ${SASL_CONF_PATH}/sasldb2 +sasldb_path: ${MEMCACHED_SASL_PWDB} EOF - # Create password - echo "test" | /usr/sbin/saslpasswd2 -c memcached -a memcached -f "${SASL_CONF_PATH}/sasldb2" + echo "test" | /usr/sbin/saslpasswd2 -c memcached -a memcached -f "${MEMCACHED_SASL_PWDB}" + + # Run normal memcached + "${prefix}/bin/memcached" -d -p 11211 # Run memcached on port 11212 with SASL support - "${HOME}/memcached/bin/memcached" -S -d -p 11212 + "${prefix}/bin/memcached" -S -d -p 11212 } function build_php_memcached() { @@ -129,7 +152,8 @@ function build_php_memcached() { sasl_flag="--enable-memcached-sasl" fi - ./configure --with-libmemcached-dir="$LIBMEMCACHED_PREFIX" $protocol_flag $sasl_flag --enable-memcached-json --enable-memcached-igbinary --enable-memcached-msgpack + # ./configure --with-libmemcached-dir="$LIBMEMCACHED_PREFIX" $protocol_flag $sasl_flag + ./configure --with-libmemcached-dir="$LIBMEMCACHED_PREFIX" $protocol_flag $sasl_flag --enable-memcached-json --enable-memcached-msgpack --enable-memcached-igbinary make make install popd @@ -152,30 +176,24 @@ EOF function run_memcached_tests() { export NO_INTERACTION=1 export REPORT_EXIT_STATUS=1 - export TEST_PHP_EXECUTABLE=`which php` + export TEST_PHP_EXECUTABLE=$(which php) pushd "${PHP_MEMCACHED_BUILD_DIR}/memcached-${PHP_MEMCACHED_VERSION}" # We have one xfail test, we run it separately - php run-tests.php -d extension=msgpack.so -d extension=igbinary.so -d extension=memcached.so -n ./tests/expire.phpt + php run-tests.php -d extension=memcached.so -n ./tests/expire.phpt rm ./tests/expire.phpt # Run normal tests - php run-tests.php -d extension=msgpack.so -d extension=igbinary.so -d extension=memcached.so -n ./tests/*.phpt + php run-tests.php --show-diff -d extension=modules/memcached.so -d extension=msgpack.so -d extension=igbinary.so -n ./tests/*.phpt retval=$? - for i in `ls tests/*.out 2>/dev/null`; do - echo "-- START ${i}"; - cat $i; - echo ""; - echo "-- END"; - done popd - return $retval; } # Command line arguments ACTION=$1 LIBMEMCACHED_VERSION=$2 +MEMCACHED_VERSION="1.4.25" if test "x$ACTION" = "x"; then echo "Usage: $0 " @@ -187,11 +205,15 @@ if test "x$LIBMEMCACHED_VERSION" = "x"; then exit 1 fi +if test "x$3" != "x"; then + MEMCACHED_VERSION=$3 +fi + # the extension version PHP_MEMCACHED_VERSION=$(php -r '$sxe = simplexml_load_file ("package.xml"); echo (string) $sxe->version->release;') # Libmemcached install dir -LIBMEMCACHED_PREFIX="${HOME}/libmemcached-${LIBMEMCACHED_VERSION}" +LIBMEMCACHED_PREFIX="${HOME}/cache/libmemcached-${LIBMEMCACHED_VERSION}" # Where to do the build PHP_MEMCACHED_BUILD_DIR="/tmp/php-memcached-build" @@ -218,11 +240,9 @@ case $ACTION in # install msgpack install_msgpack - - # install SASL - if test "x$ENABLE_SASL" = "xyes"; then - install_sasl - fi + + install_memcached + run_memcached ;; script) diff --git a/README.markdown b/README.markdown index 53a466e4..ae1b86db 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,6 @@ Build Status ------------ -[![Build Status](https://travis-ci.org/php-memcached-dev/php-memcached.png?branch=master)](https://travis-ci.org/php-memcached-dev/php-memcached) +[![Build Status](https://travis-ci.org/php-memcached-dev/php-memcached.png?branch=php7)](https://travis-ci.org/php-memcached-dev/php-memcached) Description ----------- diff --git a/config.m4 b/config.m4 index 107f8cbe..e6a2dfd0 100644 --- a/config.m4 +++ b/config.m4 @@ -106,72 +106,29 @@ if test "$PHP_MEMCACHED" != "no"; then AC_MSG_RESULT([$session_inc_path]) fi fi - + if test "$PHP_MEMCACHED_JSON" != "no"; then AC_MSG_CHECKING([for json includes]) json_inc_path="" - - tmp_version=$PHP_VERSION - if test -z "$tmp_version"; then - if test -z "$PHP_CONFIG"; then - AC_MSG_ERROR([php-config not found]) - fi - PHP_MEMCACHED_VERSION_ORIG=`$PHP_CONFIG --version`; - else - PHP_MEMCACHED_VERSION_ORIG=$tmp_version - fi - if test -z $PHP_MEMCACHED_VERSION_ORIG; then - AC_MSG_ERROR([failed to detect PHP version, please report]) + if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then + json_inc_path="$abs_srcdir/include/php" + elif test -f "$abs_srcdir/ext/json/php_json.h"; then + json_inc_path="$abs_srcdir" + elif test -f "$phpincludedir/ext/json/php_json.h"; then + json_inc_path="$phpincludedir" + else + for i in php php4 php5 php6; do + if test -f "$prefix/include/$i/ext/json/php_json.h"; then + json_inc_path="$prefix/include/$i" + fi + done fi - - PHP_MEMCACHED_VERSION_MASK=`echo ${PHP_MEMCACHED_VERSION_ORIG} | awk 'BEGIN { FS = "."; } { printf "%d", ($1 * 1000 + $2) * 1000 + $3;}'` - - if test $PHP_MEMCACHED_VERSION_MASK -ge 5003000; then - if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then - json_inc_path="$abs_srcdir/include/php" - elif test -f "$abs_srcdir/ext/json/php_json.h"; then - json_inc_path="$abs_srcdir" - elif test -f "$phpincludedir/ext/json/php_json.h"; then - json_inc_path="$phpincludedir" - else - for i in php php4 php5 php6; do - if test -f "$prefix/include/$i/ext/json/php_json.h"; then - json_inc_path="$prefix/include/$i" - fi - done - fi - if test "$json_inc_path" = ""; then - AC_MSG_ERROR([Cannot find php_json.h]) - else - AC_DEFINE(HAVE_JSON_API,1,[Whether JSON API is available]) - AC_DEFINE(HAVE_JSON_API_5_3,1,[Whether JSON API for PHP 5.3 is available]) - AC_MSG_RESULT([$json_inc_path]) - fi - elif test $PHP_MEMCACHED_VERSION_MASK -ge 5002009; then - dnl Check JSON for PHP 5.2.9+ - if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then - json_inc_path="$abs_srcdir/include/php" - elif test -f "$abs_srcdir/ext/json/php_json.h"; then - json_inc_path="$abs_srcdir" - elif test -f "$phpincludedir/ext/json/php_json.h"; then - json_inc_path="$phpincludedir" - else - for i in php php4 php5 php6; do - if test -f "$prefix/include/$i/ext/json/php_json.h"; then - json_inc_path="$prefix/include/$i" - fi - done - fi - if test "$json_inc_path" = ""; then - AC_MSG_ERROR([Cannot find php_json.h]) - else - AC_DEFINE(HAVE_JSON_API,1,[Whether JSON API is available]) - AC_DEFINE(HAVE_JSON_API_5_2,1,[Whether JSON API for PHP 5.2 is available]) - AC_MSG_RESULT([$json_inc_path]) - fi - else - AC_MSG_RESULT([the PHP version does not support JSON serialization API]) + if test "$json_inc_path" = ""; then + AC_MSG_ERROR([Cannot find php_json.h]) + else + AC_DEFINE(HAVE_JSON_API,1,[Whether JSON API is available]) + AC_MSG_RESULT([$json_inc_path]) fi fi @@ -339,6 +296,28 @@ if test "$PHP_MEMCACHED" != "no"; then AC_MSG_RESULT([no]) fi + ORIG_CFLAGS="$CFLAGS" + ORIG_LIBS="$LIBS" + + CFLAGS="$CFLAGS $PHP_LIBMEMCACHED_INCLUDES" + LIBS="$LIBS $PHP_LIBMEMCACHED_LIBS" + + AC_CACHE_CHECK([whether memcached_exist is defined], ac_cv_have_memcached_exist, [ + AC_TRY_LINK( + [ #include ], + [ memcached_exist (NULL, NULL, 0); ], + [ ac_cv_have_memcached_exist="yes" ], + [ ac_cv_have_memcached_exist="no" ] + ) + ]) + + CFLAGS="$ORIG_CFLAGS" + LIBS="$ORIG_LIBS" + + if test "$ac_cv_have_memcached_exist" = "yes"; then + AC_DEFINE(HAVE_MEMCACHED_EXIST, [1], [Whether memcached_exist is defined]) + fi + PHP_MEMCACHED_FILES="php_memcached.c php_libmemcached_compat.c g_fmt.c" if test "$PHP_SYSTEM_FASTLZ" != "no"; then diff --git a/memcached.ini b/memcached.ini index 1996eb27..31a6eced 100644 --- a/memcached.ini +++ b/memcached.ini @@ -4,19 +4,25 @@ ; the default is On memcached.sess_locking = On -; Session spin lock retry wait time in microseconds. -; Be carefull when setting this value. -; Valid values are integers, where 0 is interpreted as -; the default value. Negative values result in a reduces -; locking to a try lock. -; the default is 150000 -memcached.sess_lock_wait = 150000 - -; The maximum time, in seconds, to wait for a session lock -; before timing out. -; Setting to 0 results in default behavior, which is to -; use max_execution_time. -memcached.sess_lock_max_wait = 0; +; !! DEPRECATED AND REMOVED in 3.x !! +; memcached.sess_lock_wait = 150000 + +; !! DEPRECATED AND REMOVED in 3.x !! +; memcached.sess_lock_max_wait = 0; + +; The minimum time, in milliseconds, to wait between session lock attempts. +; This value is double on each lock retry until memcached.sess_lock_wait_max +; is reached, after which any further retries will take sess_lock_wait_max seconds. +; Default is 1000. +memcached.sess_lock_wait_min = 1000; + +; The maximum time, in milliseconds, to wait between session lock attempts. +; Default is 2000. +memcached.sess_lock_wait_max = 2000; + +; The number of times to retry locking the session lock, not including the first attempt. +; Default is 5. +memcached.sess_lock_retries = 5; ; The time, in seconds, before a lock should release itself. ; Setting to 0 results in the default behaviour, which is to @@ -29,16 +35,24 @@ memcached.sess_lock_expire = 0; ; the default value is "memc.sess.key." memcached.sess_prefix = "memc.sess.key." +; Whether or not to re-use the memcached connections corresponding to the value(s) +; of session.save_path after the execution of the script ends. +; Don't use this if certain settings (e.g. SASL settings, sess_binary_protocol) would +; be overridden between requests. +; Default is Off. +memcached.sess_persistent = Off + ; memcached session consistent hash mode ; if set to On, consistent hashing (libketama) is used ; for session handling. ; When consistent hashing is used, one can add or remove cache ; node(s) without messing up too much with existing keys -; default is Off -memcached.sess_consistent_hash = Off +; default is On +memcached.sess_consistent_hash = On -; Allow failed memcached server to automatically be removed -memcached.sess_remove_failed = 1 +; Allow failed memcached server to automatically be removed. +; Default is Off. (In previous versions, this setting was called memcached.sess_remove_failed) +memcached.sess_remove_failed_servers = Off ; Write data to a number of additional memcached servers ; This is "poor man's HA" as libmemcached calls it. @@ -57,7 +71,7 @@ memcached.sess_binary = Off memcached.sess_randomize_replica_read = Off ; memcached connect timeout value -; In non-blocking mode this changes the value of the timeout +; In non-blocking mode this changes the value of the timeout ; during socket connection in milliseconds. Specifying -1 means an infinite timeout. memcached.sess_connect_timeout = 1000 @@ -105,14 +119,38 @@ memcached.compression_threshold = 2000 ; The default is igbinary if available, then msgpack if available, then php otherwise. memcached.serializer = "igbinary" -; Use SASL authentication for connections -; valid values: On, Off -; the default is Off -memcached.use_sasl = Off - ; The amount of retries for failed store commands. ; This mechanism allows transparent fail-over to secondary servers when ; set/increment/decrement/setMulti operations fail on the desired server in a multi-server ; environment. ; the default is 2 memcached.store_retry_count = 2 + +; Sets the default for consistent hashing for new connections. +; (To configure consistent hashing for session connections, +; use memcached.sess_consistent_hash instead) +; +; If set to On, consistent hashing (libketama) is used +; for session handling. +; When consistent hashing is used, one can add or remove cache +; node(s) without messing up too much with existing keys +; default is Off +memcached.default_consistent_hash = Off + +; Sets the default memcached protocol for new connections. +; (To configure the memcached protocol for connections used by sessions, +; use memcached.sess_binary_protocol instead) +; +; If set to On, the memcached binary protocol is used by default. +; If set to Off, the memcached text protocol is used. +; Default is Off +memcached.default_binary_protocol = Off + +; Sets the default memcached connection timeout for new connections. +; (To configure the memcached connection timeout for sessions, +; use memcached.sess_connect_timeout instead) +; In non-blocking mode this changes the value of the timeout. +; during socket connection in milliseconds. Specifying -1 means an infinite timeout. +; Specifying 0 means using the memcached library's default connection timeout. +; Default is 0. +memcached.default_connect_timeout = 0 diff --git a/package.xml b/package.xml index fe35edac..244acfab 100644 --- a/package.xml +++ b/package.xml @@ -21,18 +21,40 @@ http://pear.php.net/dtd/package-2.0.xsd"> mkoppanen@php.net yes - 2014-04-01 + 2016-02-22 - 2.2.0 - 2.2.0 + 3.0.0a1 + 3.0.0 - stable - stable + alpha + alpha PHP -- Added the OPT_SERVER_TIMEOUT_LIMIT behaviour +PHP7 release of memcached extension. Note that support for libmemcached 0.x series has been discontinued +and the oldest actively tested version is 1.0.2. It is highly recommended to use version 1.0.18 of +libmemcached. Please note that this is a beta release and reporting any issues would be highly appreciated +before we move closer to releasing stable version. + +API + * get commands do not take cas or user flags parameters. + * get and getMulti commands take Memcached::GET_EXTENDED flag to retrieve user flags and cas tokens + * Fixes getStats command to return all stats from all servers + * Fixes allKeys command behaviour + * Fixes error where cache callback for get command was not setting expiration time properly + * Added server type to server list + * Remove use_sasl ini-variable and initialise sasl as needed + * CAS tokens are returned as integers and they overflow to strings as needed + +Session handler + * Session lock algorithm updated (new ini-values memcached.sess_lock_wait_min, memcached.sess_lock_wait_max and memcached.sess_lock_retries) + * Session extension uses PHP allocators (still some work to do on the rest of the extension) + * Ini-values take effect during session_start or session_regenerate_id + * Fixes crash with session_regenerate_id (work-around for PHP bug) + +Tests + * Fix several problematic tests @@ -100,6 +122,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -135,15 +159,18 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + + - 5.2.0 - 6.0.0 - 6.0.0 + 7.0.0 1.4.0b1 @@ -155,6 +182,42 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + alpha + alpha + + + 3.0.0a1 + 3.0.0 + + 2016-02-22 + +PHP7 release of memcached extension. Note that support for libmemcached 0.x series has been discontinued +and the oldest actively tested version is 1.0.2. It is highly recommended to use version 1.0.18 of +libmemcached. Please note that this is a beta release and reporting any issues would be highly appreciated +before we move closer to releasing stable version. + +API + * get commands do not take cas or user flags parameters. + * get and getMulti commands take Memcached::GET_EXTENDED flag to retrieve user flags and cas tokens + * Fixes getStats command to return all stats from all servers + * Fixes allKeys command behaviour + * Fixes error where cache callback for get command was not setting expiration time properly + * Added server type to server list + * Remove use_sasl ini-variable and initialise sasl as needed + * CAS tokens are returned as integers and they overflow to strings as needed + +Session handler + * Session lock algorithm updated (new ini-values memcached.sess_lock_wait_min, memcached.sess_lock_wait_max and memcached.sess_lock_retries) + * Session extension uses PHP allocators (still some work to do on the rest of the extension) + * Ini-values take effect during session_start or session_regenerate_id + * Fixes crash with session_regenerate_id (work-around for PHP bug) + +Tests + * Fix several problematic tests + + stablestable 2.2.02.2.0 diff --git a/php_libmemcached_compat.c b/php_libmemcached_compat.c index f238709e..bd35d8fe 100644 --- a/php_libmemcached_compat.c +++ b/php_libmemcached_compat.c @@ -18,35 +18,20 @@ #include "php_memcached_private.h" #include "php_libmemcached_compat.h" -memcached_st *php_memc_create_str (const char *str, size_t str_len) +memcached_return php_memcached_exist (memcached_st *memc, zend_string *key) { -#ifdef HAVE_LIBMEMCACHED_MEMCACHED - return memcached (str, str_len); +#ifdef HAVE_MEMCACHED_EXIST + return memcached_exist (memc, key->val, key->len); #else - memcached_return rc; - memcached_st *memc; - memcached_server_st *servers; - - memc = memcached_create(NULL); - - if (!memc) { - return NULL; - } - - servers = memcached_servers_parse (str); - - if (!servers) { - memcached_free (memc); - return NULL; - } - - rc = memcached_server_push (memc, servers); - memcached_server_free (servers); - - if (rc != MEMCACHED_SUCCESS) { - memcached_free (memc); - return NULL; + memcached_return rc = MEMCACHED_SUCCESS; + uint32_t flags = 0; + size_t value_length = 0; + char *value = NULL; + + value = memcached_get (memc, key->val, key->len, &value_length, &flags, &rc); + if (value) { + free (value); } - return memc; + return rc; #endif -} \ No newline at end of file +} diff --git a/php_libmemcached_compat.h b/php_libmemcached_compat.h index ae400d9c..e740d310 100644 --- a/php_libmemcached_compat.h +++ b/php_libmemcached_compat.h @@ -20,7 +20,7 @@ /* this is the version(s) we support */ #include -memcached_st *php_memc_create_str (const char *str, size_t str_len); +memcached_return php_memcached_exist (memcached_st *memc, zend_string *key); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x00052000 # define MEMCACHED_SERVER_TEMPORARILY_DISABLED (1024 << 2) diff --git a/php_memcached.c b/php_memcached.c index 77dd8c29..095366c4 100644 --- a/php_memcached.c +++ b/php_memcached.c @@ -27,6 +27,9 @@ #include "php_memcached_server.h" #include "g_fmt.h" +#include +#include + #ifdef HAVE_MEMCACHED_SESSION # include "php_memcached_session.h" #endif @@ -49,14 +52,18 @@ # include "ext/msgpack/php_msgpack.h" #endif -/* - * This is needed because PHP 5.3.[01] does not install JSON_parser.h by default. This - * constant will move into php_json.h in the future anyway. - */ -#ifndef JSON_PARSER_DEFAULT_DEPTH -#define JSON_PARSER_DEFAULT_DEPTH 512 +#ifdef ZTS +#define MEMC_G(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, memc.v) +#else +#define MEMC_G(v) (php_memcached_globals.memc.v) #endif +static int le_memc; + +static int php_memc_list_entry(void) { + return le_memc; +} + /**************************************** Protocol parameters ****************************************/ @@ -70,6 +77,7 @@ #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 @@ -116,96 +124,114 @@ /**************************************** "get" operation flags ****************************************/ -#define MEMC_GET_PRESERVE_ORDER (1<<0) - +#define MEMC_GET_PRESERVE_ORDER 1 +#define MEMC_GET_EXTENDED 2 /**************************************** Helper macros ****************************************/ -#define MEMC_METHOD_INIT_VARS \ - zval* object = getThis(); \ - php_memc_t* i_obj = NULL; \ - struct memc_obj* m_obj = NULL; - -#define MEMC_METHOD_FETCH_OBJECT \ - i_obj = (php_memc_t *) zend_object_store_get_object( object TSRMLS_CC ); \ - m_obj = i_obj->obj; \ - if (!m_obj) { \ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Memcached constructor was not called"); \ - return; \ - } - -#ifndef DVAL_TO_LVAL -#ifdef _WIN64 -# define DVAL_TO_LVAL(d, l) \ - if ((d) > LONG_MAX) { \ - (l) = (long)(unsigned long)(__int64) (d); \ - } else { \ - (l) = (long) (d); \ - } -#else -# define DVAL_TO_LVAL(d, l) \ - if ((d) > LONG_MAX) { \ - (l) = (unsigned long) (d); \ - } else { \ - (l) = (long) (d); \ - } -#endif -#endif - #define RETURN_FROM_GET RETURN_FALSE -#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 3) -#define zend_parse_parameters_none() \ - zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") -#endif - - /**************************************** Structures and definitions ****************************************/ -enum memcached_compression_type { - COMPRESSION_TYPE_ZLIB = 1, - COMPRESSION_TYPE_FASTLZ = 2, -}; + +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_object zo; - struct memc_obj { - memcached_st *memc; - zend_bool compression; - enum memcached_serializer serializer; - enum memcached_compression_type compression_type; + zend_bool is_persistent; + zend_bool compression_enabled; + + zend_long serializer; + zend_long compression_type; + + zend_long store_retry_count; + zend_long set_udf_flags; + #if HAVE_MEMCACHED_SASL - zend_bool has_sasl_data; + zend_bool has_sasl_data; #endif - long store_retry_count; - } *obj; +} php_memc_user_data_t; - zend_bool is_persistent; +typedef struct { + memcached_st *memc; zend_bool is_pristine; int rescode; int memc_errno; -} php_memc_t; + 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); + +#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) ? \ + strchr(ZSTR_VAL(key), '\n') : \ + strchr(ZSTR_VAL(key), ' ')))) { \ + intern->rescode = MEMCACHED_BAD_KEY_PROVIDED; \ + RETURN_FALSE; \ + } #ifdef HAVE_MEMCACHED_PROTOCOL typedef struct { - zend_object zo; php_memc_proto_handler_t *handler; + zend_object zo; } php_memc_server_t; -#endif +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)) -enum { - MEMC_OP_SET, - MEMC_OP_TOUCH, - MEMC_OP_ADD, - MEMC_OP_REPLACE, - MEMC_OP_APPEND, - MEMC_OP_PREPEND -}; +#ifdef ZTS +#define MEMC_SERVER_G(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, server.v) +#else +#define MEMC_SERVER_G(v) (php_memcached_globals.server.v) +#endif +#endif static zend_class_entry *memcached_ce = NULL; @@ -218,27 +244,10 @@ static zend_object_handlers memcached_server_object_handlers; static zend_class_entry *memcached_server_ce = NULL; #endif -struct callbackContext -{ - zval *array; - zval *entry; - memcached_stat_st *stats; /* for use with functions that need stats */ - void *return_value; - unsigned int i; /* for use with structures mapped against servers */ -}; - #if HAVE_SPL static zend_class_entry *spl_ce_RuntimeException = NULL; #endif -#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3) -const zend_fcall_info empty_fcall_info = { 0, NULL, NULL, NULL, NULL, 0, NULL, NULL, 0 }; -#undef ZEND_BEGIN_ARG_INFO_EX -#define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ - static zend_arg_info name[] = { \ - { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, -#endif - ZEND_DECLARE_MODULE_GLOBALS(php_memcached) #ifdef COMPILE_DL_MEMCACHED @@ -248,82 +257,126 @@ ZEND_GET_MODULE(memcached) static PHP_INI_MH(OnUpdateCompressionType) { if (!new_value) { - MEMC_G(compression_type_real) = COMPRESSION_TYPE_FASTLZ; - } else if (!strcmp(new_value, "fastlz")) { - MEMC_G(compression_type_real) = COMPRESSION_TYPE_FASTLZ; - } else if (!strcmp(new_value, "zlib")) { - MEMC_G(compression_type_real) = COMPRESSION_TYPE_ZLIB; + 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, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } static PHP_INI_MH(OnUpdateSerializer) { if (!new_value) { - MEMC_G(serializer) = SERIALIZER_DEFAULT; - } else if (!strcmp(new_value, "php")) { - MEMC_G(serializer) = SERIALIZER_PHP; + 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(new_value, "igbinary")) { - MEMC_G(serializer) = SERIALIZER_IGBINARY; + } else if (!strcmp(ZSTR_VAL(new_value), "igbinary")) { + MEMC_G(serializer_type) = SERIALIZER_IGBINARY; #endif // IGBINARY #ifdef HAVE_JSON_API - } else if (!strcmp(new_value, "json")) { - MEMC_G(serializer) = SERIALIZER_JSON; - } else if (!strcmp(new_value, "json_array")) { - MEMC_G(serializer) = SERIALIZER_JSON_ARRAY; + } 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(new_value, "msgpack")) { - MEMC_G(serializer) = SERIALIZER_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, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + 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) { + char *ptr = ZSTR_VAL(new_value); + + while (*ptr != '\0') { + if (isspace (*ptr++)) { + php_error_docref(NULL, E_WARNING, "memcached.sess_prefix cannot contain whitespace characters"); + return FAILURE; + } + } + 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; + } + } + 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_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) + + /* {{{ INI entries */ PHP_INI_BEGIN() + #ifdef HAVE_MEMCACHED_SESSION - STD_PHP_INI_ENTRY("memcached.sess_locking", "1", PHP_INI_ALL, OnUpdateBool, sess_locking_enabled, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_consistent_hash", "0", PHP_INI_ALL, OnUpdateBool, sess_consistent_hash_enabled, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_binary", "0", PHP_INI_ALL, OnUpdateBool, sess_binary_enabled, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_lock_wait", "150000", PHP_INI_ALL, OnUpdateLongGEZero,sess_lock_wait, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_lock_max_wait", "0", PHP_INI_ALL, OnUpdateLongGEZero, sess_lock_max_wait, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_lock_expire", "0", PHP_INI_ALL, OnUpdateLongGEZero, sess_lock_expire, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_prefix", "memc.sess.key.", PHP_INI_ALL, OnUpdateString, sess_prefix, zend_php_memcached_globals, php_memcached_globals) - - STD_PHP_INI_ENTRY("memcached.sess_number_of_replicas", "0", PHP_INI_ALL, OnUpdateLongGEZero, sess_number_of_replicas, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_randomize_replica_read", "0", PHP_INI_ALL, OnUpdateBool, sess_randomize_replica_read, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_remove_failed", "0", PHP_INI_ALL, OnUpdateBool, sess_remove_failed_enabled, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_connect_timeout", "1000", PHP_INI_ALL, OnUpdateLong, sess_connect_timeout, zend_php_memcached_globals, php_memcached_globals) -#if HAVE_MEMCACHED_SASL - STD_PHP_INI_ENTRY("memcached.sess_sasl_username", "", PHP_INI_ALL, OnUpdateString, sess_sasl_username, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.sess_sasl_password", "", PHP_INI_ALL, OnUpdateString, sess_sasl_password, zend_php_memcached_globals, php_memcached_globals) -#endif + MEMC_SESSION_INI_ENTRY("locking", "1", OnUpdateBool, lock_enabled) + MEMC_SESSION_INI_ENTRY("lock_wait_min", "1000", OnUpdateLongGEZero, lock_wait_min) + MEMC_SESSION_INI_ENTRY("lock_wait_max", "2000", OnUpdateLongGEZero, lock_wait_max) + MEMC_SESSION_INI_ENTRY("lock_retries", "5", OnUpdateLong, lock_retries) + MEMC_SESSION_INI_ENTRY("lock_expire", "0", OnUpdateLongGEZero, lock_expiration) + MEMC_SESSION_INI_ENTRY("binary_protocol", "1", OnUpdateBool, binary_protocol_enabled) + MEMC_SESSION_INI_ENTRY("consistent_hash", "1", OnUpdateBool, consistent_hash_enabled) + MEMC_SESSION_INI_ENTRY("number_of_replicas", "0", OnUpdateLongGEZero, number_of_replicas) + MEMC_SESSION_INI_ENTRY("randomize_replica_read", "0", OnUpdateLongGEZero, randomize_replica_read_enabled) + MEMC_SESSION_INI_ENTRY("remove_failed_servers", "0", OnUpdateBool, remove_failed_servers_enabled) + MEMC_SESSION_INI_ENTRY("server_failure_limit", "0", OnUpdateLongGEZero, server_failure_limit) + MEMC_SESSION_INI_ENTRY("connect_timeout", "0", OnUpdateLongGEZero, connect_timeout) + MEMC_SESSION_INI_ENTRY("sasl_username", "", OnUpdateString, sasl_username) + MEMC_SESSION_INI_ENTRY("sasl_password", "", OnUpdateString, sasl_password) + MEMC_SESSION_INI_ENTRY("prefix", "memc.sess.", OnUpdateSessionPrefixString, prefix) + MEMC_SESSION_INI_ENTRY("persistent", "0", OnUpdateBool, persistent_enabled) + + /* 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 - STD_PHP_INI_ENTRY("memcached.compression_type", "fastlz", PHP_INI_ALL, OnUpdateCompressionType, compression_type, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.compression_factor", "1.3", PHP_INI_ALL, OnUpdateReal, compression_factor, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.compression_threshold", "2000", PHP_INI_ALL, OnUpdateLong, compression_threshold, zend_php_memcached_globals, php_memcached_globals) - STD_PHP_INI_ENTRY("memcached.serializer", SERIALIZER_DEFAULT_NAME, PHP_INI_ALL, OnUpdateSerializer, serializer_name, zend_php_memcached_globals, php_memcached_globals) -#if HAVE_MEMCACHED_SASL - STD_PHP_INI_ENTRY("memcached.use_sasl", "0", PHP_INI_SYSTEM, OnUpdateBool, use_sasl, zend_php_memcached_globals, php_memcached_globals) -#endif - STD_PHP_INI_ENTRY("memcached.store_retry_count", "2", PHP_INI_ALL, OnUpdateLong, store_retry_count, zend_php_memcached_globals, php_memcached_globals) + 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_ENTRY("default_consistent_hash", "0", OnUpdateBool, default_behavior.consistent_hash_enabled) + MEMC_INI_ENTRY("default_binary_protocol", "0", OnUpdateBool, default_behavior.binary_protocol_enabled) + MEMC_INI_ENTRY("default_connect_timeout", "0", OnUpdateLongGEZero, default_behavior.connect_timeout) + PHP_INI_END() /* }}} */ +#undef MEMC_INI_ENTRY +#undef MEMC_SESSION_INI_ENTRY + /**************************************** Forward declarations ****************************************/ -static int php_memc_handle_error(php_memc_t *i_obj, memcached_return status TSRMLS_DC); -static char *php_memc_zval_to_payload(zval *value, size_t *payload_len, uint32_t *flags, enum memcached_serializer serializer, enum memcached_compression_type compression_type TSRMLS_DC); -static int php_memc_zval_from_payload(zval *value, const char *payload, size_t payload_len, uint32_t flags, enum memcached_serializer serializer TSRMLS_DC); 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); @@ -331,560 +384,1177 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke 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); -static memcached_return php_memc_do_cache_callback(zval *memc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, char *key, size_t key_len, zval *value TSRMLS_DC); -static int php_memc_do_result_callback(zval *memc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, memcached_result_st *result TSRMLS_DC); -static memcached_return php_memc_do_serverlist_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context); -static memcached_return php_memc_do_stats_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context); -static memcached_return php_memc_do_version_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context); -static void php_memc_destroy(struct memc_obj *m_obj, zend_bool persistent TSRMLS_DC); + +/* 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); + /**************************************** - Method implementations + Exported helper functions ****************************************/ +zend_bool php_memc_init_sasl_if_needed() +{ +#if 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 TSRMLS_DC) +char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) { char *buffer = NULL; - if (fci->object_ptr) { - spprintf (&buffer, 0, "%s::%s", Z_OBJCE_P (fci->object_ptr)->name, fci_cache->function_handler->common.function_name); + 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_P (fci->function_name) == IS_OBJECT) { - spprintf (&buffer, 0, "%s", Z_OBJCE_P (fci->function_name)->name); + 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_P (fci->function_name)); + spprintf (&buffer, 0, "%s", Z_STRVAL(fci->function_name)); } } return buffer; } -static zend_bool php_memcached_on_new_callback(zval *object, zend_fcall_info *fci, zend_fcall_info_cache *fci_cache, char *persistent_id, int persistent_id_len TSRMLS_DC) -{ - zend_bool retval = 1; - zval pid_z; - zval *retval_ptr, *pid_z_ptr = &pid_z; - zval **params[2]; - INIT_ZVAL(pid_z); - if (persistent_id) { - ZVAL_STRINGL(pid_z_ptr, persistent_id, persistent_id_len, 1); - } +/**************************************** + Handling error codes +****************************************/ - /* Call the cb */ - params[0] = &object; - params[1] = &pid_z_ptr; +static +zend_bool s_memcached_return_is_error(memcached_return status, zend_bool strict) +{ + zend_bool result = 0; - fci->params = params; - fci->param_count = 2; - fci->retval_ptr_ptr = &retval_ptr; - fci->no_separation = 1; + switch (status) { + case MEMCACHED_SUCCESS: + case MEMCACHED_STORED: + case MEMCACHED_DELETED: + case MEMCACHED_STAT: + case MEMCACHED_END: + case MEMCACHED_BUFFERED: + result = 0; + break; - if (zend_call_function(fci, fci_cache TSRMLS_CC) == FAILURE) { - char *buf = php_memc_printable_func (fci, fci_cache TSRMLS_CC); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to invoke 'on_new' callback %s()", buf); - efree (buf); - retval = 0; - } - zval_dtor(pid_z_ptr); + case MEMCACHED_SOME_ERRORS: + result = strict; + break; - if (retval_ptr) { - zval_ptr_dtor(&retval_ptr); + default: + result = 1; + break; } - return retval; + return result; } -static int le_memc, le_memc_sess; +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 int php_memc_list_entry(void) +static +void s_memc_set_status(php_memc_object_t *intern, memcached_return status, int memc_errno) { - return le_memc; + intern->rescode = status; + intern->memc_errno = memc_errno; } -/* {{{ 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) +static +zend_bool s_memc_status_has_error(php_memc_object_t *intern) { - zval *object = getThis(); - php_memc_t *i_obj; - struct memc_obj *m_obj = NULL; - char *persistent_id = NULL, *conn_str = NULL; - int persistent_id_len, conn_str_len; - zend_bool is_persistent = 0; + return s_memcached_return_is_error(intern->rescode, 1); +} - char *plist_key = NULL; - int plist_key_len = 0; +static +zend_bool s_memc_status_has_result_code(php_memc_object_t *intern, memcached_return status) +{ + return intern->rescode == status; +} - zend_fcall_info fci = {0}; - zend_fcall_info_cache fci_cache; +/**************************************** + Marshall cas tokens +****************************************/ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!f!s", &persistent_id, &persistent_id_len, &fci, &fci_cache, &conn_str, &conn_str_len) == FAILURE) { - ZVAL_NULL(object); - return; +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); + } +} - i_obj = (php_memc_t *) zend_object_store_get_object(object TSRMLS_CC); - i_obj->is_pristine = 0; +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; - if (persistent_id && *persistent_id) { - zend_rsrc_list_entry *le = NULL; + case IS_DOUBLE: + if (Z_DVAL_P (cas) < 0.0) + return 0; - is_persistent = 1; - plist_key_len = spprintf(&plist_key, 0, "memcached:id=%s", persistent_id); - plist_key_len += 1; + return (uint64_t) zval_get_double (cas); + break; - if (zend_hash_find(&EG(persistent_list), plist_key, plist_key_len, (void *)&le) == SUCCESS) { - if (le->type == php_memc_list_entry()) { - m_obj = (struct memc_obj *) le->ptr; + 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; } - i_obj->obj = m_obj; + break; } + return 0; +} - i_obj->is_persistent = is_persistent; - if (!m_obj) { - m_obj = pecalloc(1, sizeof(*m_obj), is_persistent); - if (m_obj == NULL) { - if (plist_key) { - efree(plist_key); - } - php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory: cannot allocate handle"); - /* not reached */ - } +/**************************************** + Iterate over memcached results and mget +****************************************/ - if (conn_str) { - m_obj->memc = php_memc_create_str(conn_str, conn_str_len); - if (!m_obj->memc) { - char error_buffer[1024]; - if (plist_key) { - efree(plist_key); - } -#ifdef HAVE_LIBMEMCACHED_CHECK_CONFIGURATION - if (libmemcached_check_configuration(conn_str, conn_str_len, error_buffer, sizeof(error_buffer)) != MEMCACHED_SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "configuration error %s", error_buffer); - } else { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not allocate libmemcached structure"); - } -#else - php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not allocate libmemcached structure"); -#endif - /* not reached */ - } - } else { - m_obj->memc = memcached_create(NULL); - if (m_obj->memc == NULL) { - if (plist_key) { - efree(plist_key); - } - php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not allocate libmemcached structure"); - /* not reached */ - } - } +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; - m_obj->serializer = MEMC_G(serializer); - m_obj->compression_type = MEMC_G(compression_type_real); - m_obj->compression = 1; - m_obj->store_retry_count = MEMC_G(store_retry_count); + memcached_result_create(intern->memc, &result); - i_obj->obj = m_obj; - i_obj->is_pristine = 1; + do { + result_ptr = memcached_fetch_result(intern->memc, &result, &rc); - if (fci.size) { /* will be 0 when not available */ - if (!php_memcached_on_new_callback(object, &fci, &fci_cache, persistent_id, persistent_id_len TSRMLS_CC) || EG(exception)) { - /* error calling or exception thrown from callback */ - if (plist_key) { - efree(plist_key); - } + if (s_memcached_return_is_error(rc, 0)) { + status = rc; + } - i_obj->obj = NULL; - if (is_persistent) { - php_memc_destroy(m_obj, is_persistent TSRMLS_CC); + /* 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; } - - return; + status = MEMCACHED_SOME_ERRORS; + continue; } - } - if (is_persistent) { - zend_rsrc_list_entry le; + 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); - le.type = php_memc_list_entry(); - le.ptr = m_obj; - if (zend_hash_update(&EG(persistent_list), (char *)plist_key, - plist_key_len, (void *)&le, sizeof(le), NULL) == FAILURE) { - if (plist_key) { - efree(plist_key); + 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)) {} } - php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not register persistent entry"); - /* not reached */ + break; } } - } + } while (result_ptr != NULL); - if (plist_key) { - efree(plist_key); - } + memcached_result_free(&result); + return status; } -/* }}} */ -/* {{{ Memcached::get(string key [, mixed callback [, double &cas_token [, int &udf_flags ] ] ]) - Returns a value for the given key or false */ -PHP_METHOD(Memcached, get) +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) { - php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ + memcached_return status; + int mget_status; + uint64_t orig_cas_flag = 0; -/* {{{ Memcached::getByKey(string server_key, string key [, mixed callback [, double &cas_token [, int &udf_flags ] ] ]) - 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); -} -/* }}} */ + // Reset status code + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); -/* {{{ -- php_memc_get_impl */ -static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) -{ - char *key = NULL; - int key_len = 0; - char *server_key = NULL; - int server_key_len = 0; - const char *payload = NULL; - size_t payload_len = 0; - uint32_t flags = 0; - uint64_t cas = 0; - const char* keys[1] = { NULL }; - size_t key_lens[1] = { 0 }; - zval *cas_token = NULL; - zval *udf_flags = NULL; - zend_fcall_info fci = empty_fcall_info; - zend_fcall_info_cache fcc = empty_fcall_info_cache; - memcached_result_st result; - memcached_return status = MEMCACHED_SUCCESS; - MEMC_METHOD_INIT_VARS; + if (!keys->num_valid_keys) { + intern->rescode = MEMCACHED_BAD_KEY_PROVIDED; + return 0; + } - if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|f!zz", &server_key, - &server_key_len, &key, &key_len, &fci, &fcc, &cas_token, &udf_flags) == FAILURE) { - return; + 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 { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f!zz", &key, &key_len, - &fci, &fcc, &cas_token, &udf_flags) == FAILURE) { - return; - } + status = memcached_mget(intern->memc, keys->mkeys, keys->mkeys_len, keys->num_valid_keys); } - MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + /* 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); + } - if (key_len == 0 || key_len > MEMC_OBJECT_KEY_MAX_LENGTH || strchr(key, ' ') || strchr(key, '\n')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FROM_GET; + /* Return on failure codes */ + if (mget_status == FAILURE) { + return 0; } - keys[0] = key; - key_lens[0] = key_len; + if (!result_apply_fn) { + /* no callback, for example getDelayed */ + return 1; + } - uint64_t orig_cas_flag; - orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); + status = php_memc_result_apply(intern, result_apply_fn, 0, context); - /* - * Enable CAS support, but only if it is currently disabled. - */ - if (cas_token && PZVAL_IS_REF(cas_token) && orig_cas_flag == 0) { - memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + return 0; } - status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, keys, key_lens, 1); + return 1; +} - if (cas_token && PZVAL_IS_REF(cas_token) && orig_cas_flag == 0) { - memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); +/**************************************** + 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(¶ms[0], object); + if (persistent_id) { + ZVAL_STR(¶ms[1], zend_string_copy(persistent_id)); + } else { + ZVAL_NULL(¶ms[1]); } - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - RETURN_FROM_GET; + 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(¶ms[0]); + zval_ptr_dtor(¶ms[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(¶ms[0], zobject); + ZVAL_STR_COPY(¶ms[1], key); /* key */ + ZVAL_NEW_REF(¶ms[2], value); /* value */ + + if (with_cas) { + fci->param_count = 3; + } else { + ZVAL_NEW_EMPTY_REF(¶ms[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(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + if (!with_cas) { + zval_ptr_dtor(¶ms[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; + + /* 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; + MEMC_VAL_SET_FLAG(*flags, 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; + MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_ZLIB); + } + } + break; + + default: + compress_status = 0; + break; } - status = MEMCACHED_SUCCESS; - memcached_result_create(m_obj->memc, &result); + if (!compress_status) { + php_error_docref(NULL, E_WARNING, "could not compress value"); + efree (buffer); + return 0; + } + + /* This means the value was too small to be compressed, still a success */ + if (compressed_size > (ZSTR_LEN(payload) * MEMC_G(compression_factor))) { + efree (buffer); + return 1; + } + + MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSED); + + payload = zend_string_realloc(payload, compressed_size + sizeof(uint32_t), 0); - if (memcached_fetch_result(m_obj->memc, &result, &status) == NULL) { - /* This is for historical reasons */ - if (status == MEMCACHED_END) - status = MEMCACHED_NOTFOUND; + /* 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; +} + +static +zend_bool s_serialize_value (php_memc_serializer_type serializer, zval *value, smart_str *buf, uint32_t *flags) +{ + switch (serializer) { /* - * If the result wasn't found, and we have the read-through callback, invoke - * it to get the value. The CAS token will be 0, because we cannot generate it - * ourselves. - */ - if (cas_token) { - ZVAL_DOUBLE(cas_token, 0.0); + 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, buffer, buffer_len); + efree(buffer); + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_IGBINARY); } + break; +#endif - if (status == MEMCACHED_NOTFOUND && fci.size != 0) { - status = php_memc_do_cache_callback(getThis(), &fci, &fcc, key, key_len, return_value TSRMLS_CC); + /* + 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 - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - memcached_result_free(&result); - RETURN_FROM_GET; + /* + 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; + } - /* if we have a callback, all processing is done */ - if (fci.size != 0) { - memcached_result_free(&result); - return; + /* 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; } + zend_string_forget_hash_val(payload); - /* Fetch all remaining results */ - memcached_result_st dummy_result; - memcached_return dummy_status = MEMCACHED_SUCCESS; - memcached_result_create(m_obj->memc, &dummy_result); - while (memcached_fetch_result(m_obj->memc, &dummy_result, &dummy_status) != NULL) {} - memcached_result_free(&dummy_result); + /* turn off compression for values below the threshold */ + if (ZSTR_LEN(payload) == 0 || ZSTR_LEN(payload) < MEMC_G(compression_threshold)) { + should_compress = 0; + } - payload = memcached_result_value(&result); - payload_len = memcached_result_length(&result); - flags = memcached_result_flags(&result); - if (cas_token) { - cas = memcached_result_cas(&result); + /* If we have compression flag, compress the value */ + if (should_compress) { + /* status */ + if (!s_compress_value (memc_user_data->compression_type, &payload, flags)) { + zend_string_release(payload); + return NULL; + } } - if (php_memc_zval_from_payload(return_value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { - memcached_result_free(&result); - i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; - RETURN_FROM_GET; + if (memc_user_data->set_udf_flags >= 0) { + MEMC_VAL_SET_USER_FLAGS(*flags, ((uint32_t) memc_user_data->set_udf_flags)); } - if (cas_token) { - zval_dtor(cas_token); - ZVAL_DOUBLE(cas_token, (double)cas); + 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; } - if (udf_flags) { - zval_dtor(udf_flags); - ZVAL_LONG(udf_flags, MEMC_VAL_GET_USER_FLAGS(flags)); + 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; + 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; + } } - memcached_result_free(&result); +#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 = 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 = 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; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!f!S", &persistent_id, &fci, &fci_cache, &conn_str) == FAILURE) { + return; + } + + 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->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 */ + { +#ifdef mikko_0 + fprintf (stderr, "consistent_hash_enabled=%d binary_protocol_enabled=%d connect_timeout=%ld\n", + MEMC_G(default_behavior.consistent_hash_enabled), MEMC_G(default_behavior.binary_protocol_enabled), MEMC_G(default_behavior.connect_timeout)); +#endif + + memcached_return rc; + + if (MEMC_G(default_behavior.consistent_hash_enabled)) { + + 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)) { + 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)) { + 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_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); + } } /* }}} */ -/* {{{ Memcached::getMulti(array keys [, array &cas_tokens [, array &udf_flags ] ]) - Returns values for the given keys or false */ -PHP_METHOD(Memcached, getMulti) + + +static +void s_hash_to_keys(php_memc_keys_t *keys_out, HashTable *hash_in, zend_bool preserve_order, zval *return_value) { - php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + 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 = {}; + 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) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|f!l", &server_key, &key, &fci, &fcc, &get_flags) == FAILURE) { + return; + } + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|f!l", &key, &fci, &fcc, &get_flags) == FAILURE) { + return; + } + } + + 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::getMultiByKey(string server_key, array keys [, array &cas_tokens [, array &udf_flags ] ]) - Returns values for the given keys from the server identified by the server key or false */ -PHP_METHOD(Memcached, getMultiByKey) +/* {{{ 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_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + 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) -{ - zval *keys = NULL; - char *server_key = NULL; - int server_key_len = 0; - size_t num_keys = 0; - zval **entry = NULL; - const char *payload = NULL; - size_t payload_len = 0; - const char **mkeys = NULL; - size_t *mkeys_len = NULL; - const char *tmp_key = NULL; - size_t res_key_len = 0; - uint32_t flags; - uint64_t cas = 0; - zval *cas_tokens = NULL; - zval *udf_flags = NULL; - uint64_t orig_cas_flag = 0; - zval *value; - long get_flags = 0; - int i = 0; - zend_bool preserve_order; - memcached_result_st result; - memcached_return status = MEMCACHED_SUCCESS; +{ + 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) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|zlz", &server_key, - &server_key_len, &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sa|l", &server_key, + &keys, &flags) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|zlz", &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &keys, &flags) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - preserve_order = (get_flags & MEMC_GET_PRESERVE_ORDER); - num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); - mkeys = safe_emalloc(num_keys, sizeof(*mkeys), 0); - mkeys_len = safe_emalloc(num_keys, sizeof(*mkeys_len), 0); array_init(return_value); - - /* - * Create the array of keys for libmemcached. If none of the keys were valid - * (strings), set bad key result code and return. - */ - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(keys)); - zend_hash_get_current_data(Z_ARRVAL_P(keys), (void**)&entry) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(keys))) { - - if (Z_TYPE_PP(entry) != IS_STRING) { - convert_to_string_ex(entry); - } - - if (Z_TYPE_PP(entry) == IS_STRING && Z_STRLEN_PP(entry) > 0) { - mkeys[i] = Z_STRVAL_PP(entry); - mkeys_len[i] = Z_STRLEN_PP(entry); - - if (preserve_order) { - add_assoc_null_ex(return_value, mkeys[i], mkeys_len[i] + 1); - } - i++; - } - } - - if (i == 0) { - i_obj->rescode = MEMCACHED_NOTFOUND; - efree(mkeys); - efree(mkeys_len); + if (zend_hash_num_elements(Z_ARRVAL_P(keys)) == 0) { + /* BC compatible */ + s_memc_set_status(intern, MEMCACHED_NOTFOUND, 0); return; } - /* - * Enable CAS support, but only if it is currently disabled. - */ - if (cas_tokens && PZVAL_IS_REF(cas_tokens)) { - orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); - if (orig_cas_flag == 0) { - memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); - } - } + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, mkeys, mkeys_len, i); - /* Handle error, but ignore, there might still be some result */ - php_memc_handle_error(i_obj, status TSRMLS_CC); + preserve_order = (flags & MEMC_GET_PRESERVE_ORDER); + s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), preserve_order, return_value); - /* - * Restore the CAS support flag, but only if we had to turn it on. - */ - if (cas_tokens && PZVAL_IS_REF(cas_tokens) && orig_cas_flag == 0) { - memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); - } + context.extended = (flags & MEMC_GET_EXTENDED); + context.return_value = return_value; - efree(mkeys); - efree(mkeys_len); + retval = php_memc_mget_apply(intern, server_key, &keys_out, s_get_multi_apply_fn, context.extended, &context); - /* - * Iterate through the result set and create the result array. The CAS tokens are - * returned as doubles, because we cannot store potential 64-bit values in longs. - */ - if (cas_tokens) { - if (PZVAL_IS_REF(cas_tokens)) { - /* cas_tokens was passed by reference, we'll create an array for it. */ - zval_dtor(cas_tokens); - array_init(cas_tokens); - } else { - /* Not passed by reference, we allow this (eg.: if you specify null - to not enable cas but you want to use the udf_flags parameter). - We destruct it and set it to null for the peace of mind. */ - zval_dtor(cas_tokens); - cas_tokens = NULL; - } - } + s_clear_keys(&keys_out); - /* - * Iterate through the result set and create the result array. The flags are - * returned as longs. - */ - if (udf_flags) { - zval_dtor(udf_flags); - array_init(udf_flags); + if (!retval && (s_memc_status_has_result_code(intern, MEMCACHED_NOTFOUND) || s_memc_status_has_result_code(intern, MEMCACHED_SOME_ERRORS))) { + return; } - memcached_result_create(m_obj->memc, &result); - while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { - char res_key [MEMCACHED_MAX_KEY]; - - if (status != MEMCACHED_SUCCESS) { - status = MEMCACHED_SOME_ERRORS; - php_memc_handle_error(i_obj, status TSRMLS_CC); - continue; - } - - payload = memcached_result_value(&result); - payload_len = memcached_result_length(&result); - flags = memcached_result_flags(&result); - tmp_key = memcached_result_key_value(&result); - res_key_len = memcached_result_key_length(&result); - - /* - * This may be a bug in libmemcached, the key is not null terminated - * whe using the binary protocol. - */ - memcpy (res_key, tmp_key, res_key_len >= MEMCACHED_MAX_KEY ? MEMCACHED_MAX_KEY - 1 : res_key_len); - res_key [res_key_len] = '\0'; - - ALLOC_INIT_ZVAL(value); - - if (php_memc_zval_from_payload(value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { - zval_ptr_dtor(&value); - if (EG(exception)) { - status = MEMC_RES_PAYLOAD_FAILURE; - php_memc_handle_error(i_obj, status TSRMLS_CC); - memcached_quit(m_obj->memc); - - break; - } - status = MEMCACHED_SOME_ERRORS; - i_obj->rescode = MEMCACHED_SOME_ERRORS; - - continue; - } - - add_assoc_zval_ex(return_value, res_key, res_key_len+1, value); - if (cas_tokens) { - cas = memcached_result_cas(&result); - add_assoc_double_ex(cas_tokens, res_key, res_key_len+1, (double)cas); - } - if (udf_flags) { - add_assoc_long_ex(udf_flags, res_key, res_key_len+1, MEMC_VAL_GET_USER_FLAGS(flags)); - } + if (!retval || EG(exception)) { + zval_dtor(return_value); + RETURN_FROM_GET; } +} +/* }}} */ - memcached_result_free(&result); +/* {{{ 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); +} +/* }}} */ - if (EG(exception)) { - /* XXX: cas_tokens should only be set on success, currently we're destructive */ - if (cas_tokens) { - zval_dtor(cas_tokens); - ZVAL_NULL(cas_tokens); - } - if (udf_flags) { - zval_dtor(udf_flags); - ZVAL_NULL(udf_flags); - } - zval_dtor(return_value); - RETURN_FALSE; - } +/* {{{ 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); } /* }}} */ @@ -904,140 +1574,113 @@ PHP_METHOD(Memcached, getDelayedByKey) } /* }}} */ + +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(¶ms[0], context->object); + array_init(¶ms[1]); + + s_create_result_array(key, value, cas, flags, ¶ms[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(¶ms[0]); + zval_ptr_dtor(¶ms[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; - char *server_key = NULL; - int server_key_len = 0; + zend_string *server_key = NULL; zend_bool with_cas = 0; - size_t num_keys = 0; - zval **entry = NULL; - const char **mkeys = NULL; - size_t *mkeys_len = NULL; - uint64_t orig_cas_flag = 0; + zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; - int i = 0; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; + if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|bf!", &server_key, - &server_key_len, &keys, &with_cas, &fci, &fcc) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sa/|bf!", &server_key, + &keys, &with_cas, &fci, &fcc) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|bf!", &keys, &with_cas, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|bf!", &keys, &with_cas, &fci, &fcc) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - /* - * Create the array of keys for libmemcached. If none of the keys were valid - * (strings), set bad key result code and return. - */ - num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); - mkeys = safe_emalloc(num_keys, sizeof(*mkeys), 0); - mkeys_len = safe_emalloc(num_keys, sizeof(*mkeys_len), 0); + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(keys)); - zend_hash_get_current_data(Z_ARRVAL_P(keys), (void**)&entry) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(keys))) { + s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), 0, NULL); - if (Z_TYPE_PP(entry) != IS_STRING) { - convert_to_string_ex(entry); - } - - if (Z_TYPE_PP(entry) == IS_STRING && Z_STRLEN_PP(entry) > 0) { - mkeys[i] = Z_STRVAL_PP(entry); - mkeys_len[i] = Z_STRLEN_PP(entry); - i++; - } - } - - if (i == 0) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - efree(mkeys); - efree(mkeys_len); - zval_dtor(return_value); - RETURN_FALSE; - } - - /* - * Enable CAS support, but only if it is currently disabled. - */ - if (with_cas) { - orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); - if (orig_cas_flag == 0) { - memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); - } - } - - /* - * Issue the request, but collect results only if the result callback is provided. - */ - status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, mkeys, mkeys_len, i); - - /* - * Restore the CAS support flag, but only if we had to turn it on. - */ - if (with_cas && orig_cas_flag == 0) { - memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); + 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); } - - efree(mkeys); - efree(mkeys_len); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - zval_dtor(return_value); - RETURN_FALSE; + else { + status = php_memc_mget_apply(intern, server_key, &keys_out, NULL, with_cas, NULL); } - if (fci.size != 0) { - /* - * We have a result callback. Iterate through the result set and invoke the - * callback for each one. - */ - memcached_result_st result; - - memcached_result_create(m_obj->memc, &result); - while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { - if (php_memc_do_result_callback(getThis(), &fci, &fcc, &result TSRMLS_CC) < 0) { - status = MEMCACHED_FAILURE; - break; - } - } - memcached_result_free(&result); + s_clear_keys(&keys_out); + RETURN_BOOL(status); +} +/* }}} */ - /* we successfully retrieved all rows */ - if (status == MEMCACHED_END) { - status = MEMCACHED_SUCCESS; - } - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - RETURN_FALSE; - } - } +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_TRUE; + return 0; // stop iterating after one } -/* }}} */ /* {{{ Memcached::fetch() Returns the next result from a previous delayed request */ PHP_METHOD(Memcached, fetch) { - const char *res_key = NULL; - size_t res_key_len = 0; - const char *payload = NULL; - size_t payload_len = 0; - zval *value; - uint32_t flags = 0; - uint64_t cas = 0; - memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; @@ -1046,58 +1689,35 @@ PHP_METHOD(Memcached, fetch) } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - memcached_result_create(m_obj->memc, &result); - if ((memcached_fetch_result(m_obj->memc, &result, &status)) == NULL) { - php_memc_handle_error(i_obj, status TSRMLS_CC); - memcached_result_free(&result); - RETURN_FALSE; - } - - payload = memcached_result_value(&result); - payload_len = memcached_result_length(&result); - flags = memcached_result_flags(&result); - res_key = memcached_result_key_value(&result); - res_key_len = memcached_result_key_length(&result); - cas = memcached_result_cas(&result); + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - ALLOC_INIT_ZVAL(value); + array_init(return_value); + status = php_memc_result_apply(intern, s_fetch_apply, 1, return_value); - if (php_memc_zval_from_payload(value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { - memcached_result_free(&result); - zval_ptr_dtor(&value); - i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; - RETURN_FALSE; + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + zval_ptr_dtor(return_value); + RETURN_FROM_GET; } +} +/* }}} */ - array_init(return_value); - add_assoc_stringl_ex(return_value, ZEND_STRS("key"), res_key, res_key_len, 1); - add_assoc_zval_ex(return_value, ZEND_STRS("value"), value); - if (cas != 0) { - /* XXX: also check against ULLONG_MAX or memc_behavior */ - add_assoc_double_ex(return_value, ZEND_STRS("cas"), (double)cas); - } - if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { - add_assoc_long_ex(return_value, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); - } +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; - memcached_result_free(&result); + 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) { - const char *res_key = NULL; - size_t res_key_len = 0; - const char *payload = NULL; - size_t payload_len = 0; - zval *value, *entry; - uint32_t flags; - uint64_t cas = 0; - memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; @@ -1106,53 +1726,19 @@ PHP_METHOD(Memcached, fetchAll) } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); array_init(return_value); - memcached_result_create(m_obj->memc, &result); - - while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { - payload = memcached_result_value(&result); - payload_len = memcached_result_length(&result); - flags = memcached_result_flags(&result); - res_key = memcached_result_key_value(&result); - res_key_len = memcached_result_key_length(&result); - cas = memcached_result_cas(&result); - - ALLOC_INIT_ZVAL(value); - - if (php_memc_zval_from_payload(value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { - memcached_result_free(&result); - zval_ptr_dtor(&value); - zval_dtor(return_value); - i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; - RETURN_FALSE; - } - - MAKE_STD_ZVAL(entry); - array_init(entry); - add_assoc_stringl_ex(entry, ZEND_STRS("key"), res_key, res_key_len, 1); - add_assoc_zval_ex(entry, ZEND_STRS("value"), value); - if (cas != 0) { - /* XXX: also check against ULLONG_MAX or memc_behavior */ - add_assoc_double_ex(entry, ZEND_STRS("cas"), (double)cas); - } - if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { - add_assoc_long_ex(entry, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); - } - add_next_index_zval(return_value, entry); - } - - memcached_result_free(&result); + status = php_memc_result_apply(intern, s_fetch_all_apply, 0, return_value); - if (status != MEMCACHED_END && php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ -/* {{{ Memcached::set(string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ Memcached::set(string key, mixed value [, int expiration ]) Sets the value for the given key */ PHP_METHOD(Memcached, set) { @@ -1160,7 +1746,7 @@ PHP_METHOD(Memcached, set) } /* }}} */ -/* {{{ Memcached::setByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1187,7 +1773,7 @@ PHP_METHOD(Memcached, touchByKey) #endif -/* {{{ Memcached::setMulti(array items [, int expiration [, int udf_flags ] ]) +/* {{{ Memcached::setMulti(array items [, int expiration ]) Sets the keys/values specified in the items array */ PHP_METHOD(Memcached, setMulti) { @@ -1195,7 +1781,7 @@ PHP_METHOD(Memcached, setMulti) } /* }}} */ -/* {{{ Memcached::setMultiByKey(string server_key, array items [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1203,127 +1789,60 @@ PHP_METHOD(Memcached, setMultiByKey) } /* }}} */ -#define PHP_MEMC_FAILOVER_RETRY \ - if (!by_key && retry < m_obj->store_retry_count) { \ - switch (i_obj->rescode) { \ - case MEMCACHED_HOST_LOOKUP_FAILURE: \ - case MEMCACHED_CONNECTION_FAILURE: \ - case MEMCACHED_CONNECTION_BIND_FAILURE: \ - case MEMCACHED_WRITE_FAILURE: \ - case MEMCACHED_READ_FAILURE: \ - case MEMCACHED_UNKNOWN_READ_FAILURE: \ - case MEMCACHED_PROTOCOL_ERROR: \ - case MEMCACHED_SERVER_ERROR: \ - case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: \ - case MEMCACHED_TIMEOUT: \ - case MEMCACHED_FAIL_UNIX_SOCKET: \ - case MEMCACHED_SERVER_MARKED_DEAD: \ - case MEMCACHED_SERVER_TEMPORARILY_DISABLED: \ - if (memcached_server_count(m_obj->memc) > 0) { \ - retry++; \ - i_obj->rescode = 0; \ - goto retry; \ - } \ - break; \ - } \ - } - /* {{{ -- php_memc_setMulti_impl */ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { zval *entries; - char *server_key = NULL; - int server_key_len = 0; + zend_string *server_key = NULL; time_t expiration = 0; - long udf_flags = 0; - zval **entry; - char *str_key; - uint str_key_len; - ulong num_key; - char *payload; - size_t payload_len; - uint32_t flags = 0; - uint32_t retry = 0; - memcached_return status; - char tmp_key[MEMCACHED_MAX_KEY]; + zval *value; + zend_string *skey; + zend_ulong num_key; + int tmp_len = 0; MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|ll", &server_key, - &server_key_len, &entries, &expiration, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sa|ll", &server_key, + &entries, &expiration) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ll", &entries, &expiration, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|ll", &entries, &expiration) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - /* - * php_memcached uses 16 bits internally to store type, compression and serialization info. - * We use 16 upper bits to store user defined flags. - */ - if (udf_flags > 0) { - if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); - } - } - - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(entries)); - zend_hash_get_current_data(Z_ARRVAL_P(entries), (void**)&entry) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(entries))) { - int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(entries), &str_key, &str_key_len, &num_key, 0, NULL); - - if (key_type == HASH_KEY_IS_LONG) { - /* Array keys are unsigned, but php integers are signed. - * Keys must be converted to signed strings that match - * php integers. */ - assert(sizeof(tmp_key) >= sizeof(ZEND_TOSTR(LONG_MIN))); + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - str_key_len = sprintf(tmp_key, "%ld", (long)num_key) + 1; - str_key = (char *)tmp_key; - } else if (key_type != HASH_KEY_IS_STRING) { - continue; - } + ZEND_HASH_FOREACH_KEY_VAL (Z_ARRVAL_P(entries), num_key, skey, value) { + zend_string *str_key = NULL; - flags = 0; - if (m_obj->compression) { - MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); + if (skey) { + str_key = skey; } + else { + char tmp_key[64]; - if (udf_flags > 0) { - MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); + tmp_len = snprintf(tmp_key, sizeof(tmp_key) - 1, "%ld", (long)num_key); + str_key = zend_string_init(tmp_key, tmp_len, 0); } - payload = php_memc_zval_to_payload(*entry, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); - if (payload == NULL) { - i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; - RETURN_FALSE; + 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)); } -retry: - if (!by_key) { - status = memcached_set(m_obj->memc, str_key, str_key_len-1, payload, payload_len, expiration, flags); - } else { - status = memcached_set_by_key(m_obj->memc, server_key, server_key_len, str_key, str_key_len-1, payload, payload_len, expiration, flags); + if (!skey) { + zend_string_release (str_key); } - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - PHP_MEMC_FAILOVER_RETRY - efree(payload); - RETURN_FALSE; - } - efree(payload); - } + } ZEND_HASH_FOREACH_END(); - RETURN_TRUE; + RETURN_BOOL(!s_memc_status_has_error(intern)); } /* }}} */ -/* {{{ Memcached::add(string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1331,7 +1850,7 @@ PHP_METHOD(Memcached, add) } /* }}} */ -/* {{{ Memcached::addByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1371,7 +1890,7 @@ PHP_METHOD(Memcached, prependByKey) } /* }}} */ -/* {{{ Memcached::replace(string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1379,7 +1898,7 @@ PHP_METHOD(Memcached, replace) } /* }}} */ -/* {{{ Memcached::replaceByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1387,258 +1906,129 @@ PHP_METHOD(Memcached, replaceByKey) } /* }}} */ - /* {{{ -- php_memc_store_impl */ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key) { - char *key = NULL; - int key_len = 0; - char *server_key = NULL; - int server_key_len = 0; - char *s_value = NULL; - int s_value_len = 0; - zval s_zvalue; - zval *value; - long expiration = 0; - long udf_flags = 0; - char *payload = NULL; - size_t payload_len; - uint32_t flags = 0; - uint32_t retry = 0; - memcached_return status; + 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) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &server_key, - &server_key_len, &key, &key_len, &s_value, &s_value_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS", &server_key, &key, &s_value) == FAILURE) { return; } - INIT_ZVAL(s_zvalue); value = &s_zvalue; - ZVAL_STRINGL(value, s_value, s_value_len, 0); + ZVAL_STR(value, s_value); } else if (op == MEMC_OP_TOUCH) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &server_key, - &server_key_len, &key, &key_len, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &server_key, &key, &expiration) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|ll", &server_key, - &server_key_len, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSz|l", &server_key, &key, &value, &expiration) == FAILURE) { return; } } } else { if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, - &s_value, &s_value_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &key, &s_value) == FAILURE) { return; } - INIT_ZVAL(s_zvalue); value = &s_zvalue; - ZVAL_STRINGL(value, s_value, s_value_len, 0); + ZVAL_STR(value, s_value); } else if (op == MEMC_OP_TOUCH) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, - &key_len, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &key, &expiration) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|ll", &key, &key_len, - &value, &expiration, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &key, &value, &expiration) == FAILURE) { return; } } } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - if (key_len == 0 || key_len > MEMC_OBJECT_KEY_MAX_LENGTH || strchr(key, ' ') || strchr(key, '\n')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FALSE; - } + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + MEMC_CHECK_KEY(intern, key); - if (m_obj->compression) { + 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 TSRMLS_CC, E_WARNING, "cannot append/prepend with compression turned on"); - return; + php_error_docref(NULL, E_WARNING, "cannot append/prepend with compression turned on"); + RETURN_NULL(); } - MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); } - /* - * php_memcached uses 16 bits internally to store type, compression and serialization info. - * We use 16 upper bits to store user defined flags. - */ - if (udf_flags > 0) { - if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); - } - MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); - } if (op == MEMC_OP_TOUCH) { #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000016 - if (memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "using touch command with binary protocol is not recommended with libmemcached versions below 1.0.16"); - } -#endif - } else { - payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); - if (payload == NULL) { - i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; - RETURN_FALSE; + if (memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { + php_error_docref(NULL, E_WARNING, "using touch command with binary protocol is not recommended with libmemcached versions below 1.0.16"); } - } -retry: - switch (op) { - case MEMC_OP_SET: - if (!server_key) { - status = memcached_set(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); - } else { - status = memcached_set_by_key(m_obj->memc, server_key, server_key_len, key, - key_len, payload, payload_len, expiration, flags); - } - break; -#ifdef HAVE_MEMCACHED_TOUCH - case MEMC_OP_TOUCH: - if (!server_key) { - status = memcached_touch(m_obj->memc, key, key_len, expiration); - } else { - status = memcached_touch_by_key(m_obj->memc, server_key, server_key_len, key, - key_len, expiration); - } - break; #endif - case MEMC_OP_ADD: - if (!server_key) { - status = memcached_add(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); - } else { - status = memcached_add_by_key(m_obj->memc, server_key, server_key_len, key, - key_len, payload, payload_len, expiration, flags); - } - break; - - case MEMC_OP_REPLACE: - if (!server_key) { - status = memcached_replace(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); - } else { - status = memcached_replace_by_key(m_obj->memc, server_key, server_key_len, key, - key_len, payload, payload_len, expiration, flags); - } - break; - - case MEMC_OP_APPEND: - if (!server_key) { - status = memcached_append(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); - } else { - status = memcached_append_by_key(m_obj->memc, server_key, server_key_len, key, - key_len, payload, payload_len, expiration, flags); - } - break; - - case MEMC_OP_PREPEND: - if (!server_key) { - status = memcached_prepend(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); - } else { - status = memcached_prepend_by_key(m_obj->memc, server_key, server_key_len, key, - key_len, payload, payload_len, expiration, flags); - } - break; - - default: - /* not reached */ - status = 0; - assert(0); - break; - } - - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - PHP_MEMC_FAILOVER_RETRY - RETVAL_FALSE; - } else { - RETVAL_TRUE; } - if (payload) { - efree(payload); + 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) { - double cas_d; + zval *zv_cas; uint64_t cas; - char *key = NULL; - int key_len = 0; - char *server_key = NULL; - int server_key_len = 0; + zend_string *key; + zend_string *server_key = NULL; zval *value; time_t expiration = 0; - long udf_flags = 0; - char *payload; - size_t payload_len; + zend_string *payload; uint32_t flags = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dssz|ll", &cas_d, &server_key, - &server_key_len, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zSSz|ll", &zv_cas, &server_key, &key, + &value, &expiration) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dsz|ll", &cas_d, &key, &key_len, - &value, &expiration, &udf_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zSz|ll", &zv_cas, &key, &value, + &expiration) == FAILURE) { return; } } - MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - if (key_len == 0 || key_len > MEMC_OBJECT_KEY_MAX_LENGTH || strchr(key, ' ') || strchr(key, '\n')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FALSE; - } - - DVAL_TO_LVAL(cas_d, cas); - - if (m_obj->compression) { - MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); - } - - /* - * php_memcached uses 16 bits internally to store type, compression and serialization info. - * We use 16 upper bits to store user defined flags. - */ - if (udf_flags > 0) { - if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); - } - MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); - } - - payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); + 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) { - i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; + intern->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } if (by_key) { - status = memcached_cas_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags, cas); + 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(m_obj->memc, key, key_len, payload, payload_len, expiration, flags, cas); + status = memcached_cas(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas); } - efree(payload); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + + zend_string_release(payload); + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } @@ -1646,7 +2036,7 @@ static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) } /* }}} */ -/* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1654,7 +2044,7 @@ PHP_METHOD(Memcached, cas) } /* }}} */ -/* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) +/* {{{ 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) { @@ -1697,40 +2087,34 @@ PHP_METHOD(Memcached, deleteMultiByKey) /* {{{ -- php_memc_delete_impl */ static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { - char *key = NULL; - int key_len = 0; - char *server_key = NULL; - int server_key_len = 0; + zend_string *key, *server_key; time_t expiration = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &server_key, - &server_key_len, &key, &key_len, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &server_key, &key, &expiration) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, - &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &key, &expiration) == FAILURE) { return; } - server_key = key; - server_key_len = key_len; + server_key = key; } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + MEMC_CHECK_KEY(intern, key); - if (key_len == 0 || key_len > MEMC_OBJECT_KEY_MAX_LENGTH || strchr(key, ' ') || strchr(key, '\n')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FALSE; + 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); } - status = memcached_delete_by_key(m_obj->memc, server_key, server_key_len, key, - key_len, expiration); - - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } @@ -1741,55 +2125,50 @@ static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) /* {{{ -- php_memc_deleteMulti_impl */ static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { - zval *entries; - char *server_key = NULL; - int server_key_len = 0; + zval *entries, *zv, ret; + zend_string *server_key = NULL; time_t expiration = 0; - zval **entry; + zend_string *entry; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|l", &server_key, - &server_key_len, &entries, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sa/|l", &server_key, &entries, &expiration) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|l", &entries, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &entries, &expiration) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); array_init(return_value); - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(entries)); - zend_hash_get_current_data(Z_ARRVAL_P(entries), (void**)&entry) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(entries))) { - - if (Z_TYPE_PP(entry) != IS_STRING) { - convert_to_string_ex(entry); - } + ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P(entries), zv) { + entry = zval_get_string(zv); - if (Z_STRLEN_PP(entry) == 0) { + if (ZSTR_LEN(entry) == 0) { + zend_string_release(entry); continue; } - if (!by_key) { - server_key = Z_STRVAL_PP(entry); - server_key_len = Z_STRLEN_PP(entry); + 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); } - status = memcached_delete_by_key(m_obj->memc, server_key, server_key_len, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry), expiration); - - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - add_assoc_long(return_value, Z_STRVAL_PP(entry), status); + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + ZVAL_LONG(&ret, status); } else { - add_assoc_bool(return_value, Z_STRVAL_PP(entry), 1); + ZVAL_TRUE(&ret); } - } + zend_symtable_update(Z_ARRVAL_P(return_value), entry, &ret); + zend_string_release(entry); + } ZEND_HASH_FOREACH_END(); return; } @@ -1798,77 +2177,80 @@ static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by /* {{{ -- php_memc_incdec_impl */ static void php_memc_incdec_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key, zend_bool incr) { - char *key, *server_key; - int key_len, server_key_len; + zend_string *key, *server_key = NULL; long offset = 1; - uint64_t value, initial = 0; + uint64_t value = UINT64_MAX, initial = 0; time_t expiry = 0; memcached_return status; int n_args = ZEND_NUM_ARGS(); - uint32_t retry = 0; MEMC_METHOD_INIT_VARS; if (!by_key) { - if (zend_parse_parameters(n_args TSRMLS_CC, "s|lll", &key, &key_len, &offset, &initial, &expiry) == FAILURE) { + if (zend_parse_parameters(n_args, "S|lll", &key, &offset, &initial, &expiry) == FAILURE) { return; } } else { - if (zend_parse_parameters(n_args TSRMLS_CC, "ss|lll", &server_key, &server_key_len, &key, &key_len, &offset, &initial, &expiry) == FAILURE) { + if (zend_parse_parameters(n_args, "SS|lll", &server_key, &key, &offset, &initial, &expiry) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - if (key_len == 0 || key_len > MEMC_OBJECT_KEY_MAX_LENGTH || strchr(key, ' ') || strchr(key, '\n')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FALSE; - } + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + MEMC_CHECK_KEY(intern, key); if (offset < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset has to be > 0"); + php_error_docref(NULL, E_WARNING, "offset has to be > 0"); RETURN_FALSE; } -retry: if ((!by_key && n_args < 3) || (by_key && n_args < 4)) { if (by_key) { if (incr) { - status = memcached_increment_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, &value); + status = memcached_increment_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, &value); } else { - status = memcached_decrement_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, &value); + status = memcached_decrement_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, &value); } } else { if (incr) { - status = memcached_increment(m_obj->memc, key, key_len, (unsigned int)offset, &value); + status = memcached_increment(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, &value); } else { - status = memcached_decrement(m_obj->memc, key, key_len, (unsigned int)offset, &value); + status = memcached_decrement(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, &value); } } + } else { - if (!memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Initial value is only supported with binary protocol"); + 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(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, initial, expiry, &value); + status = memcached_increment_with_initial_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, initial, expiry, &value); } else { - status = memcached_decrement_with_initial_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, initial, expiry, &value); + status = memcached_decrement_with_initial_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, initial, expiry, &value); } } else { if (incr) { - status = memcached_increment_with_initial(m_obj->memc, key, key_len, (unsigned int)offset, initial, expiry, &value); + status = memcached_increment_with_initial(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, initial, expiry, &value); } else { - status = memcached_decrement_with_initial(m_obj->memc, key, key_len, (unsigned int)offset, initial, expiry, &value); + status = memcached_decrement_with_initial(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), (unsigned int)offset, initial, 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 (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - PHP_MEMC_FAILOVER_RETRY + if (value == UINT64_MAX) { RETURN_FALSE; } @@ -1912,33 +2294,31 @@ PHP_METHOD(Memcached, incrementByKey) Adds the given memcache server to the list */ PHP_METHOD(Memcached, addServer) { - char *host; - int host_len; + zend_string *host; long port, weight = 0; memcached_return status; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", &host, &host_len, - &port, &weight) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|l", &host, &port, &weight) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000002 - if (host[0] == '/') { /* unix domain socket */ - status = memcached_server_add_unix_socket_with_weight(m_obj->memc, host, weight); - } else if (memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_USE_UDP)) { - status = memcached_server_add_udp_with_weight(m_obj->memc, host, port, weight); + if (ZSTR_VAL(host)[0] == '/') { /* unix domain socket */ + status = memcached_server_add_unix_socket_with_weight(intern->memc, ZSTR_VAL(host), weight); + } else if (memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_USE_UDP)) { + status = memcached_server_add_udp_with_weight(intern->memc, ZSTR_VAL(host), port, weight); } else { - status = memcached_server_add_with_weight(m_obj->memc, host, port, weight); + status = memcached_server_add_with_weight(intern->memc, ZSTR_VAL(host), port, weight); } #else - status = memcached_server_add_with_weight(m_obj->memc, host, port, weight); + status = memcached_server_add_with_weight(intern->memc, ZSTR_VAL(host), port, weight); #endif - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } @@ -1951,78 +2331,83 @@ PHP_METHOD(Memcached, addServer) PHP_METHOD(Memcached, addServers) { zval *servers; - zval **entry; - zval **z_host, **z_port, **z_weight = NULL; - uint32_t weight = 0; + 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; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &servers) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &servers) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(servers)), i = 0; - zend_hash_get_current_data(Z_ARRVAL_P(servers), (void **)&entry) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(servers)), i++) { + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - if (Z_TYPE_PP(entry) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "server list entry #%d is not an array", i+1); + 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_PP(entry)); + entry_size = zend_hash_num_elements(Z_ARRVAL_P(entry)); if (entry_size > 1) { - zend_hash_internal_pointer_reset(Z_ARRVAL_PP(entry)); + 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 (zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void **)&z_host) == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get server host for entry #%d", i+1); + 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(Z_ARRVAL_PP(entry)) == FAILURE || - zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void **)&z_port) == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get server port for entry #%d", i+1); + 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; } - convert_to_string_ex(z_host); - convert_to_long_ex(z_port); + 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(Z_ARRVAL_PP(entry)) == FAILURE || - zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void **)&z_weight) == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get server weight for entry #%d", i+1); + 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); } - convert_to_long_ex(z_weight); - weight = Z_LVAL_PP(z_weight); + weight = zval_get_long(z_weight); } - list = memcached_server_list_append_with_weight(list, Z_STRVAL_PP(z_host), - Z_LVAL_PP(z_port), weight, &status); + list = memcached_server_list_append_with_weight(list, ZSTR_VAL(host), port, weight, &status); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) == 0) { + 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 TSRMLS_CC, E_WARNING, "could not add entry #%d to the server list", i+1); - } + 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(m_obj->memc, list); + status = memcached_server_push(intern->memc, list); memcached_server_list_free(list); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } @@ -2030,12 +2415,10 @@ PHP_METHOD(Memcached, addServers) } /* }}} */ - /* {{{ Memcached::getServerList() Returns the list of the memcache servers in use */ PHP_METHOD(Memcached, getServerList) { - struct callbackContext context = {0}; memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; @@ -2045,10 +2428,9 @@ PHP_METHOD(Memcached, getServerList) MEMC_METHOD_FETCH_OBJECT; - callbacks[0] = php_memc_do_serverlist_callback; + callbacks[0] = s_server_cursor_list_servers_cb; array_init(return_value); - context.return_value = return_value; - memcached_server_cursor(m_obj->memc, callbacks, &context, 1); + memcached_server_cursor(intern->memc, callbacks, return_value, 1); } /* }}} */ @@ -2056,32 +2438,26 @@ PHP_METHOD(Memcached, getServerList) Returns the server identified by the given server key */ PHP_METHOD(Memcached, getServerByKey) { - char *server_key; - int server_key_len; + zend_string *server_key; php_memcached_instance_st server_instance; memcached_return error; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &server_key, &server_key_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &server_key) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - if (server_key_len == 0 || strchr(server_key, ' ')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FALSE; - } + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - server_instance = memcached_server_by_key(m_obj->memc, server_key, server_key_len, &error); + server_instance = memcached_server_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), &error); if (server_instance == NULL) { - php_memc_handle_error(i_obj, error TSRMLS_CC); + 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), 1); + 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); } @@ -2099,7 +2475,7 @@ PHP_METHOD(Memcached, resetServerList) MEMC_METHOD_FETCH_OBJECT; - memcached_servers_reset(m_obj->memc); + memcached_servers_reset(intern->memc); RETURN_TRUE; } /* }}} */ @@ -2116,7 +2492,7 @@ PHP_METHOD(Memcached, quit) MEMC_METHOD_FETCH_OBJECT; - memcached_quit(m_obj->memc); + memcached_quit(intern->memc); RETURN_TRUE; } /* }}} */ @@ -2132,7 +2508,7 @@ PHP_METHOD(Memcached, flushBuffers) } MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(memcached_flush_buffers(m_obj->memc) == MEMCACHED_SUCCESS); + RETURN_BOOL(memcached_flush_buffers(intern->memc) == MEMCACHED_SUCCESS); } /* }}} */ @@ -2149,7 +2525,7 @@ PHP_METHOD(Memcached, getLastErrorMessage) MEMC_METHOD_FETCH_OBJECT; - RETURN_STRING(memcached_last_error_message(m_obj->memc), 1); + RETURN_STRING(memcached_last_error_message(intern->memc)); } /* }}} */ @@ -2165,7 +2541,7 @@ PHP_METHOD(Memcached, getLastErrorCode) MEMC_METHOD_FETCH_OBJECT; - RETURN_LONG(memcached_last_error(m_obj->memc)); + RETURN_LONG(memcached_last_error(intern->memc)); } /* }}} */ @@ -2181,7 +2557,7 @@ PHP_METHOD(Memcached, getLastErrorErrno) MEMC_METHOD_FETCH_OBJECT; - RETURN_LONG(memcached_last_error_errno(m_obj->memc)); + RETURN_LONG(memcached_last_error_errno(intern->memc)); } /* }}} */ #endif @@ -2200,25 +2576,91 @@ PHP_METHOD(Memcached, getLastDisconnectedServer) MEMC_METHOD_FETCH_OBJECT; - server_instance = memcached_server_get_last_disconnect(m_obj->memc); + 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), 1); + 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_stat_st *stats; memcached_return status; - struct callbackContext context = {0}; - memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { @@ -2227,29 +2669,12 @@ PHP_METHOD(Memcached, getStats) MEMC_METHOD_FETCH_OBJECT; - if (memcached_server_count(m_obj->memc) == 0) { - array_init(return_value); - return; - } - - stats = memcached_stat(m_obj->memc, NULL, &status); - php_memc_handle_error(i_obj, status TSRMLS_CC); - if (stats == NULL) { - RETURN_FALSE; - } else if (status != MEMCACHED_SUCCESS && status != MEMCACHED_SOME_ERRORS) { - memcached_stat_free(m_obj->memc, stats); + array_init(return_value); + status = memcached_stat_execute(intern->memc, NULL, s_stat_execute_cb, return_value); + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + zval_ptr_dtor(return_value); RETURN_FALSE; } - - array_init(return_value); - - callbacks[0] = php_memc_do_stats_callback; - context.i = 0; - context.stats = stats; - context.return_value = return_value; - memcached_server_cursor(m_obj->memc, callbacks, &context, 1); - - memcached_stat_free(m_obj->memc, stats); } /* }}} */ @@ -2257,8 +2682,7 @@ PHP_METHOD(Memcached, getStats) Returns the version of each memcached server in the pool */ PHP_METHOD(Memcached, getVersion) { - memcached_return status = MEMCACHED_SUCCESS; - struct callbackContext context = {0}; + memcached_return status; memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; @@ -2268,28 +2692,29 @@ PHP_METHOD(Memcached, getVersion) MEMC_METHOD_FETCH_OBJECT; - array_init(return_value); - - status = memcached_version(m_obj->memc); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - zval_dtor(return_value); + status = memcached_version(intern->memc); + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } - callbacks[0] = php_memc_do_version_callback; - context.return_value = return_value; + callbacks[0] = s_server_cursor_version_cb; - memcached_server_cursor(m_obj->memc, callbacks, &context, 1); + 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 php_memc_dump_func_callback(const memcached_st *ptr __attribute__((unused)), \ - const char *key, size_t key_length, void *context) +static +memcached_return s_dump_keys_cb(const memcached_st *ptr, const char *key, size_t key_length, void *in_context) { - zval *ctx = (zval*) context; - add_next_index_string(ctx, (char*) key, 1); + zval *return_value = (zval*) in_context; + add_next_index_stringl(return_value, key, key_length); return MEMCACHED_SUCCESS; } @@ -2300,12 +2725,17 @@ PHP_METHOD(Memcached, getAllKeys) memcached_dump_func callback[1]; MEMC_METHOD_INIT_VARS; - callback[0] = php_memc_dump_func_callback; + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + callback[0] = s_dump_keys_cb; MEMC_METHOD_FETCH_OBJECT; array_init(return_value); - rc = memcached_dump(m_obj->memc, callback, return_value, 1); - if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { + + rc = memcached_dump(intern->memc, callback, return_value, 1); + if (s_memc_status_handle_result_code(intern, rc) == FAILURE) { zval_dtor(return_value); RETURN_FALSE; } @@ -2320,15 +2750,15 @@ static PHP_METHOD(Memcached, flush) memcached_return status; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &delay) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &delay) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - status = memcached_flush(m_obj->memc, delay); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + status = memcached_flush(intern->memc, delay); + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } @@ -2345,7 +2775,7 @@ static PHP_METHOD(Memcached, getOption) memcached_behavior flag; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &option) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &option) == FAILURE) { return; } @@ -2353,22 +2783,22 @@ static PHP_METHOD(Memcached, getOption) switch (option) { case MEMC_OPT_COMPRESSION_TYPE: - RETURN_LONG(m_obj->compression_type); + RETURN_LONG(memc_user_data->compression_type); case MEMC_OPT_COMPRESSION: - RETURN_BOOL(m_obj->compression); + RETURN_BOOL(memc_user_data->compression_enabled); case MEMC_OPT_PREFIX_KEY: { memcached_return retval; char *result; - result = memcached_callback_get(m_obj->memc, MEMCACHED_CALLBACK_PREFIX_KEY, &retval); + result = memcached_callback_get(intern->memc, MEMCACHED_CALLBACK_PREFIX_KEY, &retval); if (retval == MEMCACHED_SUCCESS && result) { #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX == 0x00049000 - RETURN_STRINGL(result, strlen(result) - 1, 1); + RETURN_STRINGL(result, strlen(result)); #else - RETURN_STRING(result, 1); + RETURN_STRING(result); #endif } else { RETURN_EMPTY_STRING(); @@ -2376,17 +2806,21 @@ static PHP_METHOD(Memcached, getOption) } case MEMC_OPT_SERIALIZER: - RETURN_LONG((long)m_obj->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)m_obj->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(m_obj->memc) == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "no servers defined"); + if (memcached_server_count(intern->memc) == 0) { + php_error_docref(NULL, E_WARNING, "no servers defined"); return; } @@ -2395,44 +2829,46 @@ static PHP_METHOD(Memcached, getOption) * Assume that it's a libmemcached behavior option. */ flag = (memcached_behavior) option; - result = memcached_behavior_get(m_obj->memc, flag); + result = memcached_behavior_get(intern->memc, flag); RETURN_LONG((long)result); } } /* }}} */ -static int php_memc_set_option(php_memc_t *i_obj, long option, zval *value TSRMLS_DC) +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; - struct memc_obj *m_obj = i_obj->obj; + php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc); switch (option) { case MEMC_OPT_COMPRESSION: - convert_to_long(value); - m_obj->compression = Z_LVAL_P(value) ? 1 : 0; + memc_user_data->compression_enabled = zval_get_long(value) ? 1 : 0; break; case MEMC_OPT_COMPRESSION_TYPE: - convert_to_long(value); - if (Z_LVAL_P(value) == COMPRESSION_TYPE_FASTLZ || - Z_LVAL_P(value) == COMPRESSION_TYPE_ZLIB) { - m_obj->compression_type = Z_LVAL_P(value); + lval = zval_get_long(value); + if (lval == COMPRESSION_TYPE_FASTLZ || + lval == COMPRESSION_TYPE_ZLIB) { + memc_user_data->compression_type = lval; } else { /* invalid compression type */ - i_obj->rescode = MEMCACHED_INVALID_ARGUMENTS; + intern->rescode = MEMCACHED_INVALID_ARGUMENTS; return 0; } break; case MEMC_OPT_PREFIX_KEY: { + zend_string *str; char *key; #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX == 0x00049000 char tmp[MEMCACHED_PREFIX_KEY_MAX_SIZE - 1]; #endif - convert_to_string(value); - if (Z_STRLEN_P(value) == 0) { + str = zval_get_string(value); + if (ZSTR_VAL(str) == 0) { key = NULL; } else { /* @@ -2440,28 +2876,30 @@ static int php_memc_set_option(php_memc_t *i_obj, long option, zval *value TSRML character of the key prefix, to avoid the issue we pad it with a '0' */ #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX == 0x00049000 - snprintf(tmp, sizeof(tmp), "%s0", Z_STRVAL_P(value)); + snprintf(tmp, sizeof(tmp), "%s0", ZSTR_VAL(str)); key = tmp; #else - key = Z_STRVAL_P(value); + key = ZSTR_VAL(str); #endif } - if (memcached_callback_set(m_obj->memc, MEMCACHED_CALLBACK_PREFIX_KEY, key) == MEMCACHED_BAD_KEY_PROVIDED) { - i_obj->rescode = MEMCACHED_INVALID_ARGUMENTS; - php_error_docref(NULL TSRMLS_CC, E_WARNING, "bad key provided"); + 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; - convert_to_long(value); - rc = memcached_behavior_set(m_obj->memc, flag, (uint64_t) Z_LVAL_P(value)); + lval = zval_get_long(value); + rc = memcached_behavior_set(intern->memc, flag, (uint64_t)lval); - if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "error setting memcached option: %s", memcached_strerror (m_obj->memc, rc)); + 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; } @@ -2470,55 +2908,70 @@ static int php_memc_set_option(php_memc_t *i_obj, long option, zval *value TSRML * options on false case, like it does for MEMCACHED_BEHAVIOR_KETAMA * (non-weighted) case. We have to clean up ourselves. */ - if (!Z_LVAL_P(value)) { + if (!lval) { #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX > 0x00037000 - (void)memcached_behavior_set_key_hash(m_obj->memc, MEMCACHED_HASH_DEFAULT); - (void)memcached_behavior_set_distribution_hash(m_obj->memc, MEMCACHED_HASH_DEFAULT); - (void)memcached_behavior_set_distribution(m_obj->memc, MEMCACHED_DISTRIBUTION_MODULA); + (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); #else - m_obj->memc->hash = 0; - m_obj->memc->distribution = 0; + intern->memc->hash = 0; + intern->memc->distribution = 0; #endif } break; case MEMC_OPT_SERIALIZER: { - convert_to_long(value); + lval = zval_get_long(value); /* igbinary serializer */ #ifdef HAVE_MEMCACHED_IGBINARY - if (Z_LVAL_P(value) == SERIALIZER_IGBINARY) { - m_obj->serializer = SERIALIZER_IGBINARY; + if (lval == SERIALIZER_IGBINARY) { + memc_user_data->serializer = SERIALIZER_IGBINARY; } else #endif #ifdef HAVE_JSON_API - if (Z_LVAL_P(value) == SERIALIZER_JSON) { - m_obj->serializer = SERIALIZER_JSON; - } else if (Z_LVAL_P(value) == SERIALIZER_JSON_ARRAY) { - m_obj->serializer = SERIALIZER_JSON_ARRAY; + 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 (Z_LVAL_P(value) == SERIALIZER_MSGPACK) { - m_obj->serializer = SERIALIZER_MSGPACK; + if (lval == SERIALIZER_MSGPACK) { + memc_user_data->serializer = SERIALIZER_MSGPACK; } else #endif /* php serializer */ - if (Z_LVAL_P(value) == SERIALIZER_PHP) { - m_obj->serializer = SERIALIZER_PHP; + if (lval == SERIALIZER_PHP) { + memc_user_data->serializer = SERIALIZER_PHP; } else { - m_obj->serializer = SERIALIZER_PHP; - i_obj->rescode = MEMCACHED_INVALID_ARGUMENTS; - php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid serializer provided"); + 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: - convert_to_long(value); - m_obj->store_retry_count = Z_LVAL_P(value); + lval = zval_get_long(value); + memc_user_data->store_retry_count = lval; break; default: @@ -2530,18 +2983,18 @@ static int php_memc_set_option(php_memc_t *i_obj, long option, zval *value TSRML } else { flag = (memcached_behavior) option; - convert_to_long(value); + lval = zval_get_long(value); if (flag < MEMCACHED_BEHAVIOR_MAX) { - rc = memcached_behavior_set(m_obj->memc, flag, (uint64_t) Z_LVAL_P(value)); + rc = memcached_behavior_set(intern->memc, flag, (uint64_t)lval); } else { rc = MEMCACHED_INVALID_ARGUMENTS; } } - if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "error setting memcached option: %s", memcached_strerror (m_obj->memc, rc)); + 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; @@ -2550,9 +3003,9 @@ static int php_memc_set_option(php_memc_t *i_obj, long option, zval *value TSRML } static -uint32_t *s_zval_to_uint32_array (zval *input, size_t *num_elements TSRMLS_DC) +uint32_t *s_zval_to_uint32_array (zval *input, size_t *num_elements) { - zval **ppzval; + zval *pzval; uint32_t *retval; size_t i = 0; @@ -2564,34 +3017,19 @@ uint32_t *s_zval_to_uint32_array (zval *input, size_t *num_elements TSRMLS_DC) retval = ecalloc(*num_elements, sizeof(uint32_t)); - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(input)); - zend_hash_get_current_data(Z_ARRVAL_P(input), (void **) &ppzval) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(input)), i++) { - - long value = 0; - - if (Z_TYPE_PP(ppzval) == IS_LONG) { - value = Z_LVAL_PP(ppzval); - } - else { - zval tmp_zval, *tmp_pzval; - tmp_zval = **ppzval; - zval_copy_ctor(&tmp_zval); - tmp_pzval = &tmp_zval; - convert_to_long(tmp_pzval); - - value = (Z_LVAL_P(tmp_pzval) > 0) ? Z_LVAL_P(tmp_pzval) : 0; - zval_dtor(tmp_pzval); - } + 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 TSRMLS_CC, E_WARNING, "the map must contain positive integers"); + 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; } @@ -2610,35 +3048,35 @@ PHP_METHOD(Memcached, setBucket) memcached_return rc; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa!l", &zserver_map, &zforward_map, &replicas) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "aa!l", &zserver_map, &zforward_map, &replicas) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; if (zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "server map cannot be empty"); + 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 TSRMLS_CC, E_WARNING, "forward_map length must match the server_map length"); + php_error_docref(NULL, E_WARNING, "forward_map length must match the server_map length"); RETURN_FALSE; } if (replicas < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "replicas must be larger than zero"); + 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 TSRMLS_CC); + 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 TSRMLS_CC); + forward_map = s_zval_to_uint32_array (zforward_map, &forward_map_len); if (!forward_map) { efree (server_map); @@ -2646,9 +3084,9 @@ PHP_METHOD(Memcached, setBucket) } } - rc = memcached_bucket_set (m_obj->memc, server_map, forward_map, (uint32_t) server_map_len, replicas); + rc = memcached_bucket_set (intern->memc, server_map, forward_map, (uint32_t) server_map_len, replicas); - if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, rc) == FAILURE) { retval = 0;; } @@ -2667,38 +3105,28 @@ static PHP_METHOD(Memcached, setOptions) { zval *options; zend_bool ok = 1; - uint key_len; - char *key; + zend_string *key; ulong key_index; - zval **value; + zval *value; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &options) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; - for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(options)); - zend_hash_get_current_data(Z_ARRVAL_P(options), (void *) &value) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(options))) { - - if (zend_hash_get_current_key_ex(Z_ARRVAL_P(options), &key, &key_len, &key_index, 0, NULL) == HASH_KEY_IS_LONG) { - zval copy = **value; - zval_copy_ctor(©); - INIT_PZVAL(©); - - if (!php_memc_set_option(i_obj, (long) key_index, © TSRMLS_CC)) { + 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; } - - zval_dtor(©); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid configuration option"); - ok = 0; } - } + } ZEND_HASH_FOREACH_END(); RETURN_BOOL(ok); } @@ -2712,13 +3140,13 @@ static PHP_METHOD(Memcached, setOption) zval *value; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz/", &option, &value) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz/", &option, &value) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(php_memc_set_option(i_obj, option, value TSRMLS_CC)); + RETURN_BOOL(php_memc_set_option(intern, option, value)); } /* }}} */ @@ -2729,29 +3157,26 @@ static PHP_METHOD(Memcached, setSaslAuthData) { MEMC_METHOD_INIT_VARS; memcached_return status; + zend_string *user, *pass; - char *user, *pass; - int user_len, pass_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &user, &user_len, &pass, &pass_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &user, &pass) == FAILURE) { return; } - if (!MEMC_G(use_sasl)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "SASL support (memcached.use_sasl) isn't enabled in php.ini"); + if (!php_memc_init_sasl_if_needed()) { RETURN_FALSE; } MEMC_METHOD_FETCH_OBJECT; - if (!memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "SASL is only supported with binary protocol"); + 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; } - m_obj->has_sasl_data = 1; - status = memcached_set_sasl_auth_data(m_obj->memc, user, pass); + memc_user_data->has_sasl_data = 1; + status = memcached_set_sasl_auth_data(intern->memc, ZSTR_VAL(user), ZSTR_VAL(pass)); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -2771,7 +3196,7 @@ static PHP_METHOD(Memcached, getResultCode) MEMC_METHOD_FETCH_OBJECT; - RETURN_LONG(i_obj->rescode); + RETURN_LONG(intern->rescode); } /* }}} */ @@ -2787,24 +3212,22 @@ static PHP_METHOD(Memcached, getResultMessage) MEMC_METHOD_FETCH_OBJECT; - switch (i_obj->rescode) { + switch (intern->rescode) { case MEMC_RES_PAYLOAD_FAILURE: - RETURN_STRING("PAYLOAD FAILURE", 1); + RETURN_STRING("PAYLOAD FAILURE"); break; case MEMCACHED_ERRNO: case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: case MEMCACHED_UNKNOWN_READ_FAILURE: - if (i_obj->memc_errno) { - char *str; - int str_len; - str_len = spprintf(&str, 0, "%s: %s", memcached_strerror(m_obj->memc, (memcached_return)i_obj->rescode), - strerror(i_obj->memc_errno)); - RETURN_STRINGL(str, str_len, 0); + 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(m_obj->memc, (memcached_return)i_obj->rescode), 1); + RETURN_STRING(memcached_strerror(intern->memc, (memcached_return)intern->rescode)); break; } @@ -2823,7 +3246,7 @@ static PHP_METHOD(Memcached, isPersistent) MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(i_obj->is_persistent); + RETURN_BOOL(memc_user_data->is_persistent); } /* }}} */ @@ -2839,7 +3262,7 @@ static PHP_METHOD(Memcached, isPristine) MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(i_obj->is_pristine); + RETURN_BOOL(intern->is_pristine); } /* }}} */ @@ -2848,515 +3271,197 @@ static PHP_METHOD(Memcached, isPristine) ****************************************/ /* {{{ constructor/destructor */ -static void php_memc_destroy(struct memc_obj *m_obj, zend_bool persistent TSRMLS_DC) -{ -#if HAVE_MEMCACHED_SASL - if (m_obj->has_sasl_data) { - memcached_destroy_sasl_auth_data(m_obj->memc); - } -#endif - if (m_obj->memc) { - memcached_free(m_obj->memc); - } - - pefree(m_obj, persistent); -} - -static void php_memc_free_storage(php_memc_t *i_obj TSRMLS_DC) -{ - zend_object_std_dtor(&i_obj->zo TSRMLS_CC); - - if (i_obj->obj && !i_obj->is_persistent) { - php_memc_destroy(i_obj->obj, 0 TSRMLS_CC); - } - - i_obj->obj = NULL; - efree(i_obj); -} - -zend_object_value php_memc_new(zend_class_entry *ce TSRMLS_DC) -{ - zend_object_value retval; - php_memc_t *i_obj; - - i_obj = ecalloc(1, sizeof(*i_obj)); - zend_object_std_init( &i_obj->zo, ce TSRMLS_CC ); -#if PHP_VERSION_ID >= 50400 - object_properties_init( (zend_object *) i_obj, ce); -#else - { - zval *tmp; - zend_hash_copy(i_obj->zo.properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); - } -#endif - - retval.handle = zend_objects_store_put(i_obj, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)php_memc_free_storage, NULL TSRMLS_CC); - retval.handlers = &memcached_object_handlers; - - return retval; -} - -#ifdef HAVE_MEMCACHED_PROTOCOL static -void php_memc_server_free_storage(php_memc_server_t *intern TSRMLS_DC) -{ - zend_object_std_dtor(&intern->zo TSRMLS_CC); - efree (intern); -} - -zend_object_value php_memc_server_new(zend_class_entry *ce TSRMLS_DC) -{ - zend_object_value retval; - php_memc_server_t *intern; - zval *tmp; - - intern = ecalloc(1, sizeof(php_memc_server_t)); - zend_object_std_init (&intern->zo, ce TSRMLS_CC); -#if PHP_VERSION_ID >= 50400 - object_properties_init( (zend_object *) intern, ce); -#else - zend_hash_copy(intern->zo.properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); -#endif - - intern->handler = php_memc_proto_handler_new (); - - retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)php_memc_server_free_storage, NULL TSRMLS_CC); - retval.handlers = &memcached_server_object_handlers; - - return retval; -} -#endif - -ZEND_RSRC_DTOR_FUNC(php_memc_dtor) -{ - if (rsrc->ptr) { - struct memc_obj *m_obj = (struct memc_obj *)rsrc->ptr; - php_memc_destroy(m_obj, 1 TSRMLS_CC); - rsrc->ptr = NULL; - } -} - -ZEND_RSRC_DTOR_FUNC(php_memc_sess_dtor) +void php_memc_destroy(memcached_st *memc, php_memc_user_data_t *memc_user_data) { - if (rsrc->ptr) { - memcached_sess *memc_sess = (memcached_sess *)rsrc->ptr; - memcached_free(memc_sess->memc_sess); - pefree(rsrc->ptr, 1); - rsrc->ptr = NULL; +#if HAVE_MEMCACHED_SASL + if (memc_user_data->has_sasl_data) { + memcached_destroy_sasl_auth_data(memc); } -} -/* }}} */ - -/* {{{ internal API functions */ -static memcached_return php_memc_do_serverlist_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) -{ - struct callbackContext* context = (struct callbackContext*) in_context; - zval *array; - - MAKE_STD_ZVAL(array); - array_init(array); - add_assoc_string(array, "host", (char*) memcached_server_name(instance), 1); - add_assoc_long(array, "port", memcached_server_port(instance)); - /* - * API does not allow to get at this field. - add_assoc_long(array, "weight", instance->weight); - */ - - add_next_index_zval(context->return_value, array); - return MEMCACHED_SUCCESS; -} - -static memcached_return php_memc_do_stats_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) -{ - char *hostport = NULL; - int hostport_len; - struct callbackContext* context = (struct callbackContext*) in_context; - zval *entry; - hostport_len = spprintf(&hostport, 0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance)); - - MAKE_STD_ZVAL(entry); - array_init(entry); - - add_assoc_long(entry, "pid", context->stats[context->i].pid); - add_assoc_long(entry, "uptime", context->stats[context->i].uptime); - add_assoc_long(entry, "threads", context->stats[context->i].threads); - add_assoc_long(entry, "time", context->stats[context->i].time); - add_assoc_long(entry, "pointer_size", context->stats[context->i].pointer_size); - add_assoc_long(entry, "rusage_user_seconds", context->stats[context->i].rusage_user_seconds); - add_assoc_long(entry, "rusage_user_microseconds", context->stats[context->i].rusage_user_microseconds); - add_assoc_long(entry, "rusage_system_seconds", context->stats[context->i].rusage_system_seconds); - add_assoc_long(entry, "rusage_system_microseconds", context->stats[context->i].rusage_system_microseconds); - add_assoc_long(entry, "curr_items", context->stats[context->i].curr_items); - add_assoc_long(entry, "total_items", context->stats[context->i].total_items); - add_assoc_long(entry, "limit_maxbytes", context->stats[context->i].limit_maxbytes); - add_assoc_long(entry, "curr_connections", context->stats[context->i].curr_connections); - add_assoc_long(entry, "total_connections", context->stats[context->i].total_connections); - add_assoc_long(entry, "connection_structures", context->stats[context->i].connection_structures); - add_assoc_long(entry, "bytes", context->stats[context->i].bytes); - add_assoc_long(entry, "cmd_get", context->stats[context->i].cmd_get); - add_assoc_long(entry, "cmd_set", context->stats[context->i].cmd_set); - add_assoc_long(entry, "get_hits", context->stats[context->i].get_hits); - add_assoc_long(entry, "get_misses", context->stats[context->i].get_misses); - add_assoc_long(entry, "evictions", context->stats[context->i].evictions); - add_assoc_long(entry, "bytes_read", context->stats[context->i].bytes_read); - add_assoc_long(entry, "bytes_written", context->stats[context->i].bytes_written); - add_assoc_stringl(entry, "version", context->stats[context->i].version, strlen(context->stats[context->i].version), 1); - - add_assoc_zval_ex(context->return_value, hostport, hostport_len+1, entry); - efree(hostport); - - /* Increment the server count in our context structure. Failure to do so will cause only the stats for the last server to get displayed. */ - context->i++; - return MEMCACHED_SUCCESS; -} - -static memcached_return php_memc_do_version_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) -{ - char *hostport = NULL; - char version[16]; - int hostport_len, version_len; - struct callbackContext* context = (struct callbackContext*) in_context; - - hostport_len = spprintf(&hostport, 0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance)); -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000009 - version_len = snprintf(version, sizeof(version), "%d.%d.%d", - memcached_server_major_version(instance), - memcached_server_minor_version(instance), - memcached_server_micro_version(instance)); -#else - version_len = snprintf(version, sizeof(version), "%d.%d.%d", - instance->major_version, - instance->minor_version, - instance->micro_version); -#endif - - add_assoc_stringl_ex(context->return_value, hostport, hostport_len+1, version, version_len, 1); - efree(hostport); - return MEMCACHED_SUCCESS; -} - -static int php_memc_handle_error(php_memc_t *i_obj, memcached_return status TSRMLS_DC) -{ - int result = 0; - - switch (status) { - case MEMCACHED_SUCCESS: - case MEMCACHED_STORED: - case MEMCACHED_DELETED: - case MEMCACHED_STAT: - result = 0; - i_obj->memc_errno = 0; - break; - - case MEMCACHED_END: - case MEMCACHED_BUFFERED: - i_obj->rescode = status; - i_obj->memc_errno = 0; - result = 0; - break; - - case MEMCACHED_SOME_ERRORS: - i_obj->rescode = status; -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 - i_obj->memc_errno = memcached_last_error_errno(i_obj->obj->memc); -#else - i_obj->memc_errno = i_obj->obj->memc->cached_errno; /* Hnngghgh! */ - -#endif - result = 0; - break; - - default: - i_obj->rescode = status; -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 - i_obj->memc_errno = memcached_last_error_errno(i_obj->obj->memc); -#else - i_obj->memc_errno = i_obj->obj->memc->cached_errno; /* Hnngghgh! */ - #endif - result = -1; - break; - } - return result; + memcached_free(memc); + pefree(memc_user_data, memc_user_data->is_persistent); } static -char *s_compress_value (enum memcached_compression_type compression_type, const char *payload, size_t *payload_len, uint32_t *flags TSRMLS_DC) -{ - /* status */ - zend_bool compress_status = 0; - - /* Additional 5% for the data */ - size_t buffer_size = (size_t) (((double) *payload_len * 1.05) + 1.0); - char *buffer = emalloc(sizeof(uint32_t) + buffer_size); - - /* Store compressed size here */ - size_t compressed_size = 0; - uint32_t plen = *payload_len; - - /* Copy the uin32_t at the beginning */ - memcpy(buffer, &plen, sizeof(uint32_t)); - buffer += sizeof(uint32_t); - - switch (compression_type) { - - case COMPRESSION_TYPE_FASTLZ: - compress_status = ((compressed_size = fastlz_compress(payload, *payload_len, buffer)) > 0); - MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_FASTLZ); - break; - - case COMPRESSION_TYPE_ZLIB: - /* ZLIB returns the compressed size in this buffer */ - compressed_size = buffer_size; +void php_memc_object_free_storage(zend_object *object) +{ + php_memc_object_t *intern = php_memc_fetch_object(object); - compress_status = (compress((Bytef *)buffer, &compressed_size, (Bytef *)payload, *payload_len) == Z_OK); - MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_ZLIB); - break; + if (intern->memc) { + php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc); - default: - compress_status = 0; - break; + if (!memc_user_data->is_persistent) { + php_memc_destroy(intern->memc, memc_user_data); + } } - buffer -= sizeof(uint32_t); - *payload_len = compressed_size + sizeof(uint32_t); - if (!compress_status) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not compress value"); - MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); + intern->memc = NULL; + zend_object_std_dtor(&intern->zo); +} - efree (buffer); - *payload_len = 0; - return NULL; - } +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)); - else if (*payload_len > (compressed_size * MEMC_G(compression_factor))) { - MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); - efree (buffer); - *payload_len = 0; - return NULL; - } - return buffer; + 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 -zend_bool s_serialize_value (enum memcached_serializer serializer, zval *value, smart_str *buf, uint32_t *flags TSRMLS_DC) +void php_memc_server_free_storage(zend_object *object) { - switch (serializer) { + php_memc_server_t *intern = php_memc_server_fetch_object(object); + zend_object_std_dtor(&intern->zo); +} - /* - Igbinary serialization - */ -#ifdef HAVE_MEMCACHED_IGBINARY - case SERIALIZER_IGBINARY: - if (igbinary_serialize((uint8_t **) &buf->c, &buf->len, value TSRMLS_CC) != 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not serialize value with igbinary"); - return 0; - } - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_IGBINARY); - break; -#endif +zend_object *php_memc_server_new(zend_class_entry *ce) +{ + php_memc_server_t *intern; - /* - JSON serialization - */ -#ifdef HAVE_JSON_API - case SERIALIZER_JSON: - case SERIALIZER_JSON_ARRAY: - { -#if HAVE_JSON_API_5_2 - php_json_encode(buf, value TSRMLS_CC); -#elif HAVE_JSON_API_5_3 - php_json_encode(buf, value, 0 TSRMLS_CC); /* options */ -#endif - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_JSON); - } - break; -#endif + intern = ecalloc(1, sizeof(php_memc_server_t) + zend_object_properties_size(ce)); - /* - msgpack serialization - */ -#ifdef HAVE_MEMCACHED_MSGPACK - case SERIALIZER_MSGPACK: - php_msgpack_serialize(buf, value TSRMLS_CC); - if (!buf->c) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not serialize value with msgpack"); - return 0; - } - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_MSGPACK); - break; -#endif + zend_object_std_init(&intern->zo, ce); + object_properties_init(&intern->zo, ce); - /* - PHP serialization - */ - default: - { - php_serialize_data_t var_hash; - PHP_VAR_SERIALIZE_INIT(var_hash); - php_var_serialize(buf, &value, &var_hash TSRMLS_CC); - PHP_VAR_SERIALIZE_DESTROY(var_hash); + intern->zo.handlers = &memcached_server_object_handlers; - if (!buf->c) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not serialize value"); - return 0; - } - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_SERIALIZED); - } - break; - } + return &intern->zo; +} +#endif - /* Check for exceptions caused by serializers */ - if (EG(exception) && buf->len) { - return 0; +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; } - return 1; } +/* }}} */ + +/* {{{ internal API functions */ static -char *php_memc_zval_to_payload(zval *value, size_t *payload_len, uint32_t *flags, enum memcached_serializer serializer, enum memcached_compression_type compression_type TSRMLS_DC) +memcached_return s_server_cursor_list_servers_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) { - const char *pl; - size_t pl_len = 0; - - char *payload = NULL; - smart_str buf = {0}; - char tmp[40] = {0}; + zval array; + zval *return_value = (zval *) in_context; - switch (Z_TYPE_P(value)) { - - case IS_STRING: - pl = Z_STRVAL_P(value); - pl_len = Z_STRLEN_P(value); - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_STRING); - break; - - case IS_LONG: - pl_len = sprintf(tmp, "%ld", Z_LVAL_P(value)); - pl = tmp; - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_LONG); - break; + 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); + */ - case IS_DOUBLE: - php_memcached_g_fmt(tmp, Z_DVAL_P(value)); - pl = tmp; - pl_len = strlen(tmp); - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_DOUBLE); - break; + add_next_index_zval(return_value, &array); + return MEMCACHED_SUCCESS; +} - case IS_BOOL: - if (Z_BVAL_P(value)) { - pl_len = 1; - tmp[0] = '1'; - tmp[1] = '\0'; - } else { - pl_len = 0; - tmp[0] = '\0'; - } - pl = tmp; - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL); - break; +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; - default: - if (!s_serialize_value (serializer, value, &buf, flags TSRMLS_CC)) { - smart_str_free (&buf); - return NULL; - } - pl = buf.c; - pl_len = buf.len; - break; - } +#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 - /* turn off compression for values below the threshold */ - if (MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED) && pl_len < MEMC_G(compression_threshold)) { - MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); - } + address = strpprintf(0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance) - 1); - /* If we have compression flag, compress the value */ - if (MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED)) { - /* status */ - *payload_len = pl_len; - payload = s_compress_value (compression_type, pl, payload_len, flags TSRMLS_CC); - } + ZVAL_STR(&rv, version); + zend_hash_add(Z_ARRVAL_P(return_value), address, &rv); - /* If compression failed or value is below threshold we just use plain value */ - if (!payload || !MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED)) { - *payload_len = (uint32_t) pl_len; - payload = estrndup(pl, pl_len); - } + zend_string_release(address); - if (buf.len) { - smart_str_free(&buf); - } - return payload; + return MEMCACHED_SUCCESS; } + static -char *s_decompress_value (const char *payload, size_t *payload_len, uint32_t flags TSRMLS_DC) +zend_string *s_decompress_value (const char *payload, size_t payload_len, uint32_t flags) { - char *buffer = NULL; - uint32_t len; + zend_string *buffer; + + uint32_t stored_length; unsigned long length; zend_bool decompress_status = 0; + zend_bool is_fastlz = 0, is_zlib = 0; - /* Stored with newer memcached extension? */ - if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ) || MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB)) { - /* This is copied from Ilia's patch */ - memcpy(&len, payload, sizeof(uint32_t)); - buffer = emalloc(len + 1); - *payload_len -= sizeof(uint32_t); - payload += sizeof(uint32_t); - length = len; - - if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ)) { - decompress_status = ((length = fastlz_decompress(payload, *payload_len, buffer, len)) > 0); - } else if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB)) { - decompress_status = (uncompress((Bytef *)buffer, &length, (Bytef *)payload, *payload_len) == Z_OK); - } + if (payload_len < sizeof (uint32_t)) { + return NULL; } - /* Fall back to 'old style decompression' */ - if (!decompress_status) { - unsigned int factor = 1, maxfactor = 16; - int status; - - do { - length = (unsigned long)*payload_len * (1 << factor++); - buffer = erealloc(buffer, length + 1); - memset(buffer, 0, length + 1); - status = uncompress((Bytef *)buffer, (uLongf *)&length, (const Bytef *)payload, *payload_len); - } while ((status==Z_BUF_ERROR) && (factor < maxfactor)); - - if (status == Z_OK) { - decompress_status = 1; - } + 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 encryption 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 TSRMLS_CC, E_WARNING, "could not decompress value"); - efree(buffer); + php_error_docref(NULL, E_WARNING, "could not decompress value"); + zend_string_release (buffer); return NULL; } - buffer [length] = '\0'; - *payload_len = length; + + zend_string_forget_hash_val(buffer); return buffer; } static -zend_bool s_unserialize_value (enum memcached_serializer serializer, int val_type, zval *value, const char *payload, size_t payload_len TSRMLS_DC) +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: { - const char *payload_tmp = payload; 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(&value, (const unsigned char **)&payload_tmp, (const unsigned char *)payload_tmp + payload_len, &var_hash TSRMLS_CC)) { - ZVAL_FALSE(value); + 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 TSRMLS_CC, E_WARNING, "could not unserialize value"); + php_error_docref(NULL, E_WARNING, "could not unserialize value"); return 0; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); @@ -3365,38 +3470,37 @@ zend_bool s_unserialize_value (enum memcached_serializer serializer, int val_typ case MEMC_VAL_IS_IGBINARY: #ifdef HAVE_MEMCACHED_IGBINARY - if (igbinary_unserialize((uint8_t *)payload, payload_len, &value TSRMLS_CC)) { - ZVAL_FALSE(value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value with 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(value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no igbinary support"); + 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 -# if HAVE_JSON_API_5_2 - php_json_decode(value, payload, payload_len, (serializer == SERIALIZER_JSON_ARRAY) TSRMLS_CC); -# elif HAVE_JSON_API_5_3 - php_json_decode(value, payload, payload_len, (serializer == SERIALIZER_JSON_ARRAY), JSON_PARSER_DEFAULT_DEPTH TSRMLS_CC); -# endif + { + 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(value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no json support"); + 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(value, payload, payload_len TSRMLS_CC); + php_msgpack_unserialize(return_value, ZSTR_VAL(payload), ZSTR_LEN(payload)); #else - ZVAL_FALSE(value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no msgpack support"); + ZVAL_FALSE(return_value); + php_error_docref(NULL, E_WARNING, "could not unserialize value, no msgpack support"); return 0; #endif break; @@ -3404,122 +3508,81 @@ zend_bool s_unserialize_value (enum memcached_serializer serializer, int val_typ return 1; } -/* The caller MUST free the payload */ -static int php_memc_zval_from_payload(zval *value, const char *payload_in, size_t payload_len, uint32_t flags, enum memcached_serializer serializer TSRMLS_DC) +static +zend_bool s_memcached_result_to_zval(memcached_st *memc, memcached_result_st *result, zval *return_value) { - /* - A NULL payload is completely valid if length is 0, it is simply empty. - */ - int retval = 0; - zend_bool payload_emalloc = 0; - char *pl = NULL; - - if (payload_in == NULL && payload_len > 0) { - ZVAL_FALSE(value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Could not handle non-existing value of length %zu", payload_len); - return -1; - } else if (payload_in == NULL) { - if (MEMC_VAL_GET_TYPE(flags) == MEMC_VAL_IS_BOOL) { - ZVAL_FALSE(value); - } else { - ZVAL_EMPTY_STRING(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)) { - char *datas = s_decompress_value (payload_in, &payload_len, flags TSRMLS_CC); - if (!datas) { - ZVAL_FALSE(value); - return -1; + data = s_decompress_value (payload, payload_len, flags); + if (!data) { + return 0; } - pl = datas; - payload_emalloc = 1; } else { - pl = (char *) payload_in; + data = zend_string_init(payload, payload_len, 0); } switch (MEMC_VAL_GET_TYPE(flags)) { + case MEMC_VAL_IS_STRING: - if (payload_emalloc) { - ZVAL_STRINGL(value, pl, payload_len, 0); - payload_emalloc = 0; - } else { - ZVAL_STRINGL(value, pl, payload_len, 1); - } + ZVAL_STR_COPY(return_value, data); break; case MEMC_VAL_IS_LONG: - { - long lval; - char conv_buf [128]; - - if (payload_len >= 128) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not read long value, too big"); - retval = -1; - } - else { - memcpy (conv_buf, pl, payload_len); - conv_buf [payload_len] = '\0'; - - lval = strtol(conv_buf, NULL, 10); - ZVAL_LONG(value, lval); - } - } + ZVAL_LONG(return_value, strtol(ZSTR_VAL(data), NULL, 10)); break; case MEMC_VAL_IS_DOUBLE: { - char conv_buf [128]; - - if (payload_len >= 128) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not read double value, too big"); - retval = -1; + 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 { - memcpy (conv_buf, pl, payload_len); - conv_buf [payload_len] = '\0'; - - if (payload_len == 8 && memcmp(conv_buf, "Infinity", 8) == 0) { - ZVAL_DOUBLE(value, php_get_inf()); - } else if (payload_len == 9 && memcmp(conv_buf, "-Infinity", 9) == 0) { - ZVAL_DOUBLE(value, -php_get_inf()); - } else if (payload_len == 3 && memcmp(conv_buf, "NaN", 3) == 0) { - ZVAL_DOUBLE(value, php_get_nan()); - } else { - ZVAL_DOUBLE(value, zend_strtod(conv_buf, NULL)); - } + ZVAL_DOUBLE(return_value, zend_strtod(ZSTR_VAL(data), NULL)); } } break; case MEMC_VAL_IS_BOOL: - ZVAL_BOOL(value, payload_len > 0 && pl[0] == '1'); + 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: - if (!s_unserialize_value (serializer, MEMC_VAL_GET_TYPE(flags), value, pl, payload_len TSRMLS_CC)) { - retval = -1; - } + retval = s_unserialize_value (memc, MEMC_VAL_GET_TYPE(flags), data, return_value); break; default: - ZVAL_FALSE(value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "unknown payload type"); - retval = -1; + php_error_docref(NULL, E_WARNING, "unknown payload type"); break; } + zend_string_release(data); - if (payload_emalloc) { - efree(pl); - } return retval; } + PHP_MEMCACHED_API zend_class_entry *php_memc_get_ce(void) { @@ -3533,175 +3596,29 @@ zend_class_entry *php_memc_get_exception(void) } PHP_MEMCACHED_API -zend_class_entry *php_memc_get_exception_base(int root TSRMLS_DC) +zend_class_entry *php_memc_get_exception_base(int root) { #if HAVE_SPL if (!root) { if (!spl_ce_RuntimeException) { - zend_class_entry **pce; - - if (zend_hash_find(CG(class_table), "runtimeexception", - sizeof("RuntimeException"), (void **) &pce) == SUCCESS) { - spl_ce_RuntimeException = *pce; - return *pce; + 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 -#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) return zend_exception_get_default(); -#else - return zend_exception_get_default(TSRMLS_C); -#endif -} - -static memcached_return php_memc_do_cache_callback(zval *zmemc_obj, zend_fcall_info *fci, - zend_fcall_info_cache *fcc, char *key, - size_t key_len, zval *value TSRMLS_DC) -{ - char *payload = NULL; - size_t payload_len = 0; - zval **params[4]; - zval *retval; - zval *z_key; - zval *z_expiration; - - uint32_t flags = 0; - memcached_return rc; - php_memc_t* i_obj; - memcached_return status = MEMCACHED_SUCCESS; - int result; - - MAKE_STD_ZVAL(z_key); - MAKE_STD_ZVAL(z_expiration); - ZVAL_STRINGL(z_key, key, key_len, 1); - ZVAL_NULL(value); - ZVAL_LONG(z_expiration, 0); - - params[0] = &zmemc_obj; - params[1] = &z_key; - params[2] = &value; - params[3] = &z_expiration; - - fci->retval_ptr_ptr = &retval; - fci->params = params; - fci->param_count = sizeof(params) / sizeof(params[0]); - - result = zend_call_function(fci, fcc TSRMLS_CC); - if (result == SUCCESS && retval) { - i_obj = (php_memc_t *) zend_object_store_get_object(zmemc_obj TSRMLS_CC); - struct memc_obj *m_obj = i_obj->obj; - - if (zend_is_true(retval)) { - time_t expiration; - - if (Z_TYPE_P(z_expiration) != IS_LONG) { - convert_to_long(z_expiration); - } - - expiration = Z_LVAL_P(z_expiration); - - payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); - if (payload == NULL) { - status = (memcached_return)MEMC_RES_PAYLOAD_FAILURE; - } else { - rc = memcached_set(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) { - status = rc; - } - efree(payload); - } - } else { - status = MEMCACHED_NOTFOUND; - zval_dtor(value); - ZVAL_NULL(value); - } - - } else { - if (result == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not invoke cache callback"); - } - status = MEMCACHED_FAILURE; - zval_dtor(value); - ZVAL_NULL(value); - } - - if (retval) { - zval_ptr_dtor(&retval); - } - - zval_ptr_dtor(&z_key); - zval_ptr_dtor(&z_expiration); - - return status; } -static int php_memc_do_result_callback(zval *zmemc_obj, zend_fcall_info *fci, - zend_fcall_info_cache *fcc, - memcached_result_st *result TSRMLS_DC) -{ - const char *res_key = NULL; - size_t res_key_len = 0; - const char *payload = NULL; - size_t payload_len = 0; - zval *value, *retval = NULL; - uint64_t cas = 0; - zval **params[2]; - zval *z_result; - uint32_t flags = 0; - int rc = 0; - php_memc_t *i_obj = NULL; - - params[0] = &zmemc_obj; - params[1] = &z_result; - - fci->retval_ptr_ptr = &retval; - fci->params = params; - fci->param_count = 2; - - payload = memcached_result_value(result); - payload_len = memcached_result_length(result); - flags = memcached_result_flags(result); - res_key = memcached_result_key_value(result); - res_key_len = memcached_result_key_length(result); - cas = memcached_result_cas(result); - - ALLOC_INIT_ZVAL(value); - - i_obj = (php_memc_t *) zend_object_store_get_object(zmemc_obj TSRMLS_CC); - - if (php_memc_zval_from_payload(value, payload, payload_len, flags, i_obj->obj->serializer TSRMLS_CC) < 0) { - zval_ptr_dtor(&value); - i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; - return -1; - } - - MAKE_STD_ZVAL(z_result); - array_init(z_result); - add_assoc_stringl_ex(z_result, ZEND_STRS("key"), res_key, res_key_len, 1); - add_assoc_zval_ex(z_result, ZEND_STRS("value"), value); - if (cas != 0) { - add_assoc_double_ex(z_result, ZEND_STRS("cas"), (double)cas); - } - if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { - add_assoc_long_ex(z_result, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); - } - - if (zend_call_function(fci, fcc TSRMLS_CC) == FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not invoke result callback"); - rc = -1; - } - - if (retval) { - zval_ptr_dtor(&retval); - } - zval_ptr_dtor(&z_result); - - return rc; -} -/* }}} */ #ifdef HAVE_MEMCACHED_PROTOCOL @@ -3710,8 +3627,8 @@ void s_destroy_cb (zend_fcall_info *fci) { if (fci->size > 0) { zval_ptr_dtor(&fci->function_name); - if (fci->object_ptr != NULL) { - zval_ptr_dtor(&fci->object_ptr); + if (fci->object) { + OBJ_RELEASE(fci->object); } } } @@ -3721,20 +3638,19 @@ PHP_METHOD(MemcachedServer, run) { int i; zend_bool rc; - char *address; - int address_len; + zend_string *address; php_memc_server_t *intern; - intern = (php_memc_server_t *) zend_object_store_get_object(getThis() TSRMLS_CC); + intern = Z_MEMC_SERVER_P(getThis()); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &address, &address_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &address) == FAILURE) { return; } - rc = php_memc_proto_handler_run (intern->handler, address); + 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_G(server.callbacks) [i].fci); + s_destroy_cb(&MEMC_SERVER_G(callbacks) [i].fci); } RETURN_BOOL(rc); @@ -3748,7 +3664,7 @@ PHP_METHOD(MemcachedServer, on) zend_fcall_info_cache fci_cache; zend_bool rc = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lf!", &event, &fci, &fci_cache) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lf!", &event, &fci, &fci_cache) == FAILURE) { return; } @@ -3757,14 +3673,14 @@ PHP_METHOD(MemcachedServer, on) } if (fci.size > 0) { - s_destroy_cb (&MEMC_G(server.callbacks) [event].fci); + s_destroy_cb (&MEMC_SERVER_G(callbacks) [event].fci); - MEMC_G(server.callbacks) [event].fci = fci; - MEMC_G(server.callbacks) [event].fci_cache = fci_cache; + MEMC_SERVER_G(callbacks) [event].fci = fci; + MEMC_SERVER_G(callbacks) [event].fci_cache = fci_cache; - Z_ADDREF_P (fci.function_name); - if (fci.object_ptr) { - Z_ADDREF_P (fci.object_ptr); + Z_TRY_ADDREF(fci.function_name); + if (fci.object) { + GC_REFCOUNT(fci.object)++; } } RETURN_BOOL(rc); @@ -3787,31 +3703,25 @@ 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(2, cas_token) - ZEND_ARG_INFO(1, udf_flags) + 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(2, cas_token) - ZEND_ARG_INFO(1, udf_flags) + 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(2, cas_tokens) - ZEND_ARG_INFO(0, flags) - ZEND_ARG_INFO(1, udf_flags) + 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(2, cas_tokens) - ZEND_ARG_INFO(0, flags) - ZEND_ARG_INFO(1, udf_flags) + ZEND_ARG_INFO(0, get_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayed, 0, 0, 1) @@ -3837,7 +3747,6 @@ 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_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3) @@ -3845,7 +3754,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_touch, 0, 0, 2) @@ -3862,21 +3770,18 @@ 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_ARG_INFO(0, udf_flags) 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_ARG_INFO(0, udf_flags) 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_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_addByKey, 0, 0, 3) @@ -3884,14 +3789,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_addByKey, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) 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_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_replaceByKey, 0, 0, 3) @@ -3899,7 +3802,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_replaceByKey, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_append, 0, 0, 2) @@ -3933,7 +3835,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_cas, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4) @@ -3942,7 +3843,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_delete, 0, 0, 1) @@ -4198,39 +4098,45 @@ static PHP_GINIT_FUNCTION(php_memcached) { #ifdef HAVE_MEMCACHED_SESSION - php_memcached_globals->sess_locking_enabled = 1; - php_memcached_globals->sess_binary_enabled = 1; - php_memcached_globals->sess_consistent_hash_enabled = 0; - php_memcached_globals->sess_number_of_replicas = 0; - php_memcached_globals->sess_remove_failed_enabled = 0; - php_memcached_globals->sess_prefix = NULL; - php_memcached_globals->sess_lock_wait = 0; - php_memcached_globals->sess_lock_max_wait = 0; - php_memcached_globals->sess_lock_expire = 0; - php_memcached_globals->sess_locked = 0; - php_memcached_globals->sess_lock_key = NULL; - php_memcached_globals->sess_lock_key_len = 0; - php_memcached_globals->sess_randomize_replica_read = 0; - php_memcached_globals->sess_connect_timeout = 1000; -#if HAVE_MEMCACHED_SASL - php_memcached_globals->sess_sasl_username = NULL; - php_memcached_globals->sess_sasl_password = NULL; - php_memcached_globals->sess_sasl_data = 0; -#endif -#endif - php_memcached_globals->serializer_name = NULL; - php_memcached_globals->serializer = SERIALIZER_DEFAULT; - php_memcached_globals->compression_type = NULL; - php_memcached_globals->compression_type_real = COMPRESSION_TYPE_FASTLZ; - php_memcached_globals->compression_factor = 1.30; -#if HAVE_MEMCACHED_SASL - php_memcached_globals->use_sasl = 0; + + php_memcached_globals->session.lock_enabled = 0; + php_memcached_globals->session.lock_wait_max = 2000; + php_memcached_globals->session.lock_wait_min = 1000; + php_memcached_globals->session.lock_retries = 5; + php_memcached_globals->session.lock_expiration = 30; + php_memcached_globals->session.binary_protocol_enabled = 1; + php_memcached_globals->session.consistent_hash_enabled = 1; + php_memcached_globals->session.number_of_replicas = 0; + php_memcached_globals->session.server_failure_limit = 1; + php_memcached_globals->session.randomize_replica_read_enabled = 1; + php_memcached_globals->session.remove_failed_servers_enabled = 1; + php_memcached_globals->session.connect_timeout = 1000; + php_memcached_globals->session.prefix = NULL; + php_memcached_globals->session.persistent_enabled = 0; + php_memcached_globals->session.sasl_username = NULL; + php_memcached_globals->session.sasl_password = NULL; + #endif - php_memcached_globals->store_retry_count = 2; + php_memcached_globals->memc.serializer_name = NULL; + php_memcached_globals->memc.serializer_type = SERIALIZER_DEFAULT; + php_memcached_globals->memc.compression_name = NULL; + php_memcached_globals->memc.compression_threshold = 2000; + php_memcached_globals->memc.compression_type = COMPRESSION_TYPE_FASTLZ; + php_memcached_globals->memc.compression_factor = 1.30; + php_memcached_globals->memc.store_retry_count = 2; + + php_memcached_globals->memc.sasl_initialised = 0; + php_memcached_globals->no_effect = 0; + + /* Defaults for certain options */ + php_memcached_globals->memc.default_behavior.consistent_hash_enabled = 0; + php_memcached_globals->memc.default_behavior.binary_protocol_enabled = 0; + php_memcached_globals->memc.default_behavior.connect_timeout = 0; } zend_module_entry memcached_module_entry = { - STANDARD_MODULE_HEADER, + STANDARD_MODULE_HEADER_EX, NULL, + memcached_deps, "memcached", NULL, PHP_MINIT(memcached), @@ -4247,12 +4153,13 @@ zend_module_entry memcached_module_entry = { }; /* }}} */ + /* {{{ php_memc_register_constants */ static void php_memc_register_constants(INIT_FUNC_ARGS) { - #define REGISTER_MEMC_CLASS_CONST_LONG(name, value) zend_declare_class_constant_long(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value TSRMLS_CC) - #define REGISTER_MEMC_CLASS_CONST_BOOL(name, value) zend_declare_class_constant_bool(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value TSRMLS_CC) - #define REGISTER_MEMC_CLASS_CONST_NULL(name) zend_declare_class_constant_null(php_memc_get_ce() , ZEND_STRS( #name ) - 1 TSRMLS_CC) + #define REGISTER_MEMC_CLASS_CONST_LONG(name, value) zend_declare_class_constant_long(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value) + #define REGISTER_MEMC_CLASS_CONST_BOOL(name, value) zend_declare_class_constant_bool(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value) + #define REGISTER_MEMC_CLASS_CONST_NULL(name) zend_declare_class_constant_null(php_memc_get_ce() , ZEND_STRS( #name ) - 1) /* * Class options @@ -4263,6 +4170,8 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION_TYPE, MEMC_OPT_COMPRESSION_TYPE); REGISTER_MEMC_CLASS_CONST_LONG(OPT_PREFIX_KEY, MEMC_OPT_PREFIX_KEY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERIALIZER, MEMC_OPT_SERIALIZER); + + REGISTER_MEMC_CLASS_CONST_LONG(OPT_USER_FLAGS, MEMC_OPT_USER_FLAGS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_STORE_RETRY_COUNT, MEMC_OPT_STORE_RETRY_COUNT); /* @@ -4395,7 +4304,6 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_LONG(RES_MEMORY_ALLOCATION_FAILURE, MEMCACHED_MEMORY_ALLOCATION_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_SOCKET_CREATE_FAILURE, MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE); - REGISTER_MEMC_CLASS_CONST_LONG(RES_BAD_KEY_PROVIDED, MEMCACHED_BAD_KEY_PROVIDED); REGISTER_MEMC_CLASS_CONST_LONG(RES_E2BIG, MEMCACHED_E2BIG); REGISTER_MEMC_CLASS_CONST_LONG(RES_KEY_TOO_BIG, MEMCACHED_KEY_TOO_BIG); REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_TEMPORARILY_DISABLED, MEMCACHED_SERVER_TEMPORARILY_DISABLED); @@ -4419,11 +4327,11 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) /* * Serializer types. */ - REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_PHP, SERIALIZER_PHP); - REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_IGBINARY, SERIALIZER_IGBINARY); - REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_JSON, SERIALIZER_JSON); + REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_PHP, SERIALIZER_PHP); + REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_IGBINARY, SERIALIZER_IGBINARY); + REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_JSON, SERIALIZER_JSON); REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_JSON_ARRAY, SERIALIZER_JSON_ARRAY); - REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_MSGPACK, SERIALIZER_MSGPACK); + REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_MSGPACK, SERIALIZER_MSGPACK); /* * Compression types @@ -4435,6 +4343,7 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) * Flags. */ REGISTER_MEMC_CLASS_CONST_LONG(GET_PRESERVE_ORDER, MEMC_GET_PRESERVE_ORDER); + REGISTER_MEMC_CLASS_CONST_LONG(GET_EXTENDED, MEMC_GET_EXTENDED); #ifdef HAVE_MEMCACHED_PROTOCOL /* @@ -4483,55 +4392,44 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) } /* }}} */ -int php_memc_sess_list_entry(void) -{ - return le_memc_sess; -} - /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(memcached) { zend_class_entry ce; memcpy(&memcached_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + memcached_object_handlers.offset = XtOffsetOf(php_memc_object_t, zo); memcached_object_handlers.clone_obj = NULL; + memcached_object_handlers.free_obj = php_memc_object_free_storage; le_memc = zend_register_list_destructors_ex(NULL, php_memc_dtor, "Memcached persistent connection", module_number); - le_memc_sess = zend_register_list_destructors_ex(NULL, php_memc_sess_dtor, "Memcached Sessions persistent connection", module_number); INIT_CLASS_ENTRY(ce, "Memcached", memcached_class_methods); - memcached_ce = zend_register_internal_class(&ce TSRMLS_CC); - memcached_ce->create_object = php_memc_new; + memcached_ce = zend_register_internal_class(&ce); + memcached_ce->create_object = php_memc_object_new; #ifdef HAVE_MEMCACHED_PROTOCOL memcpy(&memcached_server_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + memcached_server_object_handlers.offset = XtOffsetOf(php_memc_server_t, zo); memcached_server_object_handlers.clone_obj = NULL; + memcached_server_object_handlers.free_obj = php_memc_server_free_storage; INIT_CLASS_ENTRY(ce, "MemcachedServer", memcached_server_class_methods); - memcached_server_ce = zend_register_internal_class(&ce TSRMLS_CC); + memcached_server_ce = zend_register_internal_class(&ce); memcached_server_ce->create_object = php_memc_server_new; #endif INIT_CLASS_ENTRY(ce, "MemcachedException", NULL); - memcached_exception_ce = zend_register_internal_class_ex(&ce, php_memc_get_exception_base(0 TSRMLS_CC), NULL TSRMLS_CC); + memcached_exception_ce = zend_register_internal_class_ex(&ce, php_memc_get_exception_base(0)); /* TODO * possibly declare custom exception property here */ php_memc_register_constants(INIT_FUNC_ARGS_PASSTHRU); + REGISTER_INI_ENTRIES(); #ifdef HAVE_MEMCACHED_SESSION - php_session_register_module(ps_memcached_ptr); -#endif - - REGISTER_INI_ENTRIES(); -#if HAVE_MEMCACHED_SASL - if (MEMC_G(use_sasl)) { - if (sasl_client_init(NULL) != SASL_OK) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize SASL library"); - return FAILURE; - } - } + php_memc_session_minit(module_number); #endif return SUCCESS; } @@ -4541,7 +4439,7 @@ PHP_MINIT_FUNCTION(memcached) PHP_MSHUTDOWN_FUNCTION(memcached) { #if HAVE_MEMCACHED_SASL - if (MEMC_G(use_sasl)) { + if (MEMC_G(sasl_initialised)) { sasl_done(); } #endif diff --git a/php_memcached.h b/php_memcached.h index bce8e37b..9e466cb4 100644 --- a/php_memcached.h +++ b/php_memcached.h @@ -20,13 +20,14 @@ #define PHP_MEMCACHED_H #include "php.h" +#include "Zend/zend_smart_str.h" #include "main/php_config.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif -#define PHP_MEMCACHED_VERSION "2.2.0" +#define PHP_MEMCACHED_VERSION "3.0.0b1" #if defined(PHP_WIN32) && defined(MEMCACHED_EXPORTS) #define PHP_MEMCACHED_API __declspec(dllexport) @@ -36,7 +37,7 @@ PHP_MEMCACHED_API zend_class_entry *php_memc_get_ce(void); PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception(void); -PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception_base(int root TSRMLS_DC); +PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception_base(int root); extern zend_module_entry memcached_module_entry; #define phpext_memcached_ptr &memcached_module_entry diff --git a/php_memcached_private.h b/php_memcached_private.h index 0ec2d76e..c06bd626 100644 --- a/php_memcached_private.h +++ b/php_memcached_private.h @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include @@ -75,13 +75,24 @@ typedef unsigned long int uint32_t; /**************************************** Structures and definitions ****************************************/ -enum memcached_serializer { - SERIALIZER_PHP = 1, - SERIALIZER_IGBINARY = 2, - SERIALIZER_JSON = 3, +typedef enum { + SERIALIZER_PHP = 1, + SERIALIZER_IGBINARY = 2, + SERIALIZER_JSON = 3, SERIALIZER_JSON_ARRAY = 4, - SERIALIZER_MSGPACK = 5, -}; + SERIALIZER_MSGPACK = 5 +} php_memc_serializer_type; + +typedef enum { + COMPRESSION_TYPE_ZLIB = 1, + COMPRESSION_TYPE_FASTLZ = 2 +} php_memc_compression_type; + +typedef struct { + const char *name; + php_memc_serializer_type type; +} php_memc_serializer; + #ifdef HAVE_MEMCACHED_IGBINARY #define SERIALIZER_DEFAULT SERIALIZER_IGBINARY #define SERIALIZER_DEFAULT_NAME "igbinary" @@ -129,46 +140,67 @@ typedef struct { #endif ZEND_BEGIN_MODULE_GLOBALS(php_memcached) + #ifdef HAVE_MEMCACHED_SESSION - zend_bool sess_locking_enabled; - long sess_lock_wait; - long sess_lock_max_wait; - long sess_lock_expire; - char* sess_prefix; - zend_bool sess_locked; - char* sess_lock_key; - int sess_lock_key_len; - - int sess_number_of_replicas; - zend_bool sess_randomize_replica_read; - zend_bool sess_remove_failed_enabled; - long sess_connect_timeout; - zend_bool sess_consistent_hash_enabled; - zend_bool sess_binary_enabled; - -#if HAVE_MEMCACHED_SASL - char *sess_sasl_username; - char *sess_sasl_password; - zend_bool sess_sasl_data; -#endif -#endif - char *serializer_name; - enum memcached_serializer serializer; + /* Session related variables */ + struct { + zend_bool lock_enabled; + zend_long lock_wait_max; + zend_long lock_wait_min; + zend_long lock_retries; + zend_long lock_expiration; + + zend_bool binary_protocol_enabled; + zend_bool consistent_hash_enabled; + + zend_long server_failure_limit; + zend_long number_of_replicas; + zend_bool randomize_replica_read_enabled; + zend_bool remove_failed_servers_enabled; - char *compression_type; - int compression_type_real; - int compression_threshold; + zend_long connect_timeout; - double compression_factor; -#if HAVE_MEMCACHED_SASL - zend_bool use_sasl; + char *prefix; + zend_bool persistent_enabled; + + char *sasl_username; + char *sasl_password; + } session; #endif + + struct { + char *serializer_name; + char *compression_name; + zend_long compression_threshold; + double compression_factor; + zend_long store_retry_count; + + /* Converted values*/ + php_memc_serializer_type serializer_type; + php_memc_compression_type compression_type; + + /* Whether we have initialised sasl for this process */ + zend_bool sasl_initialised; + + struct { + + zend_bool consistent_hash_enabled; + zend_bool binary_protocol_enabled; + zend_long connect_timeout; + + } default_behavior; + + } memc; + + /* For deprecated values */ + zend_long no_effect; + #ifdef HAVE_MEMCACHED_PROTOCOL struct { php_memc_server_cb_t callbacks [MEMC_SERVER_ON_MAX]; } server; #endif - long store_retry_count; + ZEND_END_MODULE_GLOBALS(php_memcached) PHP_RINIT_FUNCTION(memcached); @@ -177,20 +209,11 @@ PHP_MINIT_FUNCTION(memcached); PHP_MSHUTDOWN_FUNCTION(memcached); PHP_MINFO_FUNCTION(memcached); -#ifdef ZTS -#define MEMC_G(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, v) -#else -#define MEMC_G(v) (php_memcached_globals.v) -#endif - -typedef struct { - memcached_st *memc_sess; - zend_bool is_persistent; -} memcached_sess; +char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache); -int php_memc_sess_list_entry(void); +memcached_return php_memcached_exist (memcached_st *memc, zend_string *key); -char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TSRMLS_DC); +zend_bool php_memc_init_sasl_if_needed(); #endif /* PHP_MEMCACHED_PRIVATE_H */ diff --git a/php_memcached_server.c b/php_memcached_server.c index 42ccc8ee..94305101 100644 --- a/php_memcached_server.c +++ b/php_memcached_server.c @@ -29,19 +29,15 @@ #define MEMC_MAKE_ZVAL_COOKIE(my_zcookie, my_ptr) \ do { \ - char *cookie_buf; \ - spprintf (&cookie_buf, 0, "%p", my_ptr); \ - MAKE_STD_ZVAL(my_zcookie); \ - ZVAL_STRING(my_zcookie, cookie_buf, 0); \ + zend_string *cookie_buf; \ + cookie_buf = strpprintf(0, "%p", my_ptr); \ + ZVAL_STR(&my_zcookie, cookie_buf); \ } while (0) #define MEMC_MAKE_RESULT_CAS(my_zresult_cas, my_result_cas) \ do { \ my_result_cas = 0; \ - if (Z_TYPE_P(my_zresult_cas) != IS_NULL) { \ - convert_to_double (my_zresult_cas); \ - my_result_cas = (uint64_t) Z_DVAL_P(my_zresult_cas); \ - } \ + my_result_cas = zval_get_double(my_zresult_cas); \ } while (0) @@ -60,7 +56,7 @@ typedef struct { } php_memc_client_t; static -long s_invoke_php_callback (php_memc_server_cb_t *cb, zval ***params, ssize_t param_count TSRMLS_DC) +long s_invoke_php_callback (php_memc_server_cb_t *cb, zval ***params, ssize_t param_count) { long retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *retval_ptr = NULL; @@ -72,15 +68,13 @@ long s_invoke_php_callback (php_memc_server_cb_t *cb, zval ***params, ssize_t pa cb->fci.no_separation = 1; cb->fci.retval_ptr_ptr = &retval_ptr; - if (zend_call_function(&(cb->fci), &(cb->fci_cache) TSRMLS_CC) == FAILURE) { - char *buf = php_memc_printable_func (&(cb->fci), &(cb->fci_cache) TSRMLS_CC); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to invoke callback %s()", buf); + if (zend_call_function(&(cb->fci), &(cb->fci_cache)) == FAILURE) { + char *buf = php_memc_printable_func (&(cb->fci), &(cb->fci_cache)); + php_error_docref(NULL, E_WARNING, "Failed to invoke callback %s()", buf); efree (buf); } if (retval_ptr) { - convert_to_long (retval_ptr); - retval = Z_LVAL_P(retval_ptr); - zval_ptr_dtor(&retval_ptr); + retval = zval_get_long(retval_ptr); } return retval; } @@ -91,10 +85,8 @@ protocol_binary_response_status s_add_handler(const void *cookie, const void *ke uint32_t data_len, uint32_t flags, uint32_t exptime, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zkey, *zvalue, *zflags, *zexptime, *zresult_cas; - zval **params [6]; - - TSRMLS_FETCH(); + zval zcookie, zkey, zvalue, zflags, zexptime, zresult_cas; + zval params[6]; if (!MEMC_HAS_CB(MEMC_SERVER_ON_ADD)) { return retval; @@ -102,32 +94,29 @@ protocol_binary_response_status s_add_handler(const void *cookie, const void *ke MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zkey); - ZVAL_STRINGL(zkey, key, key_len, 1); - - MAKE_STD_ZVAL(zvalue); - ZVAL_STRINGL(zvalue, data, data_len, 1); - - MAKE_STD_ZVAL(zflags); - ZVAL_LONG(zflags, flags); - - MAKE_STD_ZVAL(zexptime); - ZVAL_LONG(zexptime, exptime); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_STRINGL(&zvalue, data, data_len); + ZVAL_LONG(&zflags, flags); + ZVAL_LONG(&zexptime, exptime); + ZVAL_NULL(&zresult_cas); - MAKE_STD_ZVAL(zresult_cas); - ZVAL_NULL(zresult_cas); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zvalue); + ZVAL_COPY(¶ms[3], &zflags); + ZVAL_COPY(¶ms[4], &zexptime); + ZVAL_COPY(¶ms[5], &zresult_cas); - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zvalue; - params [3] = &zflags; - params [4] = &zexptime; - params [5] = &zresult_cas; - - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_ADD), params, 6 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_ADD), params, 6); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[3]); + zval_ptr_dtor(¶ms[4]); + zval_ptr_dtor(¶ms[5]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zvalue); @@ -143,10 +132,8 @@ protocol_binary_response_status s_append_prepend_handler (php_memc_event_t event const void *data, uint32_t data_len, uint64_t cas, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zkey, *zvalue, *zcas, *zresult_cas; - zval **params [5]; - - TSRMLS_FETCH(); + zval zcookie, zkey, zvalue, zcas, zresult_cas; + zval params[5]; if (!MEMC_HAS_CB(event)) { return retval; @@ -154,28 +141,26 @@ protocol_binary_response_status s_append_prepend_handler (php_memc_event_t event MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zkey); - ZVAL_STRINGL(zkey, key, key_len, 1); - - MAKE_STD_ZVAL(zvalue); - ZVAL_STRINGL(zvalue, data, data_len, 1); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_STRINGL(&zvalue, data, data_len); + ZVAL_DOUBLE(&zcas, cas); + ZVAL_NULL(&zresult_cas); - MAKE_STD_ZVAL(zcas); - ZVAL_DOUBLE(zcas, cas); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zvalue); + ZVAL_COPY(¶ms[3], &zcas); + ZVAL_COPY(¶ms[4], &zresult_cas); - MAKE_STD_ZVAL(zresult_cas); - ZVAL_NULL(zresult_cas); - - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zvalue; - params [3] = &zcas; - params [4] = &zresult_cas; - - retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 5 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 5); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[3]); + zval_ptr_dtor(¶ms[4]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zvalue); @@ -206,10 +191,8 @@ protocol_binary_response_status s_incr_decr_handler (php_memc_event_t event, con uint64_t initial, uint32_t expiration, uint64_t *result, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zkey, *zdelta, *zinital, *zexpiration, *zresult, *zresult_cas; - zval **params [7]; - - TSRMLS_FETCH(); + zval zcookie, zkey, zdelta, zinital, zexpiration, zresult, zresult_cas; + zval params[7]; if (!MEMC_HAS_CB(event)) { return retval; @@ -217,41 +200,34 @@ protocol_binary_response_status s_incr_decr_handler (php_memc_event_t event, con MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zkey); - ZVAL_STRINGL(zkey, key, key_len, 1); - - MAKE_STD_ZVAL(zdelta); - ZVAL_LONG(zdelta, (long) delta); - - MAKE_STD_ZVAL(zinital); - ZVAL_LONG(zinital, (long) initial); - - MAKE_STD_ZVAL(zexpiration); - ZVAL_LONG(zexpiration, (long) expiration); - - MAKE_STD_ZVAL(zresult); - ZVAL_LONG(zresult, 0); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_LONG(&zdelta, (long) delta); + ZVAL_LONG(&zinital, (long) initial); + ZVAL_LONG(&zexpiration, (long) expiration); + ZVAL_LONG(&zresult, 0); + ZVAL_NULL(&zresult_cas); - MAKE_STD_ZVAL(zresult_cas); - ZVAL_NULL(zresult_cas); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zdelta); + ZVAL_COPY(¶ms[3], &zinital); + ZVAL_COPY(¶ms[4], &zexpiration); + ZVAL_COPY(¶ms[5], &zresult); + ZVAL_COPY(¶ms[6], &zresult_cas); - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zdelta; - params [3] = &zinital; - params [4] = &zexpiration; - params [5] = &zresult; - params [6] = &zresult_cas; + retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 7); - retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 7 TSRMLS_CC); - - if (Z_TYPE_P(zresult) != IS_LONG) { - convert_to_long (zresult); - } - *result = (uint64_t) Z_LVAL_P(zresult); + *result = (uint64_t)zval_get_long(zresult); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[3]); + zval_ptr_dtor(¶ms[4]); + zval_ptr_dtor(¶ms[5]); + zval_ptr_dtor(¶ms[6]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zdelta); @@ -284,10 +260,8 @@ protocol_binary_response_status s_delete_handler (const void *cookie, const void uint16_t key_len, uint64_t cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zkey, *zcas; - zval **params [3]; - - TSRMLS_FETCH(); + zval zcookie, zkey, zcas; + zval params[3]; if (!MEMC_HAS_CB(MEMC_SERVER_ON_DELETE)) { return retval; @@ -295,21 +269,21 @@ protocol_binary_response_status s_delete_handler (const void *cookie, const void MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zkey); - ZVAL_STRINGL(zkey, key, key_len, 1); - - MAKE_STD_ZVAL(zcas); - ZVAL_DOUBLE(zcas, (double) cas); - - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zcas; - - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_DELETE), params, 3 TSRMLS_CC); - - zval_ptr_dtor (&zcookie); - zval_ptr_dtor (&zkey); - zval_ptr_dtor (&zcas); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_DOUBLE(&zcas, (double) cas); + + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zcas); + + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_DELETE), params, 3); + + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(&zcookie); + zval_ptr_dtor(&zkey); + zval_ptr_dtor(&zcas); return retval; } @@ -317,10 +291,8 @@ static protocol_binary_response_status s_flush_handler(const void *cookie, uint32_t when) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zwhen; - zval **params [2]; - - TSRMLS_FETCH(); + zval zcookie, zwhen; + zval params[2]; if (!MEMC_HAS_CB(MEMC_SERVER_ON_FLUSH)) { return retval; @@ -328,16 +300,15 @@ protocol_binary_response_status s_flush_handler(const void *cookie, uint32_t whe MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zwhen); - ZVAL_LONG(zwhen, (long) when); - - params [0] = &zcookie; - params [1] = &zwhen; + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zwhen) - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_FLUSH), params, 2 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_FLUSH), params, 2); - zval_ptr_dtor (&zcookie); - zval_ptr_dtor (&zwhen); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(&zcookie); + zval_ptr_dtor(&zwhen); return retval; } @@ -346,10 +317,8 @@ protocol_binary_response_status s_get_handler (const void *cookie, const void *k memcached_binary_protocol_get_response_handler response_handler) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zkey, *zvalue, *zflags, *zresult_cas; - zval **params [5]; - - TSRMLS_FETCH(); + zval zcookie, zkey, zvalue, zflags, zresult_cas; + zval params[5]; if (!MEMC_HAS_CB(MEMC_SERVER_ON_GET)) { return retval; @@ -357,52 +326,50 @@ protocol_binary_response_status s_get_handler (const void *cookie, const void *k MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zkey); - ZVAL_STRINGL(zkey, key, key_len, 1); - - MAKE_STD_ZVAL(zvalue); - ZVAL_NULL(zvalue); - - MAKE_STD_ZVAL(zflags); - ZVAL_NULL(zflags); - - MAKE_STD_ZVAL(zresult_cas); - ZVAL_NULL(zresult_cas); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zvalue); + ZVAL_COPY(¶ms[3], &zflags); + ZVAL_COPY(¶ms[4], &zresult_cas); - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zvalue; - params [3] = &zflags; - params [4] = &zresult_cas; - - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_GET), params, 5 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_GET), params, 5); /* Succeeded in getting the key */ if (retval == PROTOCOL_BINARY_RESPONSE_SUCCESS) { uint32_t flags = 0; uint64_t result_cas = 0; - if (Z_TYPE_P (zvalue) == IS_NULL) { - zval_ptr_dtor (&zcookie); - zval_ptr_dtor (&zkey); - zval_ptr_dtor (&zvalue); - zval_ptr_dtor (&zflags); - zval_ptr_dtor (&zresult_cas); + if (Z_TYPE(zvalue) == IS_NULL) { + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[3]); + zval_ptr_dtor(¶ms[4]); + zval_ptr_dtor(&zcookie); + zval_ptr_dtor(&zkey); + zval_ptr_dtor(&zvalue); + zval_ptr_dtor(&zflags); + zval_ptr_dtor(&zresult_cas); return PROTOCOL_BINARY_RESPONSE_KEY_ENOENT; } - if (Z_TYPE_P (zvalue) != IS_STRING) { - convert_to_string (zvalue); + if (Z_TYPE(zvalue) != IS_STRING) { + convert_to_string (&zvalue); } - if (Z_TYPE_P (zflags) == IS_LONG) { - flags = Z_LVAL_P (zflags); + if (Z_TYPE(zflags) == IS_LONG) { + flags = Z_LVAL(zflags); } MEMC_MAKE_RESULT_CAS(zresult_cas, result_cas); - retval = response_handler(cookie, key, key_len, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), flags, result_cas); + retval = response_handler(cookie, key, key_len, Z_STRVAL(zvalue), Z_STRLEN(zvalue), flags, result_cas); } + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[3]); + zval_ptr_dtor(¶ms[4]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zvalue); @@ -415,10 +382,8 @@ static protocol_binary_response_status s_noop_handler(const void *cookie) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie; - zval **params [1]; - - TSRMLS_FETCH(); + zval zcookie; + zval params[1]; if (!MEMC_HAS_CB(MEMC_SERVER_ON_NOOP)) { return retval; @@ -426,10 +391,11 @@ protocol_binary_response_status s_noop_handler(const void *cookie) MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - params [0] = &zcookie; + ZVAL_COPY(¶ms[0], &zcookie); - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_NOOP), params, 1 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_NOOP), params, 1); + zval_ptr_dtor(¶ms[0]); zval_ptr_dtor (&zcookie); return retval; } @@ -437,11 +403,9 @@ protocol_binary_response_status s_noop_handler(const void *cookie) static protocol_binary_response_status s_quit_handler(const void *cookie) { - zval **params [1]; + zval params[1]; protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie; - - TSRMLS_FETCH(); + zval zcookie; if (!MEMC_HAS_CB(MEMC_SERVER_ON_QUIT)) { return retval; @@ -449,9 +413,11 @@ protocol_binary_response_status s_quit_handler(const void *cookie) MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - params [0] = &zcookie; - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_QUIT), params, 1 TSRMLS_CC); + ZVAL_COPY(¶ms[0], &zcookie); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_QUIT), params, 1); + + zval_ptr_dtor(¶ms[0]); zval_ptr_dtor (&zcookie); return retval; } @@ -463,10 +429,8 @@ protocol_binary_response_status s_set_replace_handler (php_memc_event_t event, c uint32_t data_len, uint32_t flags, uint32_t expiration, uint64_t cas, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zkey, *zdata, *zflags, *zexpiration, *zcas, *zresult_cas; - zval **params [7]; - - TSRMLS_FETCH(); + zval zcookie, zkey, zdata, zflags, zexpiration, zcas, zresult_cas; + zval params[7]; if (!MEMC_HAS_CB(event)) { return retval; @@ -474,36 +438,32 @@ protocol_binary_response_status s_set_replace_handler (php_memc_event_t event, c MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zkey); - ZVAL_STRINGL(zkey, key, key_len, 1); - - MAKE_STD_ZVAL(zdata); - ZVAL_STRINGL(zdata, ((char *) data), (int) data_len, 1); - - MAKE_STD_ZVAL(zflags); - ZVAL_LONG(zflags, (long) flags); - - MAKE_STD_ZVAL(zexpiration); - ZVAL_LONG(zexpiration, (long) expiration); - - MAKE_STD_ZVAL(zcas); - ZVAL_DOUBLE(zcas, (double) cas); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_STRINGL(&zdata, ((char *) data), (int) data_len); + ZVAL_LONG(&zflags, (long) flags); + ZVAL_LONG(&zexpiration, (long) expiration); + ZVAL_DOUBLE(&zcas, (double) cas); + ZVAL_NULL(&zresult_cas); - MAKE_STD_ZVAL(zresult_cas); - ZVAL_NULL(zresult_cas); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zdata); + ZVAL_COPY(¶ms[3], &zflags); + ZVAL_COPY(¶ms[4], &zexpiration); + ZVAL_COPY(¶ms[5], &zcas); + ZVAL_COPY(¶ms[6], &zresult_cas); - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zdata; - params [3] = &zflags; - params [4] = &zexpiration; - params [5] = &zcas; - params [6] = &zresult_cas; - - retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 7 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 7); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[3]); + zval_ptr_dtor(¶ms[4]); + zval_ptr_dtor(¶ms[5]); + zval_ptr_dtor(¶ms[6]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zdata); @@ -535,11 +495,9 @@ static protocol_binary_response_status s_stat_handler (const void *cookie, const void *key, uint16_t key_len, memcached_binary_protocol_stat_response_handler response_handler) { - zval **params [3]; + zval params[3]; protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zkey, *zbody; - - TSRMLS_FETCH(); + zval zcookie, zkey, zbody; if (!MEMC_HAS_CB(MEMC_SERVER_ON_STAT)) { return retval; @@ -547,29 +505,30 @@ protocol_binary_response_status s_stat_handler (const void *cookie, const void * MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zkey); - ZVAL_STRINGL(zkey, key, key_len, 1); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_NULL(&zbody); - MAKE_STD_ZVAL(zbody); - ZVAL_NULL(zbody); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zbody); - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zbody; - - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_STAT), params, 3 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_STAT), params, 3); if (retval == PROTOCOL_BINARY_RESPONSE_SUCCESS) { - if (Z_TYPE_P (zbody) == IS_NULL) { + if (Z_TYPE(zbody) == IS_NULL) { retval = response_handler(cookie, NULL, 0, NULL, 0); } else { - if (Z_TYPE_P (zbody) != IS_STRING) { - convert_to_string (zbody); + if (Z_TYPE(zbody) != IS_STRING) { + convert_to_string(&zbody); } - retval = response_handler(cookie, key, key_len, Z_STRVAL_P (zbody), (uint32_t) Z_STRLEN_P (zbody)); + retval = response_handler(cookie, key, key_len, Z_STRVAL(zbody), (uint32_t) Z_STRLEN(zbody)); } } + + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zbody); @@ -580,11 +539,9 @@ static protocol_binary_response_status s_version_handler (const void *cookie, memcached_binary_protocol_version_response_handler response_handler) { - zval **params [2]; + zval params[2]; protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; - zval *zcookie, *zversion; - - TSRMLS_FETCH(); + zval zcookie, zversion; if (!MEMC_HAS_CB(MEMC_SERVER_ON_VERSION)) { return retval; @@ -592,21 +549,23 @@ protocol_binary_response_status s_version_handler (const void *cookie, MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); - MAKE_STD_ZVAL(zversion); - ZVAL_NULL(zversion); + ZVAL_NULL(&zversion); - params [0] = &zcookie; - params [1] = &zversion; + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zversion); - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_VERSION), params, 2 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_VERSION), params, 2); if (retval == PROTOCOL_BINARY_RESPONSE_SUCCESS) { - if (Z_TYPE_P (zversion) != IS_STRING) { - convert_to_string (zversion); + if (Z_TYPE(zversion) != IS_STRING) { + convert_to_string(&zversion); } retval = response_handler (cookie, Z_STRVAL_P(zversion), (uint32_t) Z_STRLEN_P(zversion)); } + + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zversion); return retval; @@ -623,36 +582,33 @@ void s_handle_memcached_event (evutil_socket_t fd, short what, void *arg) php_memc_client_t *client = (php_memc_client_t *) arg; memcached_protocol_event_t events; - TSRMLS_FETCH(); - if (!client->on_connect_invoked) { if (MEMC_HAS_CB(MEMC_SERVER_ON_CONNECT)) { - zval *zremoteip, *zremoteport; - zval **params [2]; + zval zremoteip, zremoteport; + zval params[2]; protocol_binary_response_status retval; struct sockaddr_in addr_in; socklen_t addr_in_len = sizeof(addr_in); - MAKE_STD_ZVAL(zremoteip); - MAKE_STD_ZVAL(zremoteport); - if (getpeername (fd, (struct sockaddr *) &addr_in, &addr_in_len) == 0) { - ZVAL_STRING(zremoteip, inet_ntoa (addr_in.sin_addr), 1); - ZVAL_LONG(zremoteport, ntohs (addr_in.sin_port)); + ZVAL_STRING(&zremoteip, inet_ntoa (addr_in.sin_addr), 1); + ZVAL_LONG(&zremoteport, ntohs (addr_in.sin_port)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "getpeername failed: %s", strerror (errno)); - ZVAL_NULL(zremoteip); - ZVAL_NULL(zremoteport); + php_error_docref(NULL, E_WARNING, "getpeername failed: %s", strerror (errno)); + ZVAL_NULL(&zremoteip); + ZVAL_NULL(&zremoteport); } - params [0] = &zremoteip; - params [1] = &zremoteport; + ZVAL_COPY(¶ms[0], &zremoteip); + ZVAL_COPY(¶ms[1], &zremoteport); - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_CONNECT), params, 2 TSRMLS_CC); + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_CONNECT), params, 2); - zval_ptr_dtor (&zremoteip); - zval_ptr_dtor (&zremoteport); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(&zremoteip); + zval_ptr_dtor(&zremoteport); if (retval != PROTOCOL_BINARY_RESPONSE_SUCCESS) { memcached_protocol_client_destroy (client->protocol_client); @@ -683,7 +639,7 @@ void s_handle_memcached_event (evutil_socket_t fd, short what, void *arg) rc = event_base_once (client->event_base, fd, flags, s_handle_memcached_event, client, NULL); if (rc != 0) { - php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to schedule events"); + php_error_docref (NULL, E_WARNING, "Failed to schedule events"); } } @@ -698,14 +654,12 @@ void s_accept_cb (evutil_socket_t fd, short what, void *arg) php_memc_proto_handler_t *handler = (php_memc_proto_handler_t *) arg; - TSRMLS_FETCH(); - /* Accept the connection */ addr_len = sizeof (addr); sock = accept (fd, (struct sockaddr *) &addr, &addr_len); if (sock == -1) { - php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to accept the client: %s", strerror (errno)); + php_error_docref (NULL, E_WARNING, "Failed to accept the client: %s", strerror (errno)); return; } @@ -715,7 +669,7 @@ void s_accept_cb (evutil_socket_t fd, short what, void *arg) client->on_connect_invoked = 0; if (!client->protocol_client) { - php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to allocate protocol client"); + php_error_docref (NULL, E_WARNING, "Failed to allocate protocol client"); efree (client); evutil_closesocket (sock); return; @@ -725,7 +679,7 @@ void s_accept_cb (evutil_socket_t fd, short what, void *arg) rc = event_base_once (handler->event_base, sock, EV_READ, s_handle_memcached_event, client, NULL); if (rc != 0) { - php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to add event for client"); + php_error_docref (NULL, E_WARNING, "Failed to add event for client"); memcached_protocol_client_destroy (client->protocol_client); efree (client); evutil_closesocket (sock); @@ -771,59 +725,55 @@ evutil_socket_t s_create_listening_socket (const char *spec) int rc; - TSRMLS_FETCH(); - addr_len = sizeof (struct sockaddr); rc = evutil_parse_sockaddr_port (spec, (struct sockaddr *) &addr, &addr_len); if (rc != 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse bind address"); + php_error_docref(NULL, E_WARNING, "Failed to parse bind address"); return -1; } sock = socket (AF_INET, SOCK_STREAM, 0); if (sock < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket failed: %s", strerror (errno)); + php_error_docref(NULL, E_WARNING, "socket failed: %s", strerror (errno)); return -1; } rc = bind (sock, (struct sockaddr *) &addr, addr_len); if (rc < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind failed: %s", strerror (errno)); + php_error_docref(NULL, E_WARNING, "bind failed: %s", strerror (errno)); return -1; } rc = listen (sock, 1024); if (rc < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen failed: %s", strerror (errno)); + php_error_docref(NULL, E_WARNING, "listen failed: %s", strerror (errno)); return -1; } rc = evutil_make_socket_nonblocking (sock); if (rc != 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to make socket non-blocking: %s", strerror (errno)); + php_error_docref(NULL, E_WARNING, "failed to make socket non-blocking: %s", strerror (errno)); return -1; } rc = evutil_make_listen_socket_reuseable (sock); if (rc != 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to make socket reuseable: %s", strerror (errno)); + php_error_docref(NULL, E_WARNING, "failed to make socket reuseable: %s", strerror (errno)); return -1; } rc = evutil_make_socket_closeonexec (sock); if (rc != 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to make socket closeonexec: %s", strerror (errno)); + php_error_docref(NULL, E_WARNING, "failed to make socket closeonexec: %s", strerror (errno)); return -1; } return sock; } -zend_bool php_memc_proto_handler_run (php_memc_proto_handler_t *handler, const char *address) +zend_bool php_memc_proto_handler_run (php_memc_proto_handler_t *handler, zend_string *address) { struct event *accept_event; - evutil_socket_t sock = s_create_listening_socket (address); - - TSRMLS_FETCH(); + evutil_socket_t sock = s_create_listening_socket (address->val); if (sock == -1) { return 0; @@ -831,22 +781,22 @@ zend_bool php_memc_proto_handler_run (php_memc_proto_handler_t *handler, const c handler->event_base = event_base_new(); if (!handler->event_base) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "failed to allocate memory: %s", strerror (errno)); + php_error_docref(NULL, E_ERROR, "failed to allocate memory: %s", strerror (errno)); } accept_event = event_new (handler->event_base, sock, EV_READ | EV_PERSIST, s_accept_cb, handler); if (!accept_event) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "failed to allocate memory: %s", strerror (errno)); + php_error_docref(NULL, E_ERROR, "failed to allocate memory: %s", strerror (errno)); } event_add (accept_event, NULL); switch (event_base_dispatch (handler->event_base)) { case -1: - php_error_docref(NULL TSRMLS_CC, E_ERROR, "event_base_dispatch() failed: %s", strerror (errno)); + php_error_docref(NULL, E_ERROR, "event_base_dispatch() failed: %s", strerror (errno)); return 0; break; case 1: - php_error_docref(NULL TSRMLS_CC, E_ERROR, "no events registered"); + php_error_docref(NULL, E_ERROR, "no events registered"); return 0; break; @@ -865,4 +815,11 @@ void php_memc_proto_handler_destroy (php_memc_proto_handler_t **ptr) efree (handler); *ptr = NULL; -} \ No newline at end of file +} +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim: noet sw=4 ts=4 fdm=marker: + */ diff --git a/php_memcached_server.h b/php_memcached_server.h index 31676c53..285c608d 100644 --- a/php_memcached_server.h +++ b/php_memcached_server.h @@ -33,8 +33,8 @@ php_memc_proto_handler_t *php_memc_proto_handler_new (); void php_memc_proto_handler_destroy (php_memc_proto_handler_t **ptr); -zend_bool php_memc_proto_handler_run (php_memc_proto_handler_t *h, const char *address); +zend_bool php_memc_proto_handler_run (php_memc_proto_handler_t *h, zend_string *address); #endif -#endif \ No newline at end of file +#endif diff --git a/php_memcached_session.c b/php_memcached_session.c index a4e89aed..59f6b238 100644 --- a/php_memcached_session.c +++ b/php_memcached_session.c @@ -18,280 +18,397 @@ #include "php_memcached_private.h" #include "php_memcached_session.h" +#include "Zend/zend_smart_str_public.h" + extern ZEND_DECLARE_MODULE_GLOBALS(php_memcached) #define MEMC_SESS_DEFAULT_LOCK_WAIT 150000 #define MEMC_SESS_LOCK_EXPIRATION 30 +#define REALTIME_MAXDELTA 60*60*24*30 + ps_module ps_mod_memcached = { - PS_MOD(memcached) + PS_MOD_UPDATE_TIMESTAMP(memcached) }; -static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC) +typedef struct { + zend_bool is_persistent; + zend_bool has_sasl_data; + zend_bool is_locked; + zend_string *lock_key; +} php_memcached_user_data; + +#ifndef MIN +# define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +#ifdef ZTS +#define MEMC_SESS_INI(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, session.v) +#else +#define MEMC_SESS_INI(v) (php_memcached_globals.session.v) +#endif + +#define MEMC_SESS_STR_INI(vv) ((MEMC_SESS_INI(vv) && *MEMC_SESS_INI(vv)) ? MEMC_SESS_INI(vv) : NULL) + +static + int le_memc_sess; + +static +int s_memc_sess_list_entry(void) { - char *lock_key = NULL; - int lock_key_len = 0; - unsigned long attempts; - long write_retry_attempts = 0; - long lock_maxwait = MEMC_G(sess_lock_max_wait); - long lock_wait = MEMC_G(sess_lock_wait); - long lock_expire = MEMC_G(sess_lock_expire); - time_t expiration; - memcached_return status; - /* set max timeout for session_start = max_execution_time. (c) Andrei Darashenka, Richter & Poweleit GmbH */ - if (lock_maxwait <= 0) { - lock_maxwait = zend_ini_long(ZEND_STRS("max_execution_time"), 0); - if (lock_maxwait <= 0) { - lock_maxwait = MEMC_SESS_LOCK_EXPIRATION; - } + return le_memc_sess; +} + +static +void s_destroy_mod_data(memcached_st *memc) +{ + php_memcached_user_data *user_data = memcached_get_user_data(memc); + +#if HAVE_MEMCACHED_SASL + if (user_data->has_sasl_data) { + memcached_destroy_sasl_auth_data(memc); + } +#endif + + memcached_free(memc); + pefree(memc, user_data->is_persistent); + pefree(user_data, user_data->is_persistent); +} + +ZEND_RSRC_DTOR_FUNC(php_memc_sess_dtor) +{ + if (res->ptr) { + s_destroy_mod_data((memcached_st *) res->ptr); + res->ptr = NULL; } - if (lock_wait == 0) { - lock_wait = MEMC_SESS_DEFAULT_LOCK_WAIT; +} + +int php_memc_session_minit(int module_number) +{ + le_memc_sess = + zend_register_list_destructors_ex(NULL, php_memc_sess_dtor, "Memcached Sessions persistent connection", module_number); + + php_session_register_module(ps_memcached_ptr); + return SUCCESS; +} + +static +time_t s_adjust_expiration(zend_long expiration) +{ + if (expiration <= REALTIME_MAXDELTA) { + return expiration; + } else { + return time(NULL) + expiration; + } +} + +static +time_t s_lock_expiration() +{ + if (MEMC_SESS_INI(lock_expiration) > 0) { + return s_adjust_expiration(MEMC_SESS_INI(lock_expiration)); } - if (lock_expire <= 0) { - lock_expire = lock_maxwait; + else { + zend_long max_execution_time = zend_ini_long(ZEND_STRS("max_execution_time"), 0); + if (max_execution_time > 0) { + return s_adjust_expiration(max_execution_time); + } } - expiration = lock_expire + 1; - attempts = (unsigned long)((1000000.0 / lock_wait) * lock_maxwait); + return 0; +} - /* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server */ - if (MEMC_G(sess_remove_failed_enabled)) { - write_retry_attempts = MEMC_G(sess_number_of_replicas) * ( memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1); +static +time_t s_session_expiration(zend_long maxlifetime) +{ + if (maxlifetime > 0) { + return s_adjust_expiration(maxlifetime); } + return 0; +} + +static +zend_bool s_lock_session(memcached_st *memc, zend_string *sid) +{ + memcached_return rc; + char *lock_key; + size_t lock_key_len; + time_t expiration; + zend_long wait_time, retries; + php_memcached_user_data *user_data = memcached_get_user_data(memc); + + lock_key_len = spprintf(&lock_key, 0, "lock.%s", sid->val); + expiration = s_lock_expiration(); + + wait_time = MEMC_SESS_INI(lock_wait_min); + retries = MEMC_SESS_INI(lock_retries); - lock_key_len = spprintf(&lock_key, 0, "lock.%s", key); do { - status = memcached_add(memc, lock_key, lock_key_len, "1", sizeof("1")-1, expiration, 0); - if (status == MEMCACHED_SUCCESS) { - MEMC_G(sess_locked) = 1; - MEMC_G(sess_lock_key) = lock_key; - MEMC_G(sess_lock_key_len) = lock_key_len; - return 0; - } else if (status != MEMCACHED_NOTSTORED && status != MEMCACHED_DATA_EXISTS) { - if (write_retry_attempts > 0) { - write_retry_attempts--; - continue; - } - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Write of lock failed"); + rc = memcached_add(memc, lock_key, lock_key_len, "1", sizeof ("1") - 1, expiration, 0); + + switch (rc) { + + case MEMCACHED_SUCCESS: + user_data->lock_key = zend_string_init(lock_key, lock_key_len, user_data->is_persistent); + user_data->is_locked = 1; break; - } - if (lock_wait > 0) { - usleep(lock_wait); + case MEMCACHED_NOTSTORED: + case MEMCACHED_DATA_EXISTS: + if (retries > 0) { + usleep(wait_time * 1000); + wait_time = MIN(MEMC_SESS_INI(lock_wait_max), wait_time * 2); + } + break; + + default: + php_error_docref(NULL, E_WARNING, "Failed to write session lock: %s", memcached_strerror (memc, rc)); + break; } - } while(--attempts > 0); + } while (!user_data->is_locked && retries-- > 0); efree(lock_key); - return -1; + return user_data->is_locked; } -static void php_memc_sess_unlock(memcached_st *memc TSRMLS_DC) +static +void s_unlock_session(memcached_st *memc) { - if (MEMC_G(sess_locked)) { - memcached_delete(memc, MEMC_G(sess_lock_key), MEMC_G(sess_lock_key_len), 0); - MEMC_G(sess_locked) = 0; - efree(MEMC_G(sess_lock_key)); - MEMC_G(sess_lock_key_len) = 0; + php_memcached_user_data *user_data = memcached_get_user_data(memc); + + if (user_data->is_locked) { + memcached_delete(memc, user_data->lock_key->val, user_data->lock_key->len, 0); + user_data->is_locked = 0; + zend_string_release (user_data->lock_key); } } -PS_OPEN_FUNC(memcached) +static +zend_bool s_configure_from_ini_values(memcached_st *memc, zend_bool silent) { - memcached_sess *memc_sess = PS_GET_MOD_DATA(); - memcached_return status; - char *p, *plist_key = NULL; - int plist_key_len = 0; + memcached_return rc; - if (!strncmp((char *)save_path, "PERSISTENT=", sizeof("PERSISTENT=") - 1)) { - zend_rsrc_list_entry *le = NULL; - char *e; +#define check_set_behavior(behavior, value) \ + if ((rc = memcached_behavior_set(memc, (behavior), (value))) != MEMCACHED_SUCCESS) { \ + if (!silent) { php_error_docref(NULL, E_WARNING, "failed to initialise session memcached configuration: %s", memcached_strerror(memc, rc)); } \ + return 0; \ + } - p = (char *)save_path + sizeof("PERSISTENT=") - 1; - if (!*p) { -error: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid persistent id for session storage"); - return FAILURE; - } - if ((e = strchr(p, ' '))) { - plist_key_len = spprintf(&plist_key, 0, "memcached_sessions:id=%.*s", (int)(e - p), p); - } else { - goto error; - } - plist_key_len++; - if (zend_hash_find(&EG(persistent_list), plist_key, plist_key_len, (void *)&le) == SUCCESS) { - if (le->type == php_memc_sess_list_entry()) { - memc_sess = (memcached_sess *) le->ptr; - PS_SET_MOD_DATA(memc_sess); - return SUCCESS; - } + if (MEMC_SESS_INI(binary_protocol_enabled)) { + check_set_behavior(MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); + } + + if (MEMC_SESS_INI(consistent_hash_enabled)) { + check_set_behavior(MEMCACHED_BEHAVIOR_KETAMA, 1); + } + + if (MEMC_SESS_INI(server_failure_limit)) { + check_set_behavior(MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, MEMC_SESS_INI(server_failure_limit)); + } + + if (MEMC_SESS_INI(number_of_replicas)) { + check_set_behavior(MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, MEMC_SESS_INI(number_of_replicas)); + } + + if (MEMC_SESS_INI(randomize_replica_read_enabled)) { + check_set_behavior(MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, 1); + } + + if (MEMC_SESS_INI(remove_failed_servers_enabled)) { + check_set_behavior(MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS, 1); + } + + if (MEMC_SESS_INI(connect_timeout)) { + check_set_behavior(MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, MEMC_SESS_INI(connect_timeout)); + } + + if (MEMC_SESS_STR_INI(prefix)) { + memcached_callback_set(memc, MEMCACHED_CALLBACK_NAMESPACE, MEMC_SESS_STR_INI(prefix)); + } + + if (MEMC_SESS_STR_INI(sasl_username) && MEMC_SESS_STR_INI(sasl_password)) { + php_memcached_user_data *user_data; + + if (!php_memc_init_sasl_if_needed()) { + return 0; } - p = e + 1; - memc_sess = pecalloc(sizeof(*memc_sess), 1, 1); - memc_sess->is_persistent = 1; - } else { - p = (char *)save_path; - memc_sess = ecalloc(sizeof(*memc_sess), 1); - memc_sess->is_persistent = 0; - } - - if (!strstr(p, "--SERVER")) { - memcached_server_st *servers = memcached_servers_parse(p); - if (servers) { - memc_sess->memc_sess = memcached_create(NULL); - if (memc_sess->memc_sess) { - if (MEMC_G(sess_consistent_hash_enabled)) { - if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, (uint64_t) 1) == MEMCACHED_FAILURE) { - PS_SET_MOD_DATA(NULL); - if (plist_key) { - efree(plist_key); - } - memcached_free(memc_sess->memc_sess); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to enable memcached consistent hashing"); - return FAILURE; - } - } - status = memcached_server_push(memc_sess->memc_sess, servers); - memcached_server_list_free(servers); - - if (MEMC_G(sess_prefix) && MEMC_G(sess_prefix)[0] != 0 && memcached_callback_set(memc_sess->memc_sess, MEMCACHED_CALLBACK_PREFIX_KEY, MEMC_G(sess_prefix)) != MEMCACHED_SUCCESS) { - PS_SET_MOD_DATA(NULL); - if (plist_key) { - efree(plist_key); - } - memcached_free(memc_sess->memc_sess); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "bad memcached key prefix in memcached.sess_prefix"); - return FAILURE; - } + check_set_behavior(MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); - if (status == MEMCACHED_SUCCESS) { - goto success; - } - } else { - memcached_server_list_free(servers); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not allocate libmemcached structure"); - } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to parse session.save_path"); + if (memcached_set_sasl_auth_data(memc, MEMC_SESS_STR_INI(sasl_username), MEMC_SESS_STR_INI(sasl_password)) == MEMCACHED_FAILURE) { + php_error_docref(NULL, E_WARNING, "failed to set memcached session sasl credentials"); + return 0; } - } else { - memc_sess->memc_sess = php_memc_create_str(p, strlen(p)); - if (!memc_sess->memc_sess) { -#ifdef HAVE_LIBMEMCACHED_CHECK_CONFIGURATION - char error_buffer[1024]; - if (libmemcached_check_configuration(p, strlen(p), error_buffer, sizeof(error_buffer)) != MEMCACHED_SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "session.save_path configuration error %s", error_buffer); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to initialize memcached session storage"); - } -#else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to initialize memcached session storage"); -#endif + user_data = memcached_get_user_data(memc); + user_data->has_sasl_data = 1; + } - } else { -success: - PS_SET_MOD_DATA(memc_sess); +#undef safe_set_behavior - if (plist_key) { - zend_rsrc_list_entry le; + return 1; +} - le.type = php_memc_sess_list_entry(); - le.ptr = memc_sess; +static +void *s_pemalloc_fn(const memcached_st *memc, size_t size, void *context) +{ + zend_bool *is_persistent = memcached_get_user_data(memc); - if (zend_hash_update(&EG(persistent_list), (char *)plist_key, plist_key_len, (void *)&le, sizeof(le), NULL) == FAILURE) { - efree(plist_key); - php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not register persistent entry"); - } - efree(plist_key); - } + return + pemalloc(size, *is_persistent); +} - if (MEMC_G(sess_binary_enabled)) { - if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, (uint64_t) 1) == MEMCACHED_FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session binary protocol"); - return FAILURE; - } - } -#ifdef HAVE_MEMCACHED_SASL - if (MEMC_G(use_sasl)) { - /* - * Enable SASL support if username and password are set - * - */ - if (MEMC_G(sess_sasl_username) && MEMC_G(sess_sasl_password)) { - /* Force binary protocol */ - if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, (uint64_t) 1) == MEMCACHED_FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session binary protocol"); - return FAILURE; - } - if (memcached_set_sasl_auth_data(memc_sess->memc_sess, MEMC_G(sess_sasl_username), MEMC_G(sess_sasl_password)) == MEMCACHED_FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session sasl credentials"); - return FAILURE; - } - MEMC_G(sess_sasl_data) = 1; - } - } +static +void s_pefree_fn(const memcached_st *memc, void *mem, void *context) +{ + zend_bool *is_persistent = memcached_get_user_data(memc); + return + pefree(mem, *is_persistent); +} -#endif - if (MEMC_G(sess_number_of_replicas) > 0) { - if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, (uint64_t) MEMC_G(sess_number_of_replicas)) == MEMCACHED_FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session number of replicas"); - return FAILURE; +static +void *s_perealloc_fn(const memcached_st *memc, void *mem, const size_t size, void *context) +{ + zend_bool *is_persistent = memcached_get_user_data(memc); + + return + perealloc(mem, size, *is_persistent); +} + +static +void *s_pecalloc_fn(const memcached_st *memc, size_t nelem, const size_t elsize, void *context) +{ + zend_bool *is_persistent = memcached_get_user_data(memc); + + return + pecalloc(nelem, elsize, *is_persistent); +} + + +static +memcached_st *s_init_mod_data (const memcached_server_list_st servers, zend_bool is_persistent) +{ + void *buffer; + php_memcached_user_data *user_data; + memcached_st *memc; + + buffer = pecalloc(1, sizeof(memcached_st), is_persistent); + memc = memcached_create (buffer); + + if (!memc) { + php_error_docref(NULL, E_ERROR, "failed to allocate memcached structure"); + /* not reached */ + } + + memcached_set_memory_allocators(memc, s_pemalloc_fn, s_pefree_fn, s_perealloc_fn, s_pecalloc_fn, NULL); + + user_data = pecalloc(1, sizeof(php_memcached_user_data), is_persistent); + user_data->is_persistent = is_persistent; + user_data->has_sasl_data = 0; + user_data->lock_key = NULL; + user_data->is_locked = 0; + + memcached_set_user_data(memc, user_data); + memcached_server_push (memc, servers); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_VERIFY_KEY, 1); + return memc; +} + +PS_OPEN_FUNC(memcached) +{ + memcached_st *memc = NULL; + char *plist_key = NULL; + size_t plist_key_len = 0; + + memcached_server_list_st servers; + + // First parse servers + servers = memcached_servers_parse(save_path); + + if (!servers) { + php_error_docref(NULL, E_WARNING, "failed to parse session.save_path"); + PS_SET_MOD_DATA(NULL); + return FAILURE; + } + + if (MEMC_SESS_INI(persistent_enabled)) { + zend_resource *le_p; + + plist_key_len = spprintf(&plist_key, 0, "memc-session:%s", save_path); + + if ((le_p = zend_hash_str_find_ptr(&EG(persistent_list), plist_key, plist_key_len)) != NULL) { + if (le_p->type == s_memc_sess_list_entry()) { + memc = (memcached_st *) le_p->ptr; + + if (!s_configure_from_ini_values(memc, 1)) { + // Remove existing plist entry + zend_hash_str_del(&EG(persistent_list), plist_key, plist_key_len); + memc = NULL; } - if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, (uint64_t) MEMC_G(sess_randomize_replica_read)) == MEMCACHED_FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session randomize replica read"); + else { + efree(plist_key); + PS_SET_MOD_DATA(memc); + return SUCCESS; } } + } + } - if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, (uint64_t) MEMC_G(sess_connect_timeout)) == MEMCACHED_FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached connection timeout"); - return FAILURE; - } -#ifdef HAVE_MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS - /* Allow libmemcached remove failed servers */ - if (MEMC_G(sess_remove_failed_enabled)) { - if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS, (uint64_t) 1) == MEMCACHED_FAILURE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set: remove failed servers"); - return FAILURE; - } - } -#endif - return SUCCESS; + memc = s_init_mod_data(servers, MEMC_SESS_INI(persistent_enabled)); + memcached_server_list_free(servers); + + if (!s_configure_from_ini_values(memc, 0)) { + if (plist_key) { + efree(plist_key); } + s_destroy_mod_data(memc); + PS_SET_MOD_DATA(NULL); + return FAILURE; } if (plist_key) { + zend_resource le; + + le.type = s_memc_sess_list_entry(); + le.ptr = memc; + + GC_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), plist_key, plist_key_len, &le, sizeof(le)) == NULL) { + php_error_docref(NULL, E_ERROR, "Could not register persistent entry for the memcached session"); + /* not reached */ + } efree(plist_key); } - PS_SET_MOD_DATA(NULL); - return FAILURE; + PS_SET_MOD_DATA(memc); + return SUCCESS; } PS_CLOSE_FUNC(memcached) { - memcached_sess *memc_sess = PS_GET_MOD_DATA(); - - if (!memc_sess) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session is not allocated, check session.save_path value"); + php_memcached_user_data *user_data; + memcached_st *memc = PS_GET_MOD_DATA(); + + if (!memc) { + php_error_docref(NULL, E_WARNING, "Session is not allocated, check session.save_path value"); return FAILURE; } - if (MEMC_G(sess_locking_enabled)) { - php_memc_sess_unlock(memc_sess->memc_sess TSRMLS_CC); + user_data = memcached_get_user_data(memc); + + if (user_data->is_locked) { + s_unlock_session(memc); } - if (memc_sess->memc_sess) { - if (!memc_sess->is_persistent) { -#ifdef HAVE_MEMCACHED_SASL - if (MEMC_G(sess_sasl_data)) { - memcached_destroy_sasl_auth_data(memc_sess->memc_sess); - } -#endif - memcached_free(memc_sess->memc_sess); - efree(memc_sess); - } - PS_SET_MOD_DATA(NULL); + + if (!user_data->is_persistent) { + s_destroy_mod_data(memc); } + PS_SET_MOD_DATA(NULL); return SUCCESS; } @@ -299,37 +416,30 @@ PS_READ_FUNC(memcached) { char *payload = NULL; size_t payload_len = 0; - int key_len = strlen(key); uint32_t flags = 0; memcached_return status; - memcached_sess *memc_sess = PS_GET_MOD_DATA(); - size_t key_length; + memcached_st *memc = PS_GET_MOD_DATA(); - if (!memc_sess) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session is not allocated, check session.save_path value"); + if (!memc) { + php_error_docref(NULL, E_WARNING, "Session is not allocated, check session.save_path value"); return FAILURE; } - key_length = strlen(MEMC_G(sess_prefix)) + key_len + 5; // prefix + "lock." - if (!key_length || key_length >= MEMCACHED_MAX_KEY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id is too long or contains illegal characters"); - PS(invalid_session_id) = 1; - return FAILURE; - } - - if (MEMC_G(sess_locking_enabled)) { - if (php_memc_sess_lock(memc_sess->memc_sess, key TSRMLS_CC) < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to clear session lock record"); + if (MEMC_SESS_INI(lock_enabled)) { + if (!s_lock_session(memc, key)) { + php_error_docref(NULL, E_WARNING, "Unable to clear session lock record"); return FAILURE; } } - payload = memcached_get(memc_sess->memc_sess, key, key_len, &payload_len, &flags, &status); + payload = memcached_get(memc, key->val, key->len, &payload_len, &flags, &status); if (status == MEMCACHED_SUCCESS) { - *val = estrndup(payload, payload_len); - *vallen = payload_len; - free(payload); + *val = zend_string_init(payload, payload_len, 0); + efree(payload); + return SUCCESS; + } else if (status == MEMCACHED_NOTFOUND) { + *val = ZSTR_EMPTY_ALLOC(); return SUCCESS; } else { return FAILURE; @@ -338,60 +448,50 @@ PS_READ_FUNC(memcached) PS_WRITE_FUNC(memcached) { - int key_len = strlen(key); - time_t expiration = 0; - long write_try_attempts = 1; - memcached_return status; - memcached_sess *memc_sess = PS_GET_MOD_DATA(); - size_t key_length; - - if (!memc_sess) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session is not allocated, check session.save_path value"); - return FAILURE; - } + zend_long retries = 1; + memcached_st *memc = PS_GET_MOD_DATA(); + time_t expiration = s_session_expiration(maxlifetime); - key_length = strlen(MEMC_G(sess_prefix)) + key_len + 5; // prefix + "lock." - if (!key_length || key_length >= MEMCACHED_MAX_KEY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id is too long or contains illegal characters"); - PS(invalid_session_id) = 1; + if (!memc) { + php_error_docref(NULL, E_WARNING, "Session is not allocated, check session.save_path value"); return FAILURE; } - if (PS(gc_maxlifetime) > 0) { - expiration = PS(gc_maxlifetime); - } - /* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server plus the initial write */ - if (MEMC_G(sess_remove_failed_enabled)) { - write_try_attempts = 1 + MEMC_G(sess_number_of_replicas) * ( memcached_behavior_get(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1); + if (MEMC_SESS_INI(remove_failed_servers_enabled)) { + zend_long replicas, failure_limit; + + replicas = memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS); + failure_limit = memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT); + + retries = 1 + replicas * (failure_limit + 1); } do { - status = memcached_set(memc_sess->memc_sess, key, key_len, val, vallen, expiration, 0); - if (status == MEMCACHED_SUCCESS) { + if (memcached_set(memc, key->val, key->len, val->val, val->len, expiration, 0) == MEMCACHED_SUCCESS) { return SUCCESS; - } else { - write_try_attempts--; } - } while (write_try_attempts > 0); + } while (--retries > 0); return FAILURE; } PS_DESTROY_FUNC(memcached) { - memcached_sess *memc_sess = PS_GET_MOD_DATA(); + php_memcached_user_data *user_data; + memcached_st *memc = PS_GET_MOD_DATA(); - if (!memc_sess) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Session is not allocated, check session.save_path value"); + if (!memc) { + php_error_docref(NULL, E_WARNING, "Session is not allocated, check session.save_path value"); return FAILURE; } - memcached_delete(memc_sess->memc_sess, key, strlen(key), 0); - if (MEMC_G(sess_locking_enabled)) { - php_memc_sess_unlock(memc_sess->memc_sess TSRMLS_CC); - } + memcached_delete(memc, key->val, key->len, 0); + user_data = memcached_get_user_data(memc); + if (user_data->is_locked) { + s_unlock_session(memc); + } return SUCCESS; } @@ -399,4 +499,50 @@ PS_GC_FUNC(memcached) { return SUCCESS; } + +PS_CREATE_SID_FUNC(memcached) +{ + zend_string *sid; + memcached_st *memc = PS_GET_MOD_DATA(); + + if (!memc) { + sid = php_session_create_id(NULL); + } + else { + int retries = 3; + while (retries-- > 0) { + sid = php_session_create_id((void **) &memc); + + if (memcached_add (memc, sid->val, sid->len, NULL, 0, s_lock_expiration(), 0) == MEMCACHED_SUCCESS) { + break; + } + zend_string_release(sid); + sid = NULL; + } + } + return sid; +} + +PS_VALIDATE_SID_FUNC(memcached) +{ + memcached_st *memc = PS_GET_MOD_DATA(); + + if (php_memcached_exist(memc, key) == MEMCACHED_SUCCESS) { + return SUCCESS; + } else { + return FAILURE; + } +} + +PS_UPDATE_TIMESTAMP_FUNC(memcached) +{ + memcached_st *memc = PS_GET_MOD_DATA(); + time_t expiration = s_session_expiration(maxlifetime); + + if (memcached_touch(memc, key->val, key->len, expiration) == MEMCACHED_FAILURE) { + return FAILURE; + } + return SUCCESS; +} /* }}} */ + diff --git a/php_memcached_session.h b/php_memcached_session.h index 97fe2005..13ce363c 100644 --- a/php_memcached_session.h +++ b/php_memcached_session.h @@ -24,7 +24,7 @@ extern ps_module ps_mod_memcached; #define ps_memcached_ptr &ps_mod_memcached -PS_FUNCS(memcached); +PS_FUNCS_UPDATE_TIMESTAMP(memcached); PS_OPEN_FUNC(memcached); PS_CLOSE_FUNC(memcached); @@ -32,5 +32,11 @@ PS_READ_FUNC(memcached); PS_WRITE_FUNC(memcached); PS_DESTROY_FUNC(memcached); PS_GC_FUNC(memcached); +PS_CREATE_SID_FUNC(memcached); +PS_VALIDATE_SID_FUNC(memcached); +PS_UPDATE_TIMESTAMP_FUNC(memcached); + +/* Called from php_memcached.c */ +int php_memc_session_minit(int module_number); #endif /* PHP_MEMCACHED_SESSION_H */ diff --git a/tests/001.phpt b/tests/001.phpt index d5085cd5..a66d6df3 100644 --- a/tests/001.phpt +++ b/tests/001.phpt @@ -1,7 +1,7 @@ --TEST-- Check for memcached presence --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- + --FILE-- setOption (Memcached::OPT_BINARY_PROTOCOL, true)); echo "OK" . PHP_EOL; --EXPECTF-- -Memcached::__construct() expects parameter %s -NULL +Warning: Memcached::__construct() expects parameter 1 to be string, object given in %s on line 3 +Memcached::__construct() expects parameter 1 to be string, object given +object(Memcached)#1 (0) { +} -Warning: Memcached::setOption(): Memcached constructor was not called in %s on line %d +Warning: Memcached::setOption(): Memcached constructor was not called in %s on line 14 NULL -OK \ No newline at end of file +OK + diff --git a/tests/bug_16084.phpt b/tests/bug_16084.phpt index c103b297..f39d3b3f 100644 --- a/tests/bug_16084.phpt +++ b/tests/bug_16084.phpt @@ -1,22 +1,20 @@ --TEST-- Memcached: Bug #16084 (Crash when addServers is called with an associative array) --SKIPIF-- - + --FILE-- array ( 'KEYHERE' => 'localhost', 11211, 3 ), ); $m = new memcached(); var_dump($m->addServers($servers)); -var_dump($m->getServerList()); +$list = $m->getServerList(); + +var_dump ($list[0]["host"], $list[0]["port"]); +echo "OK"; + ?> --EXPECT-- bool(true) -array(1) { - [0]=> - array(2) { - ["host"]=> - string(9) "localhost" - ["port"]=> - int(11211) - } -} +string(9) "localhost" +int(11211) +OK diff --git a/tests/bug_16959.phpt b/tests/bug_16959.phpt index f03a7091..2411e15a 100644 --- a/tests/bug_16959.phpt +++ b/tests/bug_16959.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached: Bug #16959 (getMulti + BINARY_PROTOCOL problem) --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- getServerByKey('1')); bool(true) array(3) { ["host"]=> - string(9) "127.0.0.1" + string(9) "%s" ["port"]=> - int(11211) + int(%d) ["weight"]=> int(%r[01]%r) } diff --git a/tests/cachecallback.phpt b/tests/cachecallback.phpt index 9fe8f4f8..41edfc5c 100644 --- a/tests/cachecallback.phpt +++ b/tests/cachecallback.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::get() with cache callback --SKIPIF-- - + --FILE-- delete($first_key); +$m->delete($second_key); +$m->delete($third_key); + var_dump ( $m->get ($first_key, function (Memcached $memc, $key, &$value, &$expiration) { $value = "hello"; diff --git a/tests/callback_exception.phpt b/tests/callback_exception.phpt index 8f71d5f8..ddbbf81a 100644 --- a/tests/callback_exception.phpt +++ b/tests/callback_exception.phpt @@ -1,7 +1,7 @@ --TEST-- make sure that callback exception behaves correctly --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- delete('cas_test'); -$cas_token = null; -$m->set('cas_test', 10); -$v = $m->get('cas_test', null, $cas_token); +$m->set('cas_test', 'hello'); +$cas_token = $m->get('cas_test', null, Memcached::GET_EXTENDED)['cas']; -if (is_null($cas_token)) { - echo "Null cas token for key: cas_test value: 10\n"; - return; -} - -$v = $m->cas($cas_token, 'cas_test', 11); -if (!$v) { - echo "Error setting key: cas_test value: 11 with CAS: $cas_token\n"; - return; -} - -$v = $m->get('cas_test'); - -if ($v !== 11) { - echo "Wanted cas_test to be 11, value is: "; - var_dump($v); -} - -$v = $m->get('cas_test', null, 2); -if ($v != 11) { - echo "Failed to get the value with \$cas_token passed by value (2)\n"; - return; -} - -$v = $m->get('cas_test', null, null); -if ($v != 11) { - echo "Failed to get the value with \$cas_token passed by value (null)\n"; - return; -} - -$v = $m->get('cas_test', null, $data = array(2, 4)); -if ($v != 11 || $data !== array(2, 4)) { - echo "Failed to get the value with \$cas_token passed by value (\$data = array(2, 4))\n"; - return; +$v = $m->cas($cas_token, 'cas_test', 0); +if ($v != true) { + echo "CAS failed"; } echo "OK\n"; diff --git a/tests/cas_multi.phpt b/tests/cas_multi.phpt index 240ecadd..f5b27460 100644 --- a/tests/cas_multi.phpt +++ b/tests/cas_multi.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached multi fetch cas & set cas --SKIPIF-- - + --FILE-- $v) { $m->delete($key); } -$cas_tokens = array(); $m->setMulti($data, 10); -$actual = $m->getMulti(array_keys($data), $cas_tokens); +$actual = $m->getMulti(array_keys($data), Memcached::GET_EXTENDED); -foreach ($data as $key => $v) { - if (is_null($cas_tokens[$key])) { +foreach ($actual as $key => $v) { + if (is_null($v['cas'])) { echo "missing cas token(s)\n"; echo "data: "; var_dump($data); echo "actual data: "; var_dump($actual); - echo "cas tokens: "; - var_dump($cas_tokens); return; } - $v = $m->cas($cas_tokens[$key], $key, 11); + $v = $m->cas($v['cas'], $key, 11); if (!$v) { - echo "Error setting key: $key value: 11 with CAS: ", $cas_tokens[$key], "\n"; + echo "Error setting key: $key value: 11 with CAS: ", $v['cas'], "\n"; return; } $v = $m->get($key); @@ -54,24 +51,6 @@ if (array_keys($actual) !== array_keys($data)) { return; } -$actual = $m->getMulti(array_keys($data), 2); -if (array_keys($actual) !== array_keys($data)) { - echo "Failed to getMulti \$cas_token passed by value (2)\n"; - return; -} - -$actual = $m->getMulti(array_keys($data), null); -if (array_keys($actual) !== array_keys($data)) { - echo "Failed to getMulti \$cas_token passed by value (null)\n"; - return; -} - -$actual = $m->getMulti(array_keys($data), $cas_tokens = array(2, 4)); -if (array_keys($actual) !== array_keys($data) || $cas_tokens !== array(2, 4)) { - echo "Failed to getMulti \$cas_token passed by value (\$cas_tokens = array(2, 4))\n"; - return; -} - echo "OK\n"; ?> diff --git a/tests/check_if_persistent.phpt b/tests/check_if_persistent.phpt index 9af75d23..1945923a 100644 --- a/tests/check_if_persistent.phpt +++ b/tests/check_if_persistent.phpt @@ -1,7 +1,7 @@ --TEST-- Check if persistent object is persistent --SKIPIF-- - --FILE-- --FILE-- + --FILE-- + --FILE-- + --FILE-- addServer($host, $port); - $memcached->flush (); + if ($memcached->flush() === false) { + return NULL; + } return $memcached; } @@ -64,3 +66,8 @@ function memc_create_combinations ($name, $serializer, $ignore_object_type = fal ), ); } + +function memc_get_version($memc) { + $version = $memc->getVersion(); + return array_pop($version); +} diff --git a/tests/construct.phpt b/tests/construct.phpt index c5c78977..49e508e7 100644 --- a/tests/construct.phpt +++ b/tests/construct.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached constructor --SKIPIF-- - + --FILE-- + --FILE-- +--FILE-- +getOption(Memcached::OPT_DISTRIBUTION) == Memcached::DISTRIBUTION_MODULA); +var_dump ($m->getOption(Memcached::OPT_BINARY_PROTOCOL) == false); +var_dump ($m->getOption(Memcached::OPT_CONNECT_TIMEOUT) != 0); + +ini_set('memcached.default_consistent_hash', true); +ini_set('memcached.default_binary_protocol', true); +ini_set('memcached.default_connect_timeout', 1212); + +$m = new Memcached(); +var_dump ($m->getOption(Memcached::OPT_DISTRIBUTION) == Memcached::DISTRIBUTION_CONSISTENT); +var_dump ($m->getOption(Memcached::OPT_BINARY_PROTOCOL) == true); +var_dump ($m->getOption(Memcached::OPT_CONNECT_TIMEOUT) == 1212); + +echo "OK"; + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +OK diff --git a/tests/deleted.phpt b/tests/deleted.phpt index 6b5bae1a..c3127cb0 100644 --- a/tests/deleted.phpt +++ b/tests/deleted.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached store & fetch type correctness --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->delete('foo'); var_dump($m->addByKey('foo', 'foo', 1, 10)); diff --git a/tests/experimental/addserver_unixdomain.phpt b/tests/experimental/addserver_unixdomain.phpt index aaec92ae..4848015d 100644 --- a/tests/experimental/addserver_unixdomain.phpt +++ b/tests/experimental/addserver_unixdomain.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::addServer() unix doamin socket --SKIPIF-- - + --CLEAN-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->setOption(Memcached::OPT_COMPRESSION, false); var_dump($m->setByKey('foo', 'foo', 'bar', 10)); diff --git a/tests/experimental/cas_bykey.phpt b/tests/experimental/cas_bykey.phpt index f33e24ad..0a9da94e 100644 --- a/tests/experimental/cas_bykey.phpt +++ b/tests/experimental/cas_bykey.phpt @@ -1,19 +1,19 @@ --TEST-- Memcached::casByKey() --SKIPIF-- - + --FILE-- addServer('127.0.0.1', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->delete('cas_test'); -$cas_token = null; $m->setByKey('keffe', 'cas_test', 10); -$v = $m->getbyKey('keffe', 'cas_test', null, $cas_token); +$v = $m->getbyKey('keffe', 'cas_test', null, Memcached::GET_EXTENDED); -if (is_null($cas_token)) { +$cas_token = $v["cas"]; +if (empty($cas_token)) { echo "Null cas token for key: cas_test value: 10\n"; return; } diff --git a/tests/experimental/cas_invalid_key.phpt b/tests/experimental/cas_invalid_key.phpt index c01d2c8d..6011c4b8 100644 --- a/tests/experimental/cas_invalid_key.phpt +++ b/tests/experimental/cas_invalid_key.phpt @@ -1,21 +1,21 @@ --TEST-- Memcached::cas() with strange key --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); error_reporting(0); var_dump($m->cas(0, '', true, 10)); echo $m->getResultMessage(), "\n"; -var_dump($m->cas(0, ' äö jas kjjhask d ', true, 10)); +var_dump($m->cas(0, ' äö jas kjjhask d ', true, 10)); # no spaces allowed echo $m->getResultMessage(), "\n"; --EXPECTF-- bool(false) A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE bool(false) -%rCLIENT ERROR|NOT FOUND%r +A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE diff --git a/tests/experimental/delete_bykey.phpt b/tests/experimental/delete_bykey.phpt index 87219539..807af8ca 100644 --- a/tests/experimental/delete_bykey.phpt +++ b/tests/experimental/delete_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::deleteByKey() --SKIPIF-- - + --FILE-- addServer('127.0.0.1', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->setByKey('keffe', 'eisaleeoo', "foo"); var_dump($m->getByKey('keffe', 'eisaleeoo')); @@ -21,7 +21,7 @@ var_dump($m->deleteByKey('keffe', '')); echo $m->getResultMessage(), "\n"; var_dump($m->deleteByKey('', 'keffe')); echo $m->getResultMessage(), "\n"; -var_dump($m->deleteByKey('keffe', 'äöåasäö åaösdäf asdf')); +var_dump($m->deleteByKey('keffe', 'äöåasäö åaösdäf asdf')); # no spaces allowed echo $m->getResultMessage(), "\n"; --EXPECTF-- string(3) "foo" @@ -37,4 +37,4 @@ A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE bool(false) NOT FOUND bool(false) -%rPROTOCOL ERROR|NOT FOUND|WRITE FAILURE|CLIENT ERROR%r +A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE diff --git a/tests/experimental/deletemulti_nonstringkeys.phpt b/tests/experimental/deletemulti_nonstringkeys.phpt index 69d56c78..2dac8920 100644 --- a/tests/experimental/deletemulti_nonstringkeys.phpt +++ b/tests/experimental/deletemulti_nonstringkeys.phpt @@ -1,12 +1,11 @@ --TEST-- Delete multi with integer keys --SKIPIF-- - + --FILE-- addServer('127.0.0.1', 11211, 1); -$m->addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $data = array( 1 => '1-data', diff --git a/tests/experimental/extreme_floats.phpt b/tests/experimental/extreme_floats.phpt index 0d530370..80ef568d 100644 --- a/tests/experimental/extreme_floats.phpt +++ b/tests/experimental/extreme_floats.phpt @@ -1,11 +1,11 @@ --TEST-- Extreme floats: max, min, Inf, -Inf, and NaN --SKIPIF-- - + --FILE-- addServer('127.0.0.1', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->set('float_inf', INF); $m->set('float_ninf', -INF); diff --git a/tests/experimental/fetch.phpt b/tests/experimental/fetch.phpt index e7eee9d3..13136c5b 100644 --- a/tests/experimental/fetch.phpt +++ b/tests/experimental/fetch.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached getDelayed() and fetch() with and without cas --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/experimental/fetch_badunserialize.phpt b/tests/experimental/fetch_badunserialize.phpt index 36a3049e..251d2ca5 100644 --- a/tests/experimental/fetch_badunserialize.phpt +++ b/tests/experimental/fetch_badunserialize.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::fetch() with bad unserialize --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public $serialize_throws = false; diff --git a/tests/experimental/fetchall_badunserialize.phpt b/tests/experimental/fetchall_badunserialize.phpt index 6108498a..e3ad3310 100644 --- a/tests/experimental/fetchall_badunserialize.phpt +++ b/tests/experimental/fetchall_badunserialize.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::fetch() with bad unserialize --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public $serialize_throws = false; diff --git a/tests/experimental/get.phpt b/tests/experimental/get.phpt index 34537fc4..308bda98 100644 --- a/tests/experimental/get.phpt +++ b/tests/experimental/get.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::get() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->delete('foo'); @@ -30,4 +30,4 @@ A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE string(4) "asdf" SUCCESS bool(false) -NOT FOUND +A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE diff --git a/tests/experimental/get_bykey.phpt b/tests/experimental/get_bykey.phpt index 220dd44a..a392aaeb 100644 --- a/tests/experimental/get_bykey.phpt +++ b/tests/experimental/get_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->set('foo', 1, 10); diff --git a/tests/experimental/get_bykey_cas.phpt b/tests/experimental/get_bykey_cas.phpt index ad94013e..501b13a7 100644 --- a/tests/experimental/get_bykey_cas.phpt +++ b/tests/experimental/get_bykey_cas.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getByKey() with CAS --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); function the_callback(Memcached $memc, $key, &$value) { echo "called\n"; @@ -59,7 +59,7 @@ string(4) "asdf" float(%d) SUCCESS bool(false) -NULL +float(0) NOT FOUND bool(false) NULL diff --git a/tests/experimental/get_udp.phpt b/tests/experimental/get_udp.phpt index 3e038e10..ad10a7cf 100644 --- a/tests/experimental/get_udp.phpt +++ b/tests/experimental/get_udp.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::set()/delete() UDP --SKIPIF-- - + --FILE-- addServer('127.0.0.1', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m_udp = new Memcached(); $m_udp->setOption(Memcached::OPT_USE_UDP, true); diff --git a/tests/experimental/getdelayed_badserver.phpt b/tests/experimental/getdelayed_badserver.phpt index dc0508fb..c4902174 100644 --- a/tests/experimental/getdelayed_badserver.phpt +++ b/tests/experimental/getdelayed_badserver.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::getDelayedByKey() with bad server --SKIPIF-- - + --FILE-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public $serialize_throws = false; diff --git a/tests/experimental/getdelayed_bykey.phpt b/tests/experimental/getdelayed_bykey.phpt index 4b3ddf6b..a29f646b 100644 --- a/tests/experimental/getdelayed_bykey.phpt +++ b/tests/experimental/getdelayed_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getDelayedByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/experimental/getdelayed_bykey_cas.phpt b/tests/experimental/getdelayed_bykey_cas.phpt index b874e739..43b19596 100644 --- a/tests/experimental/getdelayed_bykey_cas.phpt +++ b/tests/experimental/getdelayed_bykey_cas.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getDelayedByKey() with CAS --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/experimental/getdelayed_cbthrows.phpt b/tests/experimental/getdelayed_cbthrows.phpt index 482034b6..2b71b4b4 100644 --- a/tests/experimental/getdelayed_cbthrows.phpt +++ b/tests/experimental/getdelayed_cbthrows.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getDelayedByKey() with callback exception --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/experimental/getdelayed_nonstring_keys.phpt b/tests/experimental/getdelayed_nonstring_keys.phpt index 0e68344f..81363f3b 100644 --- a/tests/experimental/getdelayed_nonstring_keys.phpt +++ b/tests/experimental/getdelayed_nonstring_keys.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached getDelayed non string keys --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); class Bar { public function __toString() { diff --git a/tests/experimental/getmulti_badserver.phpt b/tests/experimental/getmulti_badserver.phpt index 21bbe192..a34825c2 100644 --- a/tests/experimental/getmulti_badserver.phpt +++ b/tests/experimental/getmulti_badserver.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::getMulti() bad server --SKIPIF-- - + --FILE-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public function __sleep() { diff --git a/tests/experimental/getmulti_bykey.phpt b/tests/experimental/getmulti_bykey.phpt index 70ae01c5..3f7a6f78 100644 --- a/tests/experimental/getmulti_bykey.phpt +++ b/tests/experimental/getmulti_bykey.phpt @@ -1,22 +1,20 @@ --TEST-- Memcached::getMultiByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->set('foo', 1, 10); $m->set('bar', 2, 10); $m->delete('baz'); -$cas = array(); -var_dump($m->getMultiByKey('foo', array('foo', 'bar', 'baz'), $cas, Memcached::GET_PRESERVE_ORDER)); +var_dump($m->getMultiByKey('foo', array('foo', 'bar', 'baz'), Memcached::GET_PRESERVE_ORDER)); echo $m->getResultMessage(), "\n"; -$cas = array(); -var_dump($m->getMultiByKey('foo', array(), $cas, Memcached::GET_PRESERVE_ORDER)); +var_dump($m->getMultiByKey('foo', array(), Memcached::GET_PRESERVE_ORDER)); echo $m->getResultMessage(), "\n"; --EXPECT-- diff --git a/tests/experimental/getmulti_empty.phpt b/tests/experimental/getmulti_empty.phpt index 7c8621da..6279104d 100644 --- a/tests/experimental/getmulti_empty.phpt +++ b/tests/experimental/getmulti_empty.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getMulti() with empty array --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $v = $m->getMulti(array()); var_dump($v); diff --git a/tests/experimental/getmulti_partial_error.phpt b/tests/experimental/getmulti_partial_error.phpt index dc4daa50..fa392e9e 100644 --- a/tests/experimental/getmulti_partial_error.phpt +++ b/tests/experimental/getmulti_partial_error.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getMulti() partial error --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $data = array(); for ($i = 0; $i < 1000; $i++) { diff --git a/tests/experimental/getversion.phpt b/tests/experimental/getversion.phpt index 509ff5d5..55d06714 100644 --- a/tests/experimental/getversion.phpt +++ b/tests/experimental/getversion.phpt @@ -1,13 +1,14 @@ --TEST-- Memcached::getVersion() --SKIPIF-- - + --FILE-- getVersion()); -$m->addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . "/config.inc"; +$m = memc_get_instance (); $stats = $m->getVersion(); var_dump($stats); diff --git a/tests/experimental/locale_float.phpt b/tests/experimental/locale_float.phpt index e6967bdb..c071d974 100644 --- a/tests/experimental/locale_float.phpt +++ b/tests/experimental/locale_float.phpt @@ -2,14 +2,15 @@ Float should not consider locale --SKIPIF-- addServer('127.0.0.1', 11211); + +include dirname(dirname(__FILE__)) . '/config.inc'; +$memcache = memc_get_instance (); setlocale(LC_NUMERIC, "fi_FI", 'sv_SV', 'nl_NL'); diff --git a/tests/experimental/moduleinfo.phpt b/tests/experimental/moduleinfo.phpt index a1f59b44..8605c11a 100644 --- a/tests/experimental/moduleinfo.phpt +++ b/tests/experimental/moduleinfo.phpt @@ -2,7 +2,7 @@ Memcached::phpinfo() --SKIPIF-- --FILE-- @@ -19,20 +19,4 @@ echo implode("\n", $array); --EXPECTF-- memcached memcached support => enabled -memcached.compression_factor => %f => %f -memcached.compression_threshold => %d => %d -memcached.compression_type => %s => %s -memcached.serializer => %s => %s -memcached.sess_binary => %d => %d -memcached.sess_connect_timeout => %d => %d -memcached.sess_consistent_hash => %d => %d -memcached.sess_lock_expire => %d => %d -memcached.sess_lock_max_wait => %d => %d -memcached.sess_lock_wait => %d => %d -memcached.sess_locking => %d => %d -memcached.sess_number_of_replicas => %d => %d -memcached.sess_prefix => %s => %s -memcached.sess_randomize_replica_read => %d => %d -memcached.sess_remove_failed => %d => %d -%rmemcached.use_sasl => %d => %d -|%rmemcached.store_retry_count => %d => %d \ No newline at end of file +%A diff --git a/tests/experimental/prepend_bykey.phpt b/tests/experimental/prepend_bykey.phpt index 85b12f98..87d3fd38 100644 --- a/tests/experimental/prepend_bykey.phpt +++ b/tests/experimental/prepend_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::appendByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $m->setOption(Memcached::OPT_COMPRESSION, false); var_dump($m->setByKey('foo', 'foo', 'bar', 10)); diff --git a/tests/experimental/replace_bykey.phpt b/tests/experimental/replace_bykey.phpt index 1cd858fb..b2f72836 100644 --- a/tests/experimental/replace_bykey.phpt +++ b/tests/experimental/replace_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::replaceByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); error_reporting(0); $m->delete('foo'); diff --git a/tests/experimental/serializer/serializer_php.phpt b/tests/experimental/serializer/serializer_php.phpt index ce3e7266..853d5bf4 100644 --- a/tests/experimental/serializer/serializer_php.phpt +++ b/tests/experimental/serializer/serializer_php.phpt @@ -2,12 +2,12 @@ Serializer basic --SKIPIF-- --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(dirname(__FILE__))) . '/config.inc'; +$m = memc_get_instance (); $serializer = Memcached::SERIALIZER_PHP; if (isset($_ENV['TEST_MEMC_SERIALIZER'])) { eval(sprintf('$serializer = %s;', $_ENV['TEST_MEMC_SERIALIZER'])); diff --git a/tests/experimental/serializer/serializer_php_bad_serialize.phpt b/tests/experimental/serializer/serializer_php_bad_serialize.phpt index 39ef4745..81e88b0c 100644 --- a/tests/experimental/serializer/serializer_php_bad_serialize.phpt +++ b/tests/experimental/serializer/serializer_php_bad_serialize.phpt @@ -2,7 +2,7 @@ Serializer: exception while serializing --SKIPIF-- addServer('localhost', 11211, 1); +include dirname(dirname(dirname(__FILE__))) . '/config.inc'; +$m = memc_get_instance (); $serializer = Memcached::SERIALIZER_PHP; if (isset($_ENV['TEST_MEMC_SERIALIZER'])) { eval(sprintf('$serializer = %s;', $_ENV['TEST_MEMC_SERIALIZER'])); diff --git a/tests/experimental/serializer/serializer_php_bad_unserialize.phpt b/tests/experimental/serializer/serializer_php_bad_unserialize.phpt index 86fba091..39d2cc37 100644 --- a/tests/experimental/serializer/serializer_php_bad_unserialize.phpt +++ b/tests/experimental/serializer/serializer_php_bad_unserialize.phpt @@ -2,10 +2,10 @@ Serializer: exception while unserializing --SKIPIF-- addServer('localhost', 11211, 1); +include dirname(dirname(dirname(__FILE__))) . '/config.inc'; +$m = memc_get_instance (); $serializer = Memcached::SERIALIZER_PHP; if (isset($_ENV['TEST_MEMC_SERIALIZER'])) { eval(sprintf('$serializer = %s;', $_ENV['TEST_MEMC_SERIALIZER'])); diff --git a/tests/experimental/serializer_igbinary.phpt b/tests/experimental/serializer_igbinary.phpt index a762121e..4d2360bc 100644 --- a/tests/experimental/serializer_igbinary.phpt +++ b/tests/experimental/serializer_igbinary.phpt @@ -2,7 +2,7 @@ Serialize igbinary --SKIPIF-- diff --git a/tests/experimental/session_gc.phpt b/tests/experimental/session_gc.phpt index 561901fc..61dc7d3d 100644 --- a/tests/experimental/session_gc.phpt +++ b/tests/experimental/session_gc.phpt @@ -2,7 +2,10 @@ Session expiration --SKIPIF-- --INI-- diff --git a/tests/experimental/set_bykey.phpt b/tests/experimental/set_bykey.phpt index fa6d6a4b..e1fec459 100644 --- a/tests/experimental/set_bykey.phpt +++ b/tests/experimental/set_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::setByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); var_dump($m->setByKey('foo', 'foo', 1, 10)); echo $m->getResultMessage(), "\n"; diff --git a/tests/experimental/set_comp_below_factor.phpt b/tests/experimental/set_comp_below_factor.phpt index f3eece2f..12e6d6a4 100644 --- a/tests/experimental/set_comp_below_factor.phpt +++ b/tests/experimental/set_comp_below_factor.phpt @@ -1,11 +1,11 @@ --TEST-- Compress below factor and fail to plain. --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); ini_set('memcached.compression_threshold', 100); ini_set('memcached.compression_factor', 10); diff --git a/tests/experimental/set_default_serializer.phpt b/tests/experimental/set_default_serializer.phpt index f9658736..4886e9b7 100644 --- a/tests/experimental/set_default_serializer.phpt +++ b/tests/experimental/set_default_serializer.phpt @@ -1,7 +1,7 @@ --TEST-- Set default serializer --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); ini_set('memcached.compression_factor', 0); $array = range(1, 20000, 1); diff --git a/tests/experimental/setmulti_badserialize.phpt b/tests/experimental/setmulti_badserialize.phpt index b12b1d3a..3bba314a 100644 --- a/tests/experimental/setmulti_badserialize.phpt +++ b/tests/experimental/setmulti_badserialize.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::setMultiByKey() with bad serialize --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public function __sleep() { @@ -42,6 +42,6 @@ try { var_dump($m->getByKey('kef', 'foo')); --EXPECT-- - +Memcached::setMultiByKey(): failed to set key foo 1234 int(10) diff --git a/tests/experimental/setmulti_bykey.phpt b/tests/experimental/setmulti_bykey.phpt index fad3db29..e3533c2f 100644 --- a/tests/experimental/setmulti_bykey.phpt +++ b/tests/experimental/setmulti_bykey.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::setMultiByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(dirname(__FILE__)) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/experimental/stats.phpt b/tests/experimental/stats.phpt index 4f416384..26d814ce 100644 --- a/tests/experimental/stats.phpt +++ b/tests/experimental/stats.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::getStats() --SKIPIF-- - + --FILE-- + --FILE-- --FILE-- diff --git a/tests/flush_buffers.phpt b/tests/flush_buffers.phpt index 989a4b63..a345edce 100644 --- a/tests/flush_buffers.phpt +++ b/tests/flush_buffers.phpt @@ -1,7 +1,7 @@ --TEST-- Test flushing buffers --SKIPIF-- - + --FILE-- +--FILE-- +set ($key1, 'hello1', 20); +$m->set ($key2, 'hello2', 20); + +$value = $m->get($key1); +$extended = $m->get($key1, null, Memcached::GET_EXTENDED); + +var_dump ($value); +var_dump ($extended); + +$values = $m->getMulti(array ($key1, $key2), Memcached::GET_PRESERVE_ORDER); +$extended = $m->getMulti(array ($key1, $key2), Memcached::GET_EXTENDED | Memcached::GET_PRESERVE_ORDER); + +var_dump ($values); +var_dump ($extended); +echo "OK"; + +--EXPECTF-- +string(6) "hello1" +array(3) { + ["value"]=> + string(6) "hello1" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) +} +array(2) { + ["memc.test.%s"]=> + string(6) "hello1" + ["memc.test.%s"]=> + string(6) "hello2" +} +array(2) { + ["memc.test.%s"]=> + array(3) { + ["value"]=> + string(6) "hello1" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) + } + ["memc.test.%s"]=> + array(3) { + ["value"]=> + string(6) "hello2" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) + } +} +OK \ No newline at end of file diff --git a/tests/getdelayed.phpt b/tests/getdelayed.phpt index 7479e97b..6a792fbc 100644 --- a/tests/getdelayed.phpt +++ b/tests/getdelayed.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached getDelayed callback --SKIPIF-- - + --FILE-- $v) { function myfunc() { $datas = func_get_args(); if (isset($datas[1])) { - unset($datas[1]['cas']); var_dump($datas[1]); } } -$m->getDelayed(array_keys($data), false, 'myfunc'); +$m->getDelayed(array_keys($data), true, 'myfunc'); ?> ---EXPECT-- -array(2) { +--EXPECTF-- +array(4) { ["key"]=> string(3) "foo" ["value"]=> string(8) "foo-data" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) } -array(2) { +array(4) { ["key"]=> string(3) "bar" ["value"]=> string(8) "bar-data" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) } -array(2) { +array(4) { ["key"]=> string(3) "baz" ["value"]=> string(8) "baz-data" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) } -array(2) { +array(4) { ["key"]=> string(3) "lol" ["value"]=> string(8) "lol-data" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) } -array(2) { +array(4) { ["key"]=> string(3) "kek" ["value"]=> string(8) "kek-data" -} + ["cas"]=> + int(%d) + ["flags"]=> + int(0) +} \ No newline at end of file diff --git a/tests/getmulti.phpt b/tests/getmulti.phpt index a32583ea..f8828cba 100644 --- a/tests/getmulti.phpt +++ b/tests/getmulti.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::getMulti() --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- array ( 'KEYHERE' => 'localhost', 11211, 3 ), ); @@ -14,41 +14,52 @@ var_dump($m->getServerList()); $m = new memcached(); $m->addServer('127.0.0.1', 11211); var_dump($m->getServerList()); + +echo "OK"; ?> ---EXPECTF-- +--EXPECT-- array(0) { } array(1) { [0]=> - array(2) { + array(3) { ["host"]=> string(9) "localhost" ["port"]=> int(11211) + ["type"]=> + string(3) "TCP" } } array(2) { [0]=> - array(2) { + array(3) { ["host"]=> string(9) "localhost" ["port"]=> int(11211) + ["type"]=> + string(3) "TCP" } [1]=> - array(2) { + array(3) { ["host"]=> string(9) "localhost" ["port"]=> int(11211) + ["type"]=> + string(3) "TCP" } } array(1) { [0]=> - array(2) { + array(3) { ["host"]=> string(9) "127.0.0.1" ["port"]=> int(11211) + ["type"]=> + string(3) "TCP" } } +OK \ No newline at end of file diff --git a/tests/gh_155.phpt b/tests/gh_155.phpt index c1f02bc6..a5b61d00 100644 --- a/tests/gh_155.phpt +++ b/tests/gh_155.phpt @@ -1,7 +1,11 @@ --TEST-- Test for bug 155 --SKIPIF-- - + --FILE-- + --FILE-- --FILE-- + --FILE-- + --FILE-- + --FILE-- delete('foo'); var_dump($m->increment('foo', 1)); +var_dump($m->getResultCode()); var_dump($m->decrement('foo', 1)); +var_dump($m->getResultCode()); var_dump($m->get('foo')); +var_dump($m->getResultCode()); echo "Normal\n"; $m->set('foo', 1); @@ -37,8 +40,11 @@ var_dump($m->get('foo')); --EXPECT-- Not there bool(false) +int(16) bool(false) +int(16) bool(false) +int(16) Normal int(1) int(2) diff --git a/tests/incrdecr_bykey.phpt b/tests/incrdecr_bykey.phpt index 9986b7dc..9c2db8dd 100644 --- a/tests/incrdecr_bykey.phpt +++ b/tests/incrdecr_bykey.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::incrementByKey() Memcached::decrementByKey() --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- increment('', 1)); var_dump($m->decrement('', 1)); +?> --EXPECT-- bool(false) bool(false) diff --git a/tests/invalid_options.phpt b/tests/invalid_options.phpt index af86531b..458b195e 100644 --- a/tests/invalid_options.phpt +++ b/tests/invalid_options.phpt @@ -1,7 +1,7 @@ --TEST-- Get version --SKIPIF-- - + --FILE-- + --FILE-- addServer(MEMC_SERVER_HOST, MEMC_SERVER_PORT); } +$m = new Memcached('hi', 'my_func'); $m = new Memcached('hi', 'my_func'); var_dump($m->getServerList()); @@ -21,11 +22,13 @@ echo "OK\n"; --EXPECTF-- array(1) { [0]=> - array(2) { + array(3) { ["host"]=> - string(9) "127.0.0.1" + string(9) "%s" ["port"]=> - int(11211) + int(%d) + ["type"]=> + string(3) "TCP" } } OK diff --git a/tests/invoke_callback_2.phpt b/tests/invoke_callback_2.phpt index 276d1c26..07e84f94 100644 --- a/tests/invoke_callback_2.phpt +++ b/tests/invoke_callback_2.phpt @@ -1,7 +1,7 @@ --TEST-- Use callback initializer --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- setOption(Memcached::OPT_VERIFY_KEY, 1); var_dump ($binary->set ('binary key with spaces', 'this is a test')); -var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); +var_dump ($binary->getResultCode () == Memcached::RES_SUCCESS); var_dump ($binary->set ('binarykeywithnewline' . PHP_EOL, 'this is a test')); var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); @@ -33,7 +34,7 @@ var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); echo "OK" . PHP_EOL; --EXPECT-- -bool(false) +bool(true) bool(true) bool(false) bool(true) diff --git a/tests/localserver.phpt b/tests/localserver.phpt index 22872070..28df5119 100644 --- a/tests/localserver.phpt +++ b/tests/localserver.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached local server test --SKIPIF-- - + --FILE-- + --FILE-- $v) { $m->set($k, $v, 3600); } -$null = null; $keys = array_keys($data); $keys[] = 'zoo'; -$got = $m->getMulti($keys, $null, Memcached::GET_PRESERVE_ORDER); +$got = $m->getMulti($keys, Memcached::GET_PRESERVE_ORDER); foreach ($got as $k => $v) { echo "$k $v\n"; diff --git a/tests/no-not-found.phpt b/tests/no-not-found.phpt index de96d3e4..21ca1e74 100644 --- a/tests/no-not-found.phpt +++ b/tests/no-not-found.phpt @@ -1,7 +1,7 @@ --TEST-- Test that correct return value is returned --SKIPIF-- - + --FILE-- addServer('localhost', 5555); // Server should not exist $result = $memcached->get('foo_not_exists'); var_dump ($result === Memcached::GET_ERROR_RETURN_VALUE); -$cas = 7; -$result = $memcached->get('foo_not_exists', null, $cas); +$result = $memcached->get('foo_not_exists'); var_dump ($result === Memcached::GET_ERROR_RETURN_VALUE); echo "OK\n"; diff --git a/tests/options.phpt b/tests/options.phpt index c987d355..ce895385 100644 --- a/tests/options.phpt +++ b/tests/options.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached options --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- + --FILE-- + --FILE-- delete('bar_foo'); echo $m->getResultCode(), "\n"; echo $m->getResultMessage(), "\n"; +$m->set ('asdf_a', 'aa'); $m->getMulti(array('asdf_a', 'jkhjkhjkb', 'nbahsdgc')); echo $m->getResultMessage(), "\n"; $code = $m->getResultCode(); diff --git a/tests/session_badconf_emptyprefix.phpt b/tests/session_badconf_emptyprefix.phpt index 74d681dd..fd19f1df 100644 --- a/tests/session_badconf_emptyprefix.phpt +++ b/tests/session_badconf_emptyprefix.phpt @@ -2,30 +2,27 @@ Session bad configurations, prefix --SKIPIF-- --INI-- memcached.sess_locking = on -memcached.sess_lock_wait = 150000 memcached.sess_prefix = "memc.sess.key." -session.save_handler = memcached +session.save_handler = "memcached" --FILE-- --INI-- diff --git a/tests/session_badconf_prefix.phpt b/tests/session_badconf_prefix.phpt index 5ad6b432..c42e8376 100644 --- a/tests/session_badconf_prefix.phpt +++ b/tests/session_badconf_prefix.phpt @@ -2,30 +2,23 @@ Session bad configurations, prefix --SKIPIF-- --INI-- -memcached.sess_locking = on -memcached.sess_lock_wait = 150000 -memcached.sess_prefix = "memc.sess.key." session.save_handler = memcached - --FILE-- --INI-- -memcached.sess_locking = on -memcached.sess_lock_wait = 150000 -memcached.sess_prefix = "memc.sess.key." session.save_handler = memcached - --FILE-- --INI-- -memcached.sess_locking = on -memcached.sess_lock_wait = 150000 -memcached.sess_prefix = "memc.sess.key." session.save_handler = memcached - --FILE-- +--INI-- +session.save_handler = memcached +--FILE-- +TRUE]); +$_SESSION['foo'] = 1; +session_write_close(); + +$_SESSION = NULL; + +var_dump($_SESSION); +session_start(); +var_dump($_SESSION); +session_write_close(); + +session_start(); +session_destroy(); + +session_start(); +var_dump($_SESSION); +session_write_close(); + + +--EXPECT-- +NULL +array(1) { + ["foo"]=> + int(1) +} +array(0) { +} diff --git a/tests/session_basic3.phpt b/tests/session_basic3.phpt new file mode 100644 index 00000000..d684fa35 --- /dev/null +++ b/tests/session_basic3.phpt @@ -0,0 +1,45 @@ +--TEST-- +Session basic open, write, destroy +--SKIPIF-- + +--INI-- +session.save_handler = memcached +--FILE-- + true +]); +var_dump($_SESSION); +session_write_close(); + +session_start(); +session_destroy(); + +session_start(); +var_dump($_SESSION); +session_write_close(); + + +--EXPECT-- +NULL +array(1) { + ["foo"]=> + int(1) +} +array(0) { +} diff --git a/tests/session_lock.phpt b/tests/session_lock.phpt new file mode 100644 index 00000000..79c32888 --- /dev/null +++ b/tests/session_lock.phpt @@ -0,0 +1,58 @@ +--TEST-- +Session lock +--SKIPIF-- + +--INI-- +memcached.sess_locking = true +memcached.sess_lock_wait_min = 500 +memcached.sess_lock_wait_max = 1000 +memcached.sess_lock_retries = 3 +memcached.sess_prefix = "memc.test." + +session.save_handler = memcached + +--FILE-- +addServer(MEMC_SERVER_HOST, MEMC_SERVER_PORT); + +ob_start(); +ini_set ('session.save_path', MEMC_SERVER_HOST . ':' . MEMC_SERVER_PORT); + +session_start(); +$session_id = session_id(); + +$_SESSION["test"] = "hello"; +session_write_close(); + +session_start(); +var_dump ($m->get ('memc.test.' . session_id())); +var_dump ($m->get ('memc.test.lock.' . session_id())); +session_write_close(); +var_dump ($m->get ('memc.test.lock.' . session_id())); + +// Test lock min / max +$m->set ('memc.test.lock.' . $session_id, '1'); + +$time_start = microtime(true); +session_start(); +$time = microtime(true) - $time_start; + +if (round ($time, 1) != 2.5) { + echo "Waited longer than expected: $time" . PHP_EOL; +} +echo "OK"; + +--EXPECTF-- +string(17) "test|s:5:"hello";" +string(1) "1" +bool(false) + +Warning: session_start(): Unable to clear session lock record in %s on line %d +OK diff --git a/tests/session_regenerate.phpt b/tests/session_regenerate.phpt new file mode 100644 index 00000000..bbd37fa2 --- /dev/null +++ b/tests/session_regenerate.phpt @@ -0,0 +1,26 @@ +--TEST-- +Session regenerate +--SKIPIF-- + +--INI-- +session.save_handler = memcached +--FILE-- + + --FILE-- set($key, $value, 360); +var_dump($m->set($key, $value, 360)); var_dump($m->get($key) === $value); ?> --EXPECT-- bool(true) +bool(true) diff --git a/tests/setmulti.phpt b/tests/setmulti.phpt index 02087614..2a156bde 100644 --- a/tests/setmulti.phpt +++ b/tests/setmulti.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::setMulti() --SKIPIF-- - + --FILE-- + --FILE-- +--FILE-- +getStats(); +$key = MEMC_SERVER_HOST . ':' . MEMC_SERVER_PORT; + +var_dump (count ($stats) === 1); +var_dump (isset ($stats[$key])); +var_dump (count ($stats[$key]) > 0); +var_dump (is_int ($stats[$key]['cmd_get'])); + +echo "OK"; +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +OK diff --git a/tests/touch_binary.phpt b/tests/touch_binary.phpt index b7399616..cc35e8fb 100644 --- a/tests/touch_binary.phpt +++ b/tests/touch_binary.phpt @@ -1,8 +1,10 @@ --TEST-- Touch in binary mode --SKIPIF-- - --FILE-- --FILE-- diff --git a/tests/types_igbinary_multi.phpt b/tests/types_igbinary_multi.phpt index 33f4df67..2a0823e0 100644 --- a/tests/types_igbinary_multi.phpt +++ b/tests/types_igbinary_multi.phpt @@ -2,7 +2,7 @@ Memcached multi store & multi fetch type and value correctness using igbinary serializer --SKIPIF-- --FILE-- diff --git a/tests/types_json.phpt b/tests/types_json.phpt index f5735f6d..2491fea7 100644 --- a/tests/types_json.phpt +++ b/tests/types_json.phpt @@ -2,7 +2,7 @@ Memcached store & fetch type and value correctness using JSON serializer --SKIPIF-- --FILE-- diff --git a/tests/types_json_multi.phpt b/tests/types_json_multi.phpt index 5535abb8..b6bc203e 100644 --- a/tests/types_json_multi.phpt +++ b/tests/types_json_multi.phpt @@ -2,8 +2,8 @@ Memcached multi store & multi fetch type and value correctness using JSON serializer --SKIPIF-- --FILE-- --FILE-- diff --git a/tests/types_msgpack_multi.phpt b/tests/types_msgpack_multi.phpt index dcf251e1..4ba42347 100644 --- a/tests/types_msgpack_multi.phpt +++ b/tests/types_msgpack_multi.phpt @@ -2,7 +2,7 @@ Memcached multi store & fetch type and value correctness using msgpack serializer --SKIPIF-- --FILE-- diff --git a/tests/types_php.phpt b/tests/types_php.phpt index f6baab5b..8d99f375 100644 --- a/tests/types_php.phpt +++ b/tests/types_php.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached store & fetch type and value correctness using PHP serializer --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- + --FILE-- get($key, null, Memcached::GET_EXTENDED)['flags']; +} + define ('FLAG_1', 1); define ('FLAG_2', 2); define ('FLAG_4', 4); define ('FLAG_32', 32); define ('FLAG_64', 64); define ('FLAG_TOO_LARGE', pow(2, 16)); -$x = 0; include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (array (Memcached::OPT_BINARY_PROTOCOL => true)); $key = uniqid ('udf_test_'); -echo "stored with flags" . PHP_EOL; +// Set with flags off +$m->set ($key, '1', 10); +$v = $m->get($key, null, Memcached::GET_EXTENDED); +var_dump($v); -$m->set ($key, '1', 10, FLAG_1 | FLAG_4 | FLAG_64); -$udf_flags = 0; -$value = $m->get ($key, null, $x, $udf_flags); +// Set flags on +$m->setOption(Memcached::OPT_USER_FLAGS, FLAG_1); +$m->set ($key, '1', 10); +$m->get($key); +check_flags(get_flags($m, $key), array(FLAG_1)); -check_flags ($udf_flags, array (FLAG_1, FLAG_4, FLAG_64)); +// Multiple flags +$m->setOption(Memcached::OPT_USER_FLAGS, FLAG_1 | FLAG_2 | FLAG_4); +$m->set ($key, '1', 10); +$m->get($key); +check_flags(get_flags($m, $key), array(FLAG_1, FLAG_2, FLAG_4)); -echo "stored without flags" . PHP_EOL; -$m->set ($key, '1'); -$value = $m->get ($key, null, $x, $udf_flags); +// Even more flags +$m->setOption(Memcached::OPT_USER_FLAGS, FLAG_1 | FLAG_2 | FLAG_4 | FLAG_32 | FLAG_64); +$m->set ($key, '1', 10); +$m->get($key); +check_flags(get_flags($m, $key), array(FLAG_1, FLAG_2, FLAG_4, FLAG_32, FLAG_64)); -var_dump ($udf_flags == 0); -$m->set ($key, '1', 10, FLAG_TOO_LARGE); +// User flags with get multi +$values = array( + uniqid ('udf_test_multi_') => "first", + uniqid ('udf_test_multi_') => "second", + uniqid ('udf_test_multi_') => "third", +); +$m->setOption(Memcached::OPT_USER_FLAGS, FLAG_2 | FLAG_4); +$m->setMulti($values); +$m->getMulti(array_keys($values)); +$flags = $m->getMulti(array_keys($values), Memcached::GET_EXTENDED); + +foreach (array_keys($values) as $key) { + check_flags($flags[$key]['flags'], array(FLAG_2, FLAG_4)); +} + +// User flags with compression on +$m->setOption(Memcached::OPT_USER_FLAGS, FLAG_1 | FLAG_2 | FLAG_4); $m->setOption(Memcached::OPT_COMPRESSION, true); $m->setOption(Memcached::OPT_COMPRESSION_TYPE, Memcached::COMPRESSION_FASTLZ); -$m->set ($key, str_repeat ("abcdef1234567890", 200), 10, FLAG_1 | FLAG_4 | FLAG_64); +$m->set ($key, '1', 10); +$m->get($key); +check_flags(get_flags($m, $key), array(FLAG_1, FLAG_2, FLAG_4)); -$udf_flags = 0; -$value_back = $m->get($key, null, null, $udf_flags); -check_flags ($udf_flags, array (FLAG_1, FLAG_4, FLAG_64)); +// Too large flags +$m->setOption(Memcached::OPT_USER_FLAGS, FLAG_TOO_LARGE); echo "DONE TEST\n"; ?> --EXPECTF-- -stored with flags +array(3) { + ["value"]=> + string(1) "1" + ["cas"]=> + int(%d) + ["flags"]=> + int(0) +} Flags OK -stored without flags -bool(true) - -Warning: Memcached::set(): udf_flags will be limited to 65535 in %s on line %d Flags OK +Flags OK +Flags OK +Flags OK +Flags OK +Flags OK + +Warning: Memcached::setOption(): MEMC_OPT_USER_FLAGS must be < 65535 in %s on line %d DONE TEST \ No newline at end of file diff --git a/tests/vbucket.phpt b/tests/vbucket.phpt index 31fcb7f2..f17eb94a 100644 --- a/tests/vbucket.phpt +++ b/tests/vbucket.phpt @@ -2,7 +2,7 @@ Memcached virtual buckets --SKIPIF-- --FILE-- @@ -45,4 +45,4 @@ NULL Warning: Memcached::setBucket(): the map must contain positive integers in %s on line %d bool(false) -OK \ No newline at end of file +OK diff --git a/tests/version.phpt b/tests/version.phpt index f9adcc52..06cd537b 100644 --- a/tests/version.phpt +++ b/tests/version.phpt @@ -1,7 +1,7 @@ --TEST-- Get version --SKIPIF-- - + --FILE-- - string(6) "%d.%d.%d" + string(%d) "%d.%d.%d" } -OK \ No newline at end of file +OK