diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index cc617d3d..00000000 --- a/.cvsignore +++ /dev/null @@ -1,46 +0,0 @@ -*.lo -*.la -.deps -.libs -Makefile -Makefile.fragments -Makefile.global -Makefile.objects -acinclude.m4 -aclocal.m4 -autom4te.cache -build -config.cache -config.guess -config.h -config.h.in -config.log -config.nice -config.status -config.sub -configure -configure.in -conftest -conftest.c -include -install-sh -libtool -ltmain.sh -missing -mkinstalldirs -modules -scan_makefile_in.awk -*.dsw -*.plg -*.opt -*.ncb -Release -Release_inline -Debug -Release_TS -Release_TSDbg -Release_TS_inline -Debug_TS -memcached*.tgz -run-tests.php -cscope.out diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..12301490 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 00000000..50a0c2fd --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,88 @@ + +name: Test on Ubuntu +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] + experimental: [false] + include: + - php: '8.6' + experimental: true + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + submodules: true + - name: Install PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: none, json, igbinary, msgpack + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install cmake memcached libsasl2-dev sasl2-bin zlib1g-dev + - name: Install libmemcached-dev + run: sudo apt-get install libmemcached-dev + - name: Start memcached daemons + run: | + export SASL_CONF_PATH="/tmp/sasl2" + 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: ${MEMCACHED_SASL_PWDB} + EOF + + echo "test" | /usr/sbin/saslpasswd2 -c memcached -a memcached -f "${MEMCACHED_SASL_PWDB}" + + # Run normal memcached + memcached -d -p 11211 + + # Run memcached on port 11212 with SASL support + memcached -S -d -p 11212 + - name: Build extension + run: | + phpize + ./configure \ + --enable-memcached-protocol=no \ + --enable-memcached-sasl \ + --enable-memcached-json \ + --enable-memcached-msgpack \ + --enable-memcached-igbinary + make + sudo make install + - name: Create test configuration + run: | + cat< tests/config.inc.local + /dev/null - if [ $? != 0 ]; then - echo "Missing $file from package.xml" - retval=1; - fi - done - return $retval -} - -function install_libmemcached() { - - wget "https://launchpad.net/libmemcached/1.0/$%7BLIBMEMCACHED_VERSION%7D/+download/libmemcached-$%7BLIBMEMCACHED_VERSION%7D.tar.gz" -O libmemcached-${LIBMEMCACHED_VERSION}.tar.gz - - tar xvfz libmemcached-${LIBMEMCACHED_VERSION}.tar.gz - pushd "libmemcached-${LIBMEMCACHED_VERSION}" - - local protocol_flag="" - if test "x$ENABLE_PROTOOCOL" = "xyes"; then - protocol_flag="--enable-libmemcachedprotocol" - fi - - ./configure --prefix="$LIBMEMCACHED_PREFIX" $protocol_flag LDFLAGS="-lpthread" - make - make install - popd -} - -function install_igbinary() { - git clone https://github.com/igbinary/igbinary.git - pushd igbinary - phpize - ./configure - make - make install - popd -} - -function install_msgpack() { - git clone https://github.com/msgpack/msgpack-php.git - pushd msgpack-php - phpize - ./configure - make - make install - popd -} - -function install_sasl() { - - 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 - - pushd memcached-1.4.15 - ./configure --enable-sasl --prefix="${HOME}/memcached" - make - make install - popd - - sudo apt-get install sasl2-bin - export SASL_CONF_PATH="${HOME}/sasl2" - - # Create config path - mkdir "${SASL_CONF_PATH}" - - # Create configuration - cat< "${SASL_CONF_PATH}/memcached.conf" -mech_list: PLAIN -plainlog_level: 5 -sasldb_path: ${SASL_CONF_PATH}/sasldb2 -EOF - - # Create password - echo "test" | /usr/sbin/saslpasswd2 -c memcached -a memcached -f "${SASL_CONF_PATH}/sasldb2" - - # Run memcached on port 11212 with SASL support - "${HOME}/memcached/bin/memcached" -S -d -p 11212 -} - -function build_php_memcached() { - pear package - mkdir "$PHP_MEMCACHED_BUILD_DIR" - tar xfz "memcached-${PHP_MEMCACHED_VERSION}.tgz" -C "$PHP_MEMCACHED_BUILD_DIR" - pushd "${PHP_MEMCACHED_BUILD_DIR}/memcached-${PHP_MEMCACHED_VERSION}" - phpize - - local protocol_flag="" - if test "x$ENABLE_PROTOCOL" = "xyes"; then - protocol_flag="--enable-memcached-protocol" - fi - - local sasl_flag="--disable-memcached-sasl" - if test "x$ENABLE_SASL" = "xyes"; then - 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 - make - make install - popd -} - -function create_memcached_test_configuration() { -cat< "${PHP_MEMCACHED_BUILD_DIR}/memcached-${PHP_MEMCACHED_VERSION}/tests/config.inc.local" -/dev/null`; do - echo "-- START ${i}"; - cat $i; - echo ""; - echo "-- END"; - done - popd - - return $retval; -} - -# Command line arguments -ACTION=$1 -LIBMEMCACHED_VERSION=$2 - -if test "x$ACTION" = "x"; then - echo "Usage: $0 " - exit 1 -fi - -if test "x$LIBMEMCACHED_VERSION" = "x"; then - echo "Usage: $0 " - exit 1 -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}" - -# Where to do the build -PHP_MEMCACHED_BUILD_DIR="/tmp/php-memcached-build" - -# Check whether to enable building with protoocol and sasl support -check_protocol_support -check_sasl_support - -echo "Enable protocol: $ENABLE_PROTOOCOL" -echo "Enable sasl: $ENABLE_SASL" - -set -e - -case $ACTION in - before_script) - # validate the package.xml - validate_package_xml || exit 1 - - # Install libmemcached version - install_libmemcached - - # Install igbinary extension - install_igbinary - - # install msgpack - install_msgpack - - # install SASL - if test "x$ENABLE_SASL" = "xyes"; then - install_sasl - fi - ;; - - script) - # Build the extension - build_php_memcached - - # Create configuration - if test "x$ENABLE_SASL" = "xyes"; then - create_memcached_test_configuration - fi - - # Run tests - set +e - run_memcached_tests || exit 1 - ;; - - *) - echo "Unknown action. Valid actions are: before_script and script" - exit 1 - ;; -esac - - - - - diff --git a/ChangeLog b/ChangeLog index b9b00ea5..31994e3b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,145 @@ memcached extension changelog -Version 2.2.0b1 ---------------- +Version 3.1.5 (2019-12-03) +-------------------------- + + * Fix build with PHP 7.4 release due to ulong typedef removal (#445) + +Version 3.1.4 (2019-10-06) +-------------------------- + + * Test on PHP 7.4 as well as 8.0 (#440) + * Fix segfault for unknown memcached flags (#431) + * Update documented defaults for sess_lock_retries( #432) + * Remove stray instances of the TSRMLS_CC macro for PHP 8 compatibility (#444) + +Version 3.1.3 (2018-12-24) +-------------------------- + + * Fix --disable-memcached-session by ifdef-ing session INI handler callbacks (#396, #420) + +Version 3.1.2 (2018-12-22) +-------------------------- + + * Fix --enable-memcached-protocol was set to yes by default, reverted to no (#418) + +Version 3.1.1 (2018-12-21) +-------------------------- + + * Fix --disable-memcached-sasl and --disable-memcached-session replaced by --enable variants (#416) + +Version 3.1.0 (2018-12-21) +-------------------------- + + New + * Support for PHP 7.3 (#385, #390) + * Add INI setting to choose session consistent hash (ketama or ketama_weighted) (#344, #392) + * Add support for libmemcached encryption (#345, #381) + * Add error reporting to session code (#165) + * Expose build configuration via PECL (#383) + + Fixes + * Fix hanging getStats() when binary protocol and non-blocking are both enabled (#348) + * Fix session persistence by checking memcached behavior values before setting (#379) + * Fix memcached.sess_persistent not working with memcached.sess_binary_protocol = On (#375) + * Configure warns if libmemcached needs sasl.h (#341, #380) + * Resolve various INI deviations in 3.0.3 (#351) + * Turn off sess_binary_protocol by default with older libmemcached (#330) + + Changes + * Impove Windows builds (#411) + * Support Homebrew ZLIB path (#410) + * Remove forgotten unused comment about -lpthread (#406) + * Git ignore configure.ac (#405) + * Replace obsolete macros AC_TRY_FOO with AC_FOO_IFELSE (#403) + * Remove unused defines (#354) + * Change session_lock and sess_prefix default ini values (#340, #350) + * Use new fast_zpp parameter parsing API (#302, #311) + +Version 3.0.4 (2017-11-20) +-------------------------- + + * Fix corrupted interned strings (#338) + * Fix unit tests for compatibility with PHP 7.2 (#358, #359) + * Fix \x0a in key name locks up connection and triggers a fatal timeout error (#339) + * Fix missing optional parameter getStats($type) (#337) + * Fix typo in skip message (#331) + * Fix build warnings (#329) + * Document GET_EXTENDED flag, add/rename other missing/misnamed constants (#335) + +Version 3.0.3 (2017-02-19) +-------------------------- + + * Fix crash when checking session data with older versions of libmemcached (#328) + * Fix crash due to zend_mm_corrupted when fetching session data (#327) + +Version 3.0.2 (2017-02-12) +-------------------------- + + * Update warning for touch command in binary protocol mode with libmemcached < 1.0.18 (#322) + * Add tests for 64-bit increment/decrement/incrementByKey/decrementByKey (#321) + * Fix tests for 32-bit increment/decrement/incrementByKey/decrementByKey (#319) + +Version 3.0.1 (2017-02-07) +-------------------------- + + * Add API entries for flushBuffers() and getAllKeys() (#316) + * Ignore specific errors from memcached_dump for getAllKeys() with newer memcached servers (#315) + * Fix compiling with memcached binary protocol enabled (#312) + * Restore php_libmemcached_compat with workaround for missing memcached_exists (#314) + * Travis CI purge old versions of memcached and libmemcached (#309) + +Version 3.0.0 (2017-01-27) +-------------------------- + * Support for PHP 7.0 and PHP 7.1 + * Fix compiling with SASL disabled + * Improved the test suite and Travis CI test runners + * Fix small string compression / decompression + * Fix increment/decrement with adjustments greater than 32-bit integers + * Fix session.gc_maxlifetime to handle both relative and absolute times + * Fix inability to reset OPT_PREFIX_KEY + +Version 3.0.0a1 (2016-02-22) +---------------------------- + Dependencies + * Support for PHP 7.0 + * Requires libmemcached 1.0 or higher + * Optional extension igbinary must 2.0 or higher + * Optional extension msgpack must be 2.0 or higher + + API + * The method signature of get, getByKey, getMulti, and getMultiByKey changed. + * get* and getMulti* commands no longer take cas or user flags parameters. + * get* and getMulti* commands now take the 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 + * The session memcached protocol config name was changed, and the default protocol was changed from text to binary protocol. If your memcached setup does not support the binary protocol(e.g. if using twemproxy), then set memcached.sess_binary_protocol = Off. (Previously called memcached.sess_binary) + * 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 + +Version 2.2.0 (2014-04-01) +-------------------------- + * Added the OPT_SERVER_TIMEOUT_LIMIT behaviour + +Version 2.2.0RC1 (2014-03-12) +----------------------------- + * Added the OPT_SERVER_TIMEOUT_LIMIT behaviour + * Fixes incorrect size when compressing serialized objects + * Fixes endianess of compressed values + +Version 2.2.0b1 (2013-10-28) +---------------------------- * Reinstate support for libememcached 0.x series * Added SASL support to session handler * Added Memcached::flushBuffers as per GH #78 @@ -20,28 +158,28 @@ Version 2.2.0b1 * Added Memcached::setBucket for virtual bucket support * Added support for msgpack serialization * Memcached::setSaslAuthData returns correct status on success - * Added support for user-defined flags in set and get operations + * Added support for user-defined flags in set and get operations -Version 2.1.0 -------------- +Version 2.1.0 (2012-08-06) +-------------------------- * Drop support for libmemcached 0.x series, now 1.0.x is required * Add support for virtual bucket distribution * Fix compilation against PHP 5.2 -Version 2.0.1 -------------- +Version 2.0.1 (2012-03-03) +-------------------------- * Fix embedded version number to be not -dev -Version 2.0.0 -------------- +Version 2.0.0 (2012-03-02) +-------------------------- * Add touch() and touchByKey() methods * Add resetServerList() and quit() methods * Support binary protocol in sessions * Make it work with libmemcached up to 1.0.4 * Test against PHP 5.4.0 -Version 2.0.0b2 ---------------- +Version 2.0.0b2 (2011-06-24) +---------------------------- * Add OPT_REMOVE_FAILED_SERVERS option. * Make it work with libmemcached up to 0.49. * Fix a case where invalid session ID could lock the script. @@ -61,8 +199,8 @@ Version 2.0.0b2 * Make increment/decrement initialize value when it is not available (when using binary protocol) -Version 2.0.0b1 ---------------- +Version 2.0.0b1 (2011-03-12) +---------------------------- * Add fastlz library that provides better/faster payload compression * Add configure switch to enable/disable JSON serialization support * Add getAllKeys() method @@ -84,23 +222,27 @@ Version 2.0.0b1 * Add 'on_new' callback to constructor * Add SASL support -Version 1.0.1 -------------- +Version 1.0.2 (2010-05-03) +-------------------------- + * Fix build for libmemcached-0.39 (memcached_server_list() issue) + +Version 1.0.1 (2010-03-11) +-------------------------- * Fix JSON API handling to account for PHP 5.2/5.3 version differences. * Add memcached.sess_locking, memcached.sess_lock_wait, and memcached.sess_prefix INI entries. * Add OPT_AUTO_EJECT_HOSTS option. -Version 1.0.0 -------------- +Version 1.0.0 (2009-07-04) +-------------------------- * First stable release. * Add getResultMessage() method. * Fix OPT_RECV_TIMEOUT definition. * Initialize Session lock wait to max execution time (if max execution time is unlimited, default to 30 seconds). -Version 0.2.0 -------------- +Version 0.2.0 (2009-06-04) +-------------------------- * Add JSON serializer support, requires PHP 5.2.10+. * Add HAVE_JSON and HAVE_IGBINARY class constants that indicate whether the respective serializers are available. @@ -114,25 +256,25 @@ Version 0.2.0 the cache when upgrading to this version. * Add several tests. -Version 0.1.5 -------------- +Version 0.1.5 (2009-03-31) +-------------------------- * Implement getVersion(). * Add support for preserving boolean value types. * Fix crash when child class does not call constructor. * Fix bug #16084 (Crash when addServers is called with an associative array). * ZTS compilation fixes. -Version 0.1.4 -------------- +Version 0.1.4 (2009-02-13) +-------------------------- * Fix compilation against PHP 5.3. * Add support for 'igbinary' serializer (Oleg Grenrus) -Version 0.1.3 -------------- +Version 0.1.3 (2009-02-06) +-------------------------- * Bludgeon bug #15896 (Memcached setMulti error) into submission. -Version 0.1.2 -------------- +Version 0.1.2 (2009-02-06) +-------------------------- * Fix bug #15896 (Memcached setMulti error). * Check for empty key in getServerByKey(). * Allow passing 'null' for callbacks. @@ -141,12 +283,12 @@ Version 0.1.2 * Allow only strings as the append/prepend value. * Remove expiration parameter from append/prepend. -Version 0.1.1 -------------- +Version 0.1.1 (2009-02-02) +-------------------------- * Add OPT_LIBKETAMA_COMPATIBLE option. * Implement addServers() method. * Swap internal compressed and serialized flags to be compatible with other clients. -Version 0.1.0 -------------- +Version 0.1.0 (2009-01-29) +-------------------------- * Initial release diff --git a/README.markdown b/README.markdown index e827e6fa..38dbd5bc 100644 --- a/README.markdown +++ b/README.markdown @@ -1,15 +1,15 @@ 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://github.com/php-memcached-dev/php-memcached/actions/workflows/build-and-test.yml/badge.svg?branch=master) Description ----------- -This extension uses libmemcached library to provide API for communicating with -memcached servers. +This is the [PECL memcached](https://pecl.php.net/package/memcached) extension, +using the libmemcached library to connect to memcached servers. -memcached is a high-performance, distributed memory object caching system, -generic in nature, but intended for use in speeding up dynamic web applications -by alleviating database load. +[memcached](https://memcached.org) is a high-performance, distributed memory +object caching system, generic in nature, but intended for use in speeding up +dynamic web applications by alleviating database load. Building -------- @@ -19,8 +19,34 @@ Building $ make $ make test -Resources ---------- - * [libmemcached](http://libmemcached.org/libMemcached.html) - * [memcached](http://memcached.org/) - * [igbinary](https://github.com/phadej/igbinary/) +Dependencies +------------ + +php-memcached 3.x: +* Supports PHP 7.0 - 8.3 or higher. +* Requires libmemcached 1.x or higher. +* Optionally supports igbinary 2.0 or higher. +* Optionally supports msgpack 2.0 or higher. + +php-memcached 2.x: +* Supports PHP 5.2 - 5.6. +* Requires libmemcached 0.44 or higher. +* Optionally supports igbinary 1.0 or higher. +* Optionally supports msgpack 0.5 or higher. + +[libmemcached](http://libmemcached.org/libMemcached.html) or the new +[libmemcached-awesome](https://github.com/awesomized/libmemcached) version +1.0.18 or higher is recommended for best performance and compatibility with +memcached servers. + +[igbinary](https://github.com/igbinary/igbinary) is a faster and more compact +binary serializer for PHP data structures. When installing php-memcached from +source code, the igbinary module must be installed first so that php-memcached +can access its C header files. Load both modules in your `php.ini` at runtime +to begin using igbinary. + +[msgpack](https://msgpack.org) is a faster and more compact data structure +representation that is interoperable with msgpack implementations for other +languages. When installing php-memcached from source code, the msgpack module +must be installed first so that php-memcached can access its C header files. +Load both modules in your `php.ini` at runtime to begin using msgpack. diff --git a/README.win32.txt b/README.win32.txt new file mode 100644 index 00000000..c717aa8a --- /dev/null +++ b/README.win32.txt @@ -0,0 +1,13 @@ +Build Steps for Windows +------------------------- + +Follow https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2#building_pecl_extensions + +- Add igbinary module to pecl directory if support desired +- Download/Compile libmemcached & add to deps folders (includes & lib). Lib should be named memcache.lib + - Important for 32bit: libmemcached must be built with _USE_32BIT_TIME_T defined (confirmed on PHP 7.2, VC15) + - https://github.com/yshurik/libmemcached-win/tree/1.0.18 is confirmed working + - To use the dll on the releases page you'd likely need to change the header files to use __time64_t instead of time_t +- Enable all options desired: --enable-memcached=shared --enable-memcached-session --enable-memcached-json + - for igbinary, add --enable-memcached-igbinary --enable-igbinary=shared +- Run nmake \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..3356c89d --- /dev/null +++ b/composer.json @@ -0,0 +1,68 @@ +{ + "name": "php-memcached/php-memcached", + "type": "php-ext", + "license": "PHP-3.01", + "description": "memcached extension based on libmemcached library ", + "require": { + "php": ">= 7.0.0" + }, + "suggest": { + "ext-igbinary": "igbinary is a faster and more compact binary serializer for PHP data structures.", + "ext-msgpack": "msgpack is a faster and more compact data structure representation that is interoperable with msgpack implementations for other languages." + }, + "php-ext": { + "extension-name": "memcached", + "configure-options": [ + { + "name": "enable-memcached", + "description": "Enable memcached support" + }, + { + "name": "with-libmemcached-dir", + "description": "Set the path to libmemcached install prefix.", + "needs-value": true + }, + { + "name": "enable-memcached-session", + "description": "Enable memcached session handler support" + }, + { + "name": "enable-memcached-igbinary", + "description": "Enable memcached igbinary serializer support" + }, + { + "name": "enable-memcached-json", + "description": "Enable memcached json serializer support" + }, + { + "name": "enable-memcached-msgpack", + "description": "Enable memcached msgpack serializer support" + }, + { + "name": "enable-memcached-sasl", + "description": "Enable memcached sasl support" + }, + { + "name": "enable-memcached-protocol", + "description": "Enable memcached protocol support" + }, + { + "name": "with-system-fastlz", + "description": "Use system FastLZ library" + }, + { + "name": "with-zstd", + "description": "Use system zstd library" + }, + { + "name": "with-zlib-dir", + "description": "Set the path to ZLIB install prefix.", + "needs-value": true + }, + { + "name": "enable-debug", + "description": "Compile with debugging symbols" + } + ] + } +} diff --git a/config.m4 b/config.m4 index 107f8cbe..0e4ef8cf 100644 --- a/config.m4 +++ b/config.m4 @@ -1,42 +1,43 @@ -dnl -dnl $ Id: $ dnl vim:se ts=2 sw=2 et: PHP_ARG_ENABLE(memcached, whether to enable memcached support, -[ --enable-memcached Enable memcached support]) +[ --enable-memcached Enable memcached support]) PHP_ARG_WITH(libmemcached-dir, for libmemcached, -[ --with-libmemcached-dir[=DIR] Set the path to libmemcached install prefix.], yes) +[ --with-libmemcached-dir=DIR Set the path to libmemcached install prefix.], yes) PHP_ARG_ENABLE(memcached-session, whether to enable memcached session handler support, -[ --disable-memcached-session Disable memcached session handler support], yes, no) +[ --enable-memcached-session Enable memcached session handler support], yes, no) PHP_ARG_ENABLE(memcached-igbinary, whether to enable memcached igbinary serializer support, -[ --enable-memcached-igbinary Enable memcached igbinary serializer support], no, no) +[ --enable-memcached-igbinary Enable memcached igbinary serializer support], no, no) PHP_ARG_ENABLE(memcached-json, whether to enable memcached json serializer support, -[ --enable-memcached-json Enable memcached json serializer support], no, no) +[ --enable-memcached-json Enable memcached json serializer support], no, no) PHP_ARG_ENABLE(memcached-msgpack, whether to enable memcached msgpack serializer support, -[ --enable-memcached-msgpack Enable memcached msgpack serializer support], no, no) +[ --enable-memcached-msgpack Enable memcached msgpack serializer support], no, no) PHP_ARG_ENABLE(memcached-sasl, whether to enable memcached sasl support, -[ --disable-memcached-sasl Disable memcached sasl support], yes, no) +[ --enable-memcached-sasl Enable memcached sasl support], yes, no) PHP_ARG_ENABLE(memcached-protocol, whether to enable memcached protocol support, -[ --enable-memcached-protocol Enable memcached protocoll support], no, no) +[ --enable-memcached-protocol Enable memcached protocol support], no, no) -PHP_ARG_WITH(system-fastlz, whether to use system FastLZ bibrary, -[ --with-system-fastlz Use system FastLZ bibrary], no, no) +PHP_ARG_WITH(system-fastlz, whether to use system FastLZ library, +[ --with-system-fastlz Use system FastLZ library], no, no) + +PHP_ARG_WITH(zstd, whether to use system zstd library, +[ --with-zstd Use system zstd library], no, no) if test -z "$PHP_ZLIB_DIR"; then PHP_ARG_WITH(zlib-dir, for ZLIB, -[ --with-zlib-dir[=DIR] Set the path to ZLIB install prefix.], no) +[ --with-zlib-dir=DIR Set the path to ZLIB install prefix.], no) fi if test -z "$PHP_DEBUG"; then AC_ARG_ENABLE(debug, - [ --enable-debug compile with debugging symbols],[ + [ --enable-debug Compile with debugging symbols],[ PHP_DEBUG=$enableval ],[ PHP_DEBUG=no ]) @@ -62,7 +63,7 @@ if test "$PHP_MEMCACHED" != "no"; then AC_MSG_ERROR([Can't find ZLIB headers under "$PHP_ZLIB_DIR"]) fi else - for i in /usr/local /usr; do + for i in /usr/local /usr/local/opt/zlib /usr; do if test -f "$i/include/zlib/zlib.h"; then PHP_ZLIB_DIR="$i" PHP_ZLIB_INCDIR="$i/include/zlib" @@ -106,72 +107,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 @@ -227,7 +185,7 @@ if test "$PHP_MEMCACHED" != "no"; then else AC_MSG_RESULT([$msgpack_inc_path]) fi - fi + fi AC_MSG_CHECKING([for memcached session support]) if test "$PHP_MEMCACHED_SESSION" != "no"; then @@ -298,47 +256,86 @@ if test "$PHP_MEMCACHED" != "no"; then PHP_EVAL_LIBLINE($PHP_LIBMEMCACHED_LIBS, MEMCACHED_SHARED_LIBADD) PHP_EVAL_INCLINE($PHP_LIBMEMCACHED_INCLUDES) - # - # Added -lpthread here because AC_TRY_LINK tests on CentOS 6 seem to fail with undefined reference to pthread_once - # ORIG_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $INCLUDES" + dnl # Always check if libmemcached was built with SASL support, + dnl # because it will require sasl.h even if not used here. + AC_CACHE_CHECK([for libmemcached sasl.h requirement], ac_cv_memc_sasl_support, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ + #if LIBMEMCACHED_WITH_SASL_SUPPORT + /* yes */ + #else + # error "no sasl support" + #endif + ]])], + [ac_cv_memc_sasl_support="yes"], + [ac_cv_memc_sasl_support="no"] + ) + ]) + + if test "$ac_cv_memc_sasl_support" = "yes"; then + AC_CHECK_HEADERS([sasl/sasl.h], [ac_cv_have_memc_sasl_h="yes"], [ac_cv_have_memc_sasl_h="no"]) + fi + + dnl # If libmemcached requires sasl.h but we can't find sasl.h, that's a hard error + dnl # regardless of the option --enable-memcached-sasl or --disable-memcached-sasl AC_MSG_CHECKING([whether to enable sasl support]) + if test "$ac_cv_memc_sasl_support" = "yes" && test "$ac_cv_have_memc_sasl_h" = "no"; then + AC_MSG_ERROR([no, libmemcached built with sasl required, but sasl.h not found.]) + fi + if test "$PHP_MEMCACHED_SASL" != "no"; then AC_MSG_RESULT(yes) - AC_CHECK_HEADERS([sasl/sasl.h], [ac_cv_have_memc_sasl_h="yes"], [ac_cv_have_memc_sasl_h="no"]) - - if test "$ac_cv_have_memc_sasl_h" = "yes"; then - - AC_CACHE_CHECK([whether libmemcached supports sasl], ac_cv_memc_sasl_support, [ - AC_TRY_COMPILE( - [ #include ], - [ - #if LIBMEMCACHED_WITH_SASL_SUPPORT - /* yes */ - #else - # error "no sasl support" - #endif - ], - [ ac_cv_memc_sasl_support="yes" ], - [ ac_cv_memc_sasl_support="no" ] - ) - ]) - - if test "$ac_cv_memc_sasl_support" = "yes"; then - PHP_CHECK_LIBRARY(sasl2, sasl_client_init, [PHP_ADD_LIBRARY(sasl2, 1, MEMCACHED_SHARED_LIBADD)]) - AC_DEFINE(HAVE_MEMCACHED_SASL, 1, [Have SASL support]) - else - AC_MSG_ERROR([no, libmemcached sasl support is not enabled. Run configure with --disable-memcached-sasl to disable this check]) - fi + if test "$ac_cv_memc_sasl_support" = "yes" && test "$ac_cv_have_memc_sasl_h" = "yes"; then + PHP_CHECK_LIBRARY(sasl2, sasl_client_init, [PHP_ADD_LIBRARY(sasl2, 1, MEMCACHED_SHARED_LIBADD)]) + AC_DEFINE(HAVE_MEMCACHED_SASL, 1, [Have SASL support]) else - AC_MSG_ERROR([no, sasl.h is not available. Run configure with --disable-memcached-sasl to disable this check]) + AC_MSG_ERROR([no, libmemcached built with sasl disabled. Run configure with --disable-memcached-sasl or update libmemcached with sasl support]) fi else 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_LINK_IFELSE( + [AC_LANG_PROGRAM([[#include ]], + [[memcached_exist (NULL, NULL, 0);]])], + [ac_cv_have_memcached_exist="yes"], + [ac_cv_have_memcached_exist="no"]) + ]) + + CFLAGS="$ORIG_CFLAGS" + LIBS="$ORIG_LIBS" + + CFLAGS="$CFLAGS $PHP_LIBMEMCACHED_INCLUDES" + LIBS="$LIBS $PHP_LIBMEMCACHED_LIBS" + + if test "$ac_cv_have_memcached_exist" = "yes"; then + AC_DEFINE(HAVE_MEMCACHED_EXIST, [1], [Whether memcached_exist is defined]) + fi + + AC_CACHE_CHECK([whether memcached_set_encoding_key is defined], ac_cv_have_memcached_set_encoding_key, [ + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#include ]], + [[memcached_set_encoding_key (NULL, NULL, 0);]])], + [ac_cv_have_memcached_set_encoding_key="yes"], + [ac_cv_have_memcached_set_encoding_key="no"]) + ]) + + CFLAGS="$ORIG_CFLAGS" + LIBS="$ORIG_LIBS" + + if test "$ac_cv_have_memcached_set_encoding_key" = "yes"; then + AC_DEFINE(HAVE_MEMCACHED_SET_ENCODING_KEY, [1], [Whether memcached_set_encoding_key is defined]) + fi + PHP_MEMCACHED_FILES="php_memcached.c php_libmemcached_compat.c g_fmt.c" if test "$PHP_SYSTEM_FASTLZ" != "no"; then @@ -351,6 +348,13 @@ if test "$PHP_MEMCACHED" != "no"; then PHP_MEMCACHED_FILES="${PHP_MEMCACHED_FILES} fastlz/fastlz.c" fi + if test "$PHP_ZSTD" != "no"; then + AC_CHECK_HEADERS([zstd.h], [ac_cv_have_zstd="yes"], [ac_cv_have_zstd="no"]) + PHP_CHECK_LIBRARY(zstd, ZSTD_compress, + [PHP_ADD_LIBRARY(zstd, 1, MEMCACHED_SHARED_LIBADD)], + [AC_MSG_ERROR(zstd library not found)]) + fi + if test "$PHP_MEMCACHED_SESSION" != "no"; then PHP_MEMCACHED_FILES="${PHP_MEMCACHED_FILES} php_memcached_session.c" fi @@ -361,13 +365,12 @@ if test "$PHP_MEMCACHED" != "no"; then AC_MSG_RESULT([enabled]) AC_CACHE_CHECK([whether libmemcachedprotocol is usable], ac_cv_have_libmemcachedprotocol, [ - AC_TRY_COMPILE( - [ #include ], - [ memcached_binary_protocol_callback_st s_test_impl; + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[memcached_binary_protocol_callback_st s_test_impl; s_test_impl.interface.v1.delete_object = 0; - ], - [ ac_cv_have_libmemcachedprotocol="yes" ], - [ ac_cv_have_libmemcachedprotocol="no" ] + ]])], + [ac_cv_have_libmemcachedprotocol="yes"], + [ac_cv_have_libmemcachedprotocol="no"] ) ]) diff --git a/config.w32 b/config.w32 index 380a9a12..76d4ee3c 100644 --- a/config.w32 +++ b/config.w32 @@ -1,18 +1,50 @@ -// $ Id: $ // vim:ft=javascript -ARG_WITH('memcached', 'libmemcached extension', 'no'); +ARG_ENABLE('memcached', 'libmemcached extension', 'no'); + +ARG_ENABLE('memcached-session', 'whether to enable memcached session handler support', 'no'); +ARG_ENABLE('memcached-igbinary', 'whether to enable memcached igbinary serializer support', 'no'); +ARG_ENABLE('memcached-json', 'whether to enable memcached json serializer support', 'no'); +ARG_ENABLE('memcached-msgpack', 'whether to enable memcached msgpack serializer support', 'no'); if (PHP_MEMCACHED == "yes") { - if (!CHECK_LIB("memcached.lib", "memcached", PHP_MEMCACHED)) { + if (!CHECK_LIB("memcached.lib;libmemcached.lib", "memcached", PHP_MEMCACHED)) { ERROR("memcached: library 'memcached' not found"); } if (!CHECK_HEADER_ADD_INCLUDE("libmemcached/memcached.h", "CFLAGS_MEMCACHED")) { ERROR("memcached: header 'libmemcached/memcached.h' not found"); } - EXTENSION("memcached", "memcached.c"); + + if (PHP_MEMCACHED_JSON != "no"){ + AC_DEFINE("HAVE_JSON_API",1); + } + + var memcached_extra_src = ""; + + if (PHP_MEMCACHED_SESSION != "no"){ + AC_DEFINE("HAVE_MEMCACHED_SESSION",1); + ADD_EXTENSION_DEP("memcached", "session", true) + memcached_extra_src += " php_memcached_session.c"; + } + + if (PHP_MEMCACHED_IGBINARY != "no"){ + AC_DEFINE("HAVE_MEMCACHED_IGBINARY",1); + ADD_EXTENSION_DEP("memcached", "igbinary", true); + if (!CHECK_HEADER_ADD_INCLUDE("igbinary.h", "CFLAGS_MEMCACHED")) { + ERROR("memcached: header 'igbinary.h' not found"); + } + } + if (PHP_MEMCACHED_MSGPACK != "no"){ + AC_DEFINE("HAVE_MEMCACHED_MSGPACK",1); + ADD_EXTENSION_DEP("memcached", "msgpack", true); + if (!CHECK_HEADER_ADD_INCLUDE("php_msgpack.h", "CFLAGS_MEMCACHED")) { + ERROR("memcached: header 'php_msgpack.h' not found"); + } + } + EXTENSION("memcached", "php_memcached.c php_libmemcached_compat.c g_fmt.c"+memcached_extra_src, null, " /DHAVE_SSIZE_T"); + ADD_SOURCES(configure_module_dirname+"\\fastlz", "fastlz.c", "memcached"); AC_DEFINE("HAVE_MEMCACHED", 1, "memcached support"); AC_DEFINE("MEMCACHED_EXPORTS", 1) } diff --git a/g_fmt.c b/g_fmt.c index 73f82fd0..f456790c 100644 --- a/g_fmt.c +++ b/g_fmt.c @@ -27,12 +27,17 @@ * // Teddy Grenman , 2010-05-18. */ -#include +#include char *php_memcached_g_fmt(register char *b, double x) { register int i, k; register char *s; - int decpt, j, sign; + int decpt, j; +#if PHP_VERSION_ID < 80100 + int sign; +#else + bool sign; +#endif char *b0, *s0, *se; b0 = b; diff --git a/memcached-api.php b/memcached-api.php index d22d0cf9..8ab03d4c 100644 --- a/memcached-api.php +++ b/memcached-api.php @@ -10,13 +10,13 @@ class Memcached { */ const OPT_HASH; - - const OPT_HASH_DEFAULT; + + const HASH_DEFAULT; const HASH_MD5; const HASH_CRC; - + const HASH_FNV1_64; const HASH_FNV1A_64; @@ -34,12 +34,16 @@ class Memcached { const DISTRIBUTION_MODULA; const DISTRIBUTION_CONSISTENT; - + const DISTRIBUTION_VIRTUAL_BUCKET; - const LIBKETAMA_COMPATIBLE; + const OPT_LIBKETAMA_COMPATIBLE; + + const OPT_LIBKETAMA_HASH; - const OPT_BUFFER_REQUESTS; + const OPT_TCP_KEEPALIVE; + + const OPT_BUFFER_WRITES; const OPT_BINARY_PROTOCOL; @@ -57,9 +61,9 @@ class Memcached { const OPT_DEAD_TIMEOUT; - const OPT_SND_TIMEOUT; + const OPT_SEND_TIMEOUT; - const OPT_RCV_TIMEOUT; + const OPT_RECV_TIMEOUT; const OPT_POLL_TIMEOUT; @@ -71,22 +75,36 @@ class Memcached { const OPT_AUTO_EJECT_HOSTS; - const OPT_NUMBER_OF_REPLICAS; - const OPT_NOREPLY; const OPT_VERIFY_KEY; - + + const OPT_USE_UDP; + + const OPT_NUMBER_OF_REPLICAS; + const OPT_RANDOMIZE_REPLICA_READS; + const OPT_REMOVE_FAILED_SERVERS; /** - * Class parameters + * Supported serializers */ const HAVE_JSON; const HAVE_IGBINARY; + const HAVE_MSGPACK; + + const HAVE_ENCODING; + + /** + * Feature support + */ + const HAVE_SESSION; + + const HAVE_SASL; + /** * Class options. */ @@ -107,18 +125,24 @@ class Memcached { const SERIALIZER_JSON_ARRAY; + const SERIALIZER_MSGPACK; + /** * Compression types */ - const COMPRESSION_TYPE_FASTLZ; + const COMPRESSION_FASTLZ; - const COMPRESSION_TYPE_ZLIB; + const COMPRESSION_ZLIB; /** - * Flags + * Flags for get and getMulti operations. */ + // Whether to preserve key order in the result const GET_PRESERVE_ORDER; + // Whether to fetch CAS token as well (use "gets"). + const GET_EXTENDED; + /** * Return values */ @@ -186,23 +210,71 @@ class Memcached { const RES_CONNECTION_SOCKET_CREATE_FAILURE; + const RES_E2BIG; + + const RES_KEY_TOO_BIG; + + const RES_SERVER_TEMPORARILY_DISABLED; + + const RES_SERVER_MEMORY_ALLOCATION_FAILURE; + + const RES_AUTH_PROBLEM; + + const RES_AUTH_FAILURE; + + const RES_AUTH_CONTINUE; + + /** Server callbacks, if compiled with --memcached-protocol */ + const ON_CONNECT; + const ON_ADD; + const ON_APPEND; + const ON_DECREMENT; + const ON_DELETE; + const ON_FLUSH; + const ON_GET; + const ON_INCREMENT; + const ON_NOOP; + const ON_PREPEND; + const ON_QUIT; + const ON_REPLACE; + const ON_SET; + const ON_STAT; + const ON_VERSION; + /** Constants used when compiled with --memcached-protocol */ + const RESPONSE_SUCCESS; + const RESPONSE_KEY_ENOENT; + const RESPONSE_KEY_EEXISTS; + const RESPONSE_E2BIG; + const RESPONSE_EINVAL; + const RESPONSE_NOT_STORED; + const RESPONSE_DELTA_BADVAL; + const RESPONSE_NOT_MY_VBUCKET; + const RESPONSE_AUTH_ERROR; + const RESPONSE_AUTH_CONTINUE; + const RESPONSE_UNKNOWN_COMMAND; + const RESPONSE_ENOMEM; + const RESPONSE_NOT_SUPPORTED; + const RESPONSE_EINTERNAL; + const RESPONSE_EBUSY; + const RESPONSE_ETMPFAIL; + public function __construct( $persistent_id = '', $on_new_object_cb = null ) {} - - public function get( $key, $cache_cb = null, &$cas_token = null, &$udf_flags = null ) {} - public function getByKey( $server_key, $key, $cache_cb = null, &$cas_token = null, &$udf_flags = null ) {} + public function get( $key, callable $cache_cb = null, $flags = 0) {} + + public function getByKey( $server_key, $key, callable $cache_cb = null, $flags = 0 ) {} - public function getMulti( array $keys, &$cas_tokens = null, $flags = 0, &$udf_flags = null ) {} + public function getMulti( array $keys, $flags = 0) {} - public function getMultiByKey( $server_key, array $keys, &$cas_tokens = null, $flags = 0, &$udf_flags = null ) {} + public function getMultiByKey( $server_key, array $keys, $flags = 0) {} public function getDelayed( array $keys, $with_cas = null, $value_cb = null ) {} public function getDelayedByKey( $server_key, array $keys, $with_cas = null, $value_cb = null ) {} public function fetch( ) {} - + public function fetchAll( ) {} public function set( $key, $value, $expiration = 0, $udf_flags = 0 ) {} @@ -250,7 +322,7 @@ public function increment( $key, $offset = 1, $initial_value = 0, $expiry = 0) { public function decrement( $key, $offset = 1, $initial_value = 0, $expiry = 0) {} public function getOption( $option ) {} - + public function setOption( $option, $value ) {} public function setOptions( array $options ) {} @@ -275,8 +347,12 @@ public function getLastDisconnectedServer( ) {} public function flush( $delay = 0 ) {} - public function getStats( ) {} - + public function flushBuffers( ) {} + + public function getStats( $type = null ) {} + + public function getAllKeys( ) {} + public function getVersion( ) {} public function getResultCode( ) {} @@ -289,6 +365,8 @@ public function isPristine( ) {} public function setSaslAuthData( $username, $password ) {} + public function setEncodingKey( $key ) {} + } class MemcachedException extends Exception { diff --git a/memcached.ini b/memcached.ini index 1996eb27..5decf399 100644 --- a/memcached.ini +++ b/memcached.ini @@ -2,77 +2,98 @@ ; Use session locking ; valid values: On, Off ; 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; +;memcached.sess_locking = On + +; 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 150. +;memcached.sess_lock_wait_min = 150; + +; The maximum time, in milliseconds, to wait between session lock attempts. +; Default is 150. +;memcached.sess_lock_wait_max = 150; + +; 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 -; use the memcached.sess_lock_max_wait setting. If that is -; also 0, max_execution_time will be used. -memcached.sess_lock_expire = 0; +; use PHP's max_execution_time. +;memcached.sess_lock_expire = 0; ; memcached session key prefix ; valid values are strings less than 219 bytes long ; the default value is "memc.sess.key." -memcached.sess_prefix = "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 +; memcached session consistent hash type +; if set to 'ketama', consistent hashing (libketama) is used +; for session handling (default for php-memcached 3.x) +; if set to 'ketama_weighted', weighted consistent hashing (libketama) is used +; for session handling (default for php-memcached 2.x) +;memcached.sess_consistent_hash_type = "ketama" + +; 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 + +; Set this value to enable the server be removed after +; configured number of continuous times connection failure. +;memcached.sess_server_failure_limit = 0 ; Write data to a number of additional memcached servers ; This is "poor man's HA" as libmemcached calls it. -; If this value is positive and sess_remove_failed is enabled +; If this value is positive and sess_remove_failed_servers is enabled ; when a memcached server fails the session will continue to be available ; from a replica. However, if the failed memcache server ; becomes available again it will read the session from there ; which could have old data or no data at all -memcached.sess_number_of_replicas = 0 +;memcached.sess_number_of_replicas = 0 -; memcached session binary mode -; libmemcached replicas only work if binary mode is enabled -memcached.sess_binary = Off +; Use the memcached binary protocol for memcached sessions (Instead of the text protocol) +; libmemcached replicas work only if binary mode is enabled. +; However, certain proxies (such as twemproxy) will work only if the binary protocol is disabled. +; In older versions of php-memcached, this setting was Off and was called memcached.sess_binary. +; Default is On with libmemcached 1.0.18 or newer. +; Default is Off with older version. +;memcached.sess_binary_protocol = On ; memcached session replica read randomize -memcached.sess_randomize_replica_read = 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 +;memcached.sess_connect_timeout = 1000 ; Session SASL username ; Both username and password need to be set for SASL to be enabled -; In addition to this memcached.use_sasl needs to be on -memcached.sess_sasl_username = NULL +;memcached.sess_sasl_username = NULL ; Session SASL password -memcached.sess_sasl_password = NULL +;memcached.sess_sasl_password = NULL ; Set the compression type ; valid values are: fastlz, zlib ; the default is fastlz -memcached.compression_type = "fastlz" +;memcached.compression_type = "fastlz" ; Compression factor ; Store compressed value only if the compression @@ -82,13 +103,13 @@ memcached.compression_type = "fastlz" ; plain_len > comp_len * factor ; ; the default value is 1.3 (23% space saving) -memcached.compression_factor = "1.3" +;memcached.compression_factor = "1.3" ; The compression threshold ; ; Do not compress serialized values below this threshold. ; the default is 2000 bytes -memcached.compression_threshold = 2000 +;memcached.compression_threshold = 2000 ; Set the default serializer for new memcached objects. ; valid values are: php, igbinary, json, json_array, msgpack @@ -103,16 +124,46 @@ memcached.compression_threshold = 2000 ; msgpack - a cross-language binary serializer ; ; 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 +;memcached.serializer = "igbinary" ; 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 +; the default is 0 +;memcached.store_retry_count = 0 + +; The maximum payload size in bytes that can be written. +; Writing a payload larger than the limit will result in RES_E2BIG error. +; Specifying 0 means no limit is enforced, though the server may still reject with RES_E2BIG. +; Default is 0. +;memcached.item_size_limit = 1000000 + +; 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 0ebc86f9..bfcd6edd 100644 --- a/package.xml +++ b/package.xml @@ -21,10 +21,28 @@ http://pear.php.net/dtd/package-2.0.xsd"> mkoppanen@php.net yes - 2014-04-01 + + Aaron Stone + sodabrew + aaron@serendipity.cx + yes + + + Remi Collet + remi + remi@php.net + yes + + + Michael Wallner + mike + mike@php.net + yes + + 2025-10-13 - 2.2.0 - 2.2.0 + 3.4.0 + 3.4.0 stable @@ -32,7 +50,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> PHP -- Added the OPT_SERVER_TIMEOUT_LIMIT behaviour +- @@ -47,6 +65,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + @@ -59,6 +80,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + @@ -71,7 +93,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -86,6 +110,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + @@ -97,9 +122,14 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + + @@ -114,10 +144,13 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + @@ -125,36 +158,438 @@ http://pear.php.net/dtd/package-2.0.xsd"> + - + + + + + + + + + + + + + + + + + - 5.2.0 - 6.0.0 - 6.0.0 + 7.0.0 1.4.0b1 + + + igbinary + 2.0 + + + msgpack + 2.0 + + memcached - + + + + + + + + + + + 2025-10-12 + + 3.4.0 + 3.4.0 + + + stable + stable + + PHP + +- Use Zend/zend_smart_string.h for PHP 8.5 compatibility (#574) +- Use zen_ce_exception for PHP 8.5 compatibility (#573) + + + + 2024-10-17 + + 3.3.0 + 3.3.0 + + + stable + stable + + PHP + +- Add #515 option to locally enforce payload size limit +- Add #539 zstd support +- Add #540 compression_level option +- Mark password as a sensitive param for PHP 8.2 +- Upgrade Windows libmemcached to v1.1.4 +- Fix Windows PHP 8 compatibility +- Fix #518 Windows msgpack support +- Fix #522 signed integer overflow +- Fix #523 incorrect PHP reflection type for Memcached::cas $cas_token +- Fix #546 don't check key automatically, unless client-side verify_key is enabled +- Fix #555 incompatible pointer types (32-bit) + + + + 2022-03-24 + + 3.2.0 + 3.2.0 + + + stable + stable + + PHP + +- PHP 8.0 and 8.1 support +- store_retry_count is no more set explicitly (#452) +- fix MemcachedServer (libmemcached-awesome is recommended) +- code cleanup +- fix windows build + + + + 2019-12-03 + + 3.1.5 + 3.0.0 + + + stable + stable + + PHP + +PHP 7.0 - 7.1 - 7.2 - 7.3 - 7.4 release of memcached extension. Note that support for +libmemcached 0.x series has been discontinued and the oldest actively tested +version is 1.0.8. It is highly recommended to use version 1.0.18 of +libmemcached. + +Fixes + * Fix build with PHP 7.4 release due to ulong typedef removal (#445) + + + + + stable + stable + + + 3.1.4 + 3.0.0 + + 2019-10-06 + +PHP 7.0 - 7.1 - 7.2 - 7.3 - 7.4 release of memcached extension. Note that support for +libmemcached 0.x series has been discontinued and the oldest actively tested +version is 1.0.8. It is highly recommended to use version 1.0.18 of +libmemcached. + +Fixes + * Test on PHP 7.4 as well as 8.0 (#440) + * Fix segfault for unknown memcached flags (#431) + * Update documented defaults for sess_lock_retries (#432) + * Remove stray instances of the TSRMLS_CC macro for PHP 8 compatibility (#444) + + + + + stable + stable + + + 3.1.3 + 3.0.0 + + 2018-12-22 + +PHP 7.0 - 7.1 - 7.2 - 7.3 release of memcached extension. Note that support for +libmemcached 0.x series has been discontinued and the oldest actively tested +version is 1.0.8. It is highly recommended to use version 1.0.18 of +libmemcached. + +Fixes + * Fix --disable-memcached-session by ifdef-ing session INI handler callbacks (#396, #420) + + + + + stable + stable + + + 3.1.2 + 3.0.0 + + 2018-12-22 + +PHP 7.0 - 7.1 - 7.2 - 7.3 release of memcached extension. Note that support for +libmemcached 0.x series has been discontinued and the oldest actively tested +version is 1.0.8. It is highly recommended to use version 1.0.18 of +libmemcached. + +Fixes + * Fix --enable-memcached-protocol was set to yes by default, reverted to no (#418) + + + + + stable + stable + + + 3.1.1 + 3.0.0 + + 2018-12-21 + +PHP 7.0 - 7.1 - 7.2 - 7.3 release of memcached extension. Note that support for +libmemcached 0.x series has been discontinued and the oldest actively tested +version is 1.0.8. It is highly recommended to use version 1.0.18 of +libmemcached. + +Fixes + * Fix --disable-memcached-sasl and --disable-memcached-session replaced by --enable variants (#416) + + + + + stable + stable + + + 3.1.0 + 3.0.0 + + 2018-12-21 + +PHP 7.0 - 7.1 - 7.2 - 7.3 release of memcached extension. Note that support for +libmemcached 0.x series has been discontinued and the oldest actively tested +version is 1.0.8. It is highly recommended to use version 1.0.18 of +libmemcached. + +New + * Support for PHP 7.3 (#385, #390) + * Add INI setting to choose session consistent hash (ketama or ketama_weighted) (#344, #392) + * Add support for libmemcached encryption (#345, #381) + * Add error reporting to session code (#165) + * Expose build configuration via PECL (#383) + +Fixes + * Fix hanging getStats() when binary protocol and non-blocking are both enabled (#348) + * Fix session persistence by checking memcached behavior values before setting (#379) + * Fix memcached.sess_persistent not working with memcached.sess_binary_protocol = On (#375) + * Configure warns if libmemcached needs sasl.h (#341, #380) + * Resolve various INI deviations in 3.0.3 (#351) + * Turn off sess_binary_protocol by default with older libmemcached (#330) + +Changes + * Impove Windows builds (#411) + * Support Homebrew ZLIB path (#410) + * Remove forgotten unused comment about -lpthread (#406) + * Git ignore configure.ac (#405) + * Replace obsolete macros AC_TRY_FOO with AC_FOO_IFELSE (#403) + * Remove unused defines (#354) + * Change session_lock and sess_prefix default ini values (#340, #350) + * Use new fast_zpp parameter parsing API (#302, #311) + + + + + stable + stable + + + 3.0.4 + 3.0.0 + + 2017-11-20 + +PHP 7.0 - 7.1 - 7.2 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. + +Fixes + * Fix corrupted interned strings (#338) + * Fix unit tests for compatibility with PHP 7.2 (#358, #359) + * Fix \x0a in key name locks up connection and triggers a fatal timeout error (#339) + * Fix missing optional parameter getStats($type) (#337) + * Fix typo in skip message (#331) + * Fix build warnings (#329) + * Document GET_EXTENDED flag, add/rename other missing/misnamed constants (#335) + + + + + stable + stable + + + 3.0.3 + 3.0.0 + + 2017-02-19 + +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. + +Fixes + * Fix crash when checking session data with older versions of libmemcached (#328) + * Fix crash due to zend_mm_corrupted when fetching session data (#327) + + + + + stable + stable + + + 3.0.2 + 3.0.0 + + 2016-02-12 + +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. + +Fixes + * Update warning for touch command in binary protocol mode with libmemcached below 1.0.18 (#322) + * Add tests for 64-bit increment/decrement/incrementByKey/decrementByKey (#321) + * Fix tests for 32-bit increment/decrement/incrementByKey/decrementByKey (#319) + + + + + stable + stable + + + 3.0.1 + 3.0.0 + + 2016-02-07 + +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. + +Fixes + * Add API entries for flushBuffers() and getAllKeys() (#316) + * Ignore specific errors from memcached_dump for getAllKeys() with newer memcached servers (#315) + * Fix compiling with memcached binary protocol enabled (#312) + * Restore php_libmemcached_compat with workaround for missing memcached_exists (#314) + * Travis CI purge old versions of memcached and libmemcached (#309) + + + + + stable + stable + + + 3.0.0 + 3.0.0 + + 2016-01-27 + +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. + +API + * The method signature of get, getByKey, getMulti, and getMultiByKey changed. + * get* and getMulti* commands no longer take cas or user flags parameters. + * get* and getMulti* commands now take the 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 + * The session memcached protocol config name was changed, and the default protocol was changed from text to binary protocol. If your memcached setup does not support the binary protocol(e.g. if using tw + * 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 + + + + + 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 @@ -162,20 +597,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> - Added the OPT_SERVER_TIMEOUT_LIMIT behaviour - - - betastable - 2.2.0b12.2.0 - 2013-10-28 - -- Reinstate support for libememcached 0.x series -- Added SASL support to session handler -- Added Memcached::flushBuffers as per GH #78 -- Fixes GH #54: Fixed UDP server adding with newer libmemcached -- Fixed PHP bug #65334: (Segfault if uncompress value failed) -- Fixes GH #14: get with cas token fails to fetch all results -- Fixes GH #69: compiling on CentOS 6.4 with libmemcached 1.0.17 - betastable diff --git a/php_libmemcached_compat.c b/php_libmemcached_compat.c index f238709e..4e294647 100644 --- a/php_libmemcached_compat.c +++ b/php_libmemcached_compat.c @@ -18,35 +18,42 @@ #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; + 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) { + zend_bool *is_persistent = memcached_get_user_data(memc); + pefree(value, *is_persistent); } + return rc; +#endif +} - servers = memcached_servers_parse (str); - - if (!servers) { - memcached_free (memc); - return NULL; +memcached_return php_memcached_touch(memcached_st *memc, const char *key, size_t key_len, time_t expiration) +{ +#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018 + if (memcached_behavior_get(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.18, please use ascii protocol or upgrade libmemcached"); } +#endif + return memcached_touch(memc, key, key_len, expiration); +} - rc = memcached_server_push (memc, servers); - memcached_server_free (servers); - - if (rc != MEMCACHED_SUCCESS) { - memcached_free (memc); - return NULL; +memcached_return php_memcached_touch_by_key(memcached_st *memc, const char *server_key, size_t server_key_len, const char *key, size_t key_len, time_t expiration) +{ +#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018 + if (memcached_behavior_get(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.18, please use ascii protocol or upgrade libmemcached"); } - return memc; #endif -} \ No newline at end of file + return memcached_touch_by_key(memc, server_key, server_key_len, key, key_len, expiration); +} + diff --git a/php_libmemcached_compat.h b/php_libmemcached_compat.h index ae400d9c..42a409dc 100644 --- a/php_libmemcached_compat.h +++ b/php_libmemcached_compat.h @@ -20,37 +20,12 @@ /* 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) -#endif - -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000002 -# define HAVE_MEMCACHED_TOUCH 1 -#endif +memcached_return php_memcached_touch(memcached_st *memc, const char *key, size_t key_len, time_t expiration); +memcached_return php_memcached_touch_by_key(memcached_st *memc, const char *server_key, size_t server_key_len, const char *key, size_t key_len, time_t expiration); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000017 -# define HAVE_MEMCACHED_INSTANCE_ST 1 -#endif - -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 -# define HAVE_LIBMEMCACHED_CHECK_CONFIGURATION 1 -#endif - -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000002 -# define HAVE_MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS 1 -#endif - -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 -# define HAVE_LIBMEMCACHED_MEMCACHED 1 -#endif - -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000018 -# define HAVE_MEMCACHED_BEHAVIOR_SERVER_TIMEOUT_LIMIT 1 -#endif - -#ifdef HAVE_MEMCACHED_INSTANCE_ST typedef const memcached_instance_st * php_memcached_instance_st; #else typedef memcached_server_instance_st php_memcached_instance_st; diff --git a/php_memcached.c b/php_memcached.c index c1bce93b..b78eb82a 100644 --- a/php_memcached.c +++ b/php_memcached.c @@ -14,12 +14,9 @@ +----------------------------------------------------------------------+ */ -/* $ Id: $ */ - /* TODO * - set LIBKETAMA_COMPATIBLE as the default? * - fix unserialize(serialize($memc)) - * - ability to set binary protocol for sessions */ #include "php_memcached.h" @@ -27,6 +24,9 @@ #include "php_memcached_server.h" #include "g_fmt.h" +#include +#include + #ifdef HAVE_MEMCACHED_SESSION # include "php_memcached_session.h" #endif @@ -37,25 +37,40 @@ #endif #include +#ifdef HAVE_ZSTD_H +#include +#endif + #ifdef HAVE_JSON_API # include "ext/json/php_json.h" #endif #ifdef HAVE_MEMCACHED_IGBINARY +#ifdef PHP_WIN32 +//Windows extensions are generally built together, +//so it wont be in the installed location +#include "igbinary.h" +#else # include "ext/igbinary/igbinary.h" #endif +#endif #ifdef HAVE_MEMCACHED_MSGPACK # include "ext/msgpack/php_msgpack.h" #endif -/* - * 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 -#endif +# include "ext/spl/spl_exceptions.h" + +static int le_memc; + +static int php_memc_list_entry(void) { + return le_memc; +} + +/**************************************** + Protocol parameters +****************************************/ +#define MEMC_OBJECT_KEY_MAX_LENGTH 250 /**************************************** Custom options @@ -65,6 +80,9 @@ #define MEMC_OPT_SERIALIZER -1003 #define MEMC_OPT_COMPRESSION_TYPE -1004 #define MEMC_OPT_STORE_RETRY_COUNT -1005 +#define MEMC_OPT_USER_FLAGS -1006 +#define MEMC_OPT_COMPRESSION_LEVEL -1007 +#define MEMC_OPT_ITEM_SIZE_LIMIT -1008 /**************************************** Custom result codes @@ -74,7 +92,7 @@ /**************************************** Payload value flags ****************************************/ -#define MEMC_CREATE_MASK(start, n_bits) (((1 << n_bits) - 1) << start) +#define MEMC_CREATE_MASK(start, n_bits) (((1U << n_bits) - 1) << start) #define MEMC_MASK_TYPE MEMC_CREATE_MASK(0, 4) #define MEMC_MASK_INTERNAL MEMC_CREATE_MASK(4, 12) @@ -95,11 +113,12 @@ #define MEMC_VAL_COMPRESSED (1<<0) #define MEMC_VAL_COMPRESSION_ZLIB (1<<1) #define MEMC_VAL_COMPRESSION_FASTLZ (1<<2) +#define MEMC_VAL_COMPRESSION_ZSTD (1<<3) -#define MEMC_VAL_GET_FLAGS(internal_flags) ((internal_flags & MEMC_MASK_INTERNAL) >> 4) -#define MEMC_VAL_SET_FLAG(internal_flags, internal_flag) ((internal_flags) |= ((internal_flag << 4) & MEMC_MASK_INTERNAL)) -#define MEMC_VAL_HAS_FLAG(internal_flags, internal_flag) ((MEMC_VAL_GET_FLAGS(internal_flags) & internal_flag) == internal_flag) -#define MEMC_VAL_DEL_FLAG(internal_flags, internal_flag) internal_flags &= ~((internal_flag << 4) & MEMC_MASK_INTERNAL) +#define MEMC_VAL_GET_FLAGS(internal_flags) (((internal_flags) & MEMC_MASK_INTERNAL) >> 4) +#define MEMC_VAL_SET_FLAG(internal_flags, internal_flag) ((internal_flags) |= (((internal_flag) << 4) & MEMC_MASK_INTERNAL)) +#define MEMC_VAL_HAS_FLAG(internal_flags, internal_flag) ((MEMC_VAL_GET_FLAGS(internal_flags) & (internal_flag)) == (internal_flag)) +#define MEMC_VAL_DEL_FLAG(internal_flags, internal_flag) (internal_flags &= (~(((internal_flag) << 4) & MEMC_MASK_INTERNAL))) /**************************************** User-defined flags @@ -111,128 +130,167 @@ /**************************************** "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; -#if HAVE_MEMCACHED_SASL - zend_bool has_sasl_data; + zend_bool is_persistent; + zend_bool compression_enabled; + zend_bool encoding_enabled; + + zend_long serializer; + zend_long compression_type; + zend_long compression_level; + + zend_long store_retry_count; + zend_long set_udf_flags; + zend_long item_size_limit; + +#ifdef HAVE_MEMCACHED_SASL + 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; -#ifdef HAVE_MEMCACHED_PROTOCOL +typedef struct { + size_t num_valid_keys; + + const char **mkeys; + size_t *mkeys_len; + + zend_string **strings; + +} php_memc_keys_t; typedef struct { - zend_object zo; - php_memc_proto_handler_t *handler; -} php_memc_server_t; + 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; + +#if PHP_VERSION_ID < 80000 +#define MEMC_METHOD_FETCH_OBJECT \ + intern = Z_MEMC_OBJ_P(object); \ + if (!intern->memc) { \ + php_error_docref(NULL, E_WARNING, "Memcached constructor was not called"); \ + return; \ + } \ + memc_user_data = (php_memc_user_data_t *) memcached_get_user_data(intern->memc); \ + (void)memc_user_data; /* avoid unused variable warning */ +#else +#define MEMC_METHOD_FETCH_OBJECT \ + intern = Z_MEMC_OBJ_P(object); \ + if (!intern->memc) { \ + zend_throw_error(NULL, "Memcached constructor was not called"); \ + RETURN_THROWS(); \ + } \ + memc_user_data = (php_memc_user_data_t *) memcached_get_user_data(intern->memc); \ + (void)memc_user_data; /* avoid unused variable warning */ #endif -enum { - MEMC_OP_SET, - MEMC_OP_TOUCH, - MEMC_OP_ADD, - MEMC_OP_REPLACE, - MEMC_OP_APPEND, - MEMC_OP_PREPEND -}; +static +zend_bool s_memc_valid_key_binary(zend_string *key) +{ + return memchr(ZSTR_VAL(key), '\n', ZSTR_LEN(key)) == NULL; +} -static zend_class_entry *memcached_ce = NULL; +static +uint32_t s_memc_object_key_max_length(php_memc_object_t *intern) { + memcached_return retval; + char *result; -static zend_class_entry *memcached_exception_ce = NULL; + result = memcached_callback_get(intern->memc, MEMCACHED_CALLBACK_PREFIX_KEY, &retval); + if (retval == MEMCACHED_SUCCESS && result) { + return MEMC_OBJECT_KEY_MAX_LENGTH - strlen(result); + } else { + return MEMC_OBJECT_KEY_MAX_LENGTH; + } +} -static zend_object_handlers memcached_object_handlers; +zend_bool s_memc_valid_key_ascii(zend_string *key, uint64_t verify_key) +{ + const char *str = ZSTR_VAL(key); + size_t i, len = ZSTR_LEN(key); + + if (verify_key) { + for (i = 0; i < len; i++) { + if (!isgraph(str[i]) || isspace(str[i])) + return 0; + } + } else { /* if key verification is disabled, only check for spaces to avoid injection issues */ + for (i = 0; i < len; i++) { + if (isspace(str[i])) + return 0; + } + } + return 1; +} + +#define MEMC_CHECK_KEY(intern, key) \ + if (UNEXPECTED(ZSTR_LEN(key) == 0 || \ + ZSTR_LEN(key) > s_memc_object_key_max_length(intern) || \ + (memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) \ + ? !s_memc_valid_key_binary(key) \ + : !s_memc_valid_key_ascii(key, memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_VERIFY_KEY)) \ + ))) { \ + intern->rescode = MEMCACHED_BAD_KEY_PROVIDED; \ + RETURN_FALSE; \ + } #ifdef HAVE_MEMCACHED_PROTOCOL -static zend_object_handlers memcached_server_object_handlers; -static zend_class_entry *memcached_server_ce = NULL; -#endif +typedef struct { + php_memc_proto_handler_t *handler; + zend_object zo; +} php_memc_server_t; -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 */ -}; +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)) -#if HAVE_SPL -static zend_class_entry *spl_ce_RuntimeException = NULL; +static zend_object_handlers memcached_server_object_handlers; +static zend_class_entry *memcached_server_ce = 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 +static zend_class_entry *memcached_ce = NULL; +static zend_class_entry *memcached_exception_ce = NULL; +static zend_object_handlers memcached_object_handlers; ZEND_DECLARE_MODULE_GLOBALS(php_memcached) @@ -243,82 +301,168 @@ 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; +#ifdef HAVE_ZSTD_H + } else if (!strcmp(ZSTR_VAL(new_value), "zstd")) { + MEMC_G(compression_type) = COMPRESSION_TYPE_ZSTD; +#endif } 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); +} + +#ifdef HAVE_MEMCACHED_SESSION +static +PHP_INI_MH(OnUpdateDeprecatedLockValue) +{ + if (ZSTR_LEN(new_value) > 0 && strcmp(ZSTR_VAL(new_value), "not set")) { + php_error_docref(NULL, E_DEPRECATED, "memcached.sess_lock_wait and memcached.sess_lock_max_wait are deprecated. Please update your configuration to use memcached.sess_lock_wait_min, memcached.sess_lock_wait_max and memcached.sess_lock_retries"); + } + return FAILURE; +} + +static +PHP_INI_MH(OnUpdateSessionPrefixString) +{ + if (new_value && ZSTR_LEN(new_value) > 0) { + if (ZSTR_LEN(new_value) > MEMCACHED_MAX_KEY) { + php_error_docref(NULL, E_WARNING, "memcached.sess_prefix too long (max: %d)", MEMCACHED_MAX_KEY - 1); + return FAILURE; + } + if (!s_memc_valid_key_ascii(new_value, 1)) { + php_error_docref(NULL, E_WARNING, "memcached.sess_prefix cannot contain whitespace or control characters"); + return FAILURE; + } + } + return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); +} + +static +PHP_INI_MH(OnUpdateConsistentHash) +{ + if (!new_value) { + MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA; + } else if (!strcmp(ZSTR_VAL(new_value), "ketama")) { + MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA; + } else if (!strcmp(ZSTR_VAL(new_value), "ketama_weighted")) { + MEMC_SESS_INI(consistent_hash_type) = MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED; + } else { + php_error_docref(NULL, E_WARNING, "memcached.sess_consistent_hash_type must be ketama or ketama_weighted"); + return FAILURE; + } + return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } +#endif // HAVE_MEMCACHED_SESSION + +#define MEMC_INI_ENTRY(key, default_value, update_fn, gkey) \ + STD_PHP_INI_ENTRY("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals) + +#define MEMC_INI_BOOL(key, default_value, update_fn, gkey) \ + STD_PHP_INI_BOOLEAN("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals) + +#define MEMC_INI_LINK(key, default_value, update_fn, gkey) \ + STD_PHP_INI_ENTRY_EX("memcached."key, default_value, PHP_INI_ALL, update_fn, memc.gkey, zend_php_memcached_globals, php_memcached_globals, display_link_numbers) + +#define MEMC_SESSION_INI_ENTRY(key, default_value, update_fn, gkey) \ + STD_PHP_INI_ENTRY("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals) + +#define MEMC_SESSION_INI_BOOL(key, default_value, update_fn, gkey) \ + STD_PHP_INI_BOOLEAN("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals) + +#define MEMC_SESSION_INI_LINK(key, default_value, update_fn, gkey) \ + STD_PHP_INI_ENTRY_EX("memcached.sess_"key, default_value, PHP_INI_ALL, update_fn, session.gkey, zend_php_memcached_globals, php_memcached_globals, display_link_numbers) + /* {{{ INI entries */ PHP_INI_BEGIN() + #ifdef HAVE_MEMCACHED_SESSION - 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) + MEMC_SESSION_INI_BOOL ("locking", "1", OnUpdateBool, lock_enabled) + MEMC_SESSION_INI_ENTRY("lock_wait_min", "150", OnUpdateLongGEZero, lock_wait_min) + MEMC_SESSION_INI_ENTRY("lock_wait_max", "150", OnUpdateLongGEZero, lock_wait_max) + MEMC_SESSION_INI_ENTRY("lock_retries", "5", OnUpdateLong, lock_retries) + MEMC_SESSION_INI_ENTRY("lock_expire", "0", OnUpdateLongGEZero, lock_expiration) +#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018 + MEMC_SESSION_INI_BOOL ("binary_protocol", "0", OnUpdateBool, binary_protocol_enabled) +#else + MEMC_SESSION_INI_BOOL ("binary_protocol", "1", OnUpdateBool, binary_protocol_enabled) #endif + MEMC_SESSION_INI_BOOL ("consistent_hash", "1", OnUpdateBool, consistent_hash_enabled) + MEMC_SESSION_INI_ENTRY("consistent_hash_type", "ketama", OnUpdateConsistentHash, consistent_hash_name) + MEMC_SESSION_INI_ENTRY("number_of_replicas", "0", OnUpdateLongGEZero, number_of_replicas) + MEMC_SESSION_INI_BOOL ("randomize_replica_read", "0", OnUpdateBool, randomize_replica_read_enabled) + MEMC_SESSION_INI_BOOL ("remove_failed_servers", "0", OnUpdateBool, remove_failed_servers_enabled) + MEMC_SESSION_INI_ENTRY("server_failure_limit", "0", OnUpdateLongGEZero, server_failure_limit) + MEMC_SESSION_INI_LINK ("connect_timeout", "0", OnUpdateLong, connect_timeout) + + MEMC_SESSION_INI_ENTRY("sasl_username", "", OnUpdateString, sasl_username) + MEMC_SESSION_INI_ENTRY("sasl_password", "", OnUpdateString, sasl_password) + MEMC_SESSION_INI_BOOL ("persistent", "0", OnUpdateBool, persistent_enabled) + MEMC_SESSION_INI_ENTRY("prefix", "memc.sess.key.", OnUpdateSessionPrefixString, prefix) + + /* Deprecated */ + STD_PHP_INI_ENTRY("memcached.sess_lock_wait", "not set", PHP_INI_ALL, OnUpdateDeprecatedLockValue, no_effect, zend_php_memcached_globals, php_memcached_globals) + STD_PHP_INI_ENTRY("memcached.sess_lock_max_wait", "not set", PHP_INI_ALL, OnUpdateDeprecatedLockValue, no_effect, zend_php_memcached_globals, php_memcached_globals) + #endif - 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_level", "3", OnUpdateLong, compression_level) + 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", "0", OnUpdateLong, store_retry_count) + MEMC_INI_ENTRY("item_size_limit", "0", OnUpdateLongGEZero, item_size_limit) + + MEMC_INI_BOOL ("default_consistent_hash", "0", OnUpdateBool, default_behavior.consistent_hash_enabled) + MEMC_INI_BOOL ("default_binary_protocol", "0", OnUpdateBool, default_behavior.binary_protocol_enabled) + MEMC_INI_LINK ("default_connect_timeout", "0", OnUpdateLong, default_behavior.connect_timeout) + PHP_INI_END() /* }}} */ +#undef MEMC_INI_BOOL +#undef MEMC_INI_LINK +#undef MEMC_INI_ENTRY +#undef MEMC_SESSION_INI_BOOL +#undef MEMC_SESSION_INI_LINK +#undef MEMC_SESSION_INI_ENTRY + /**************************************** Forward declarations ****************************************/ -static 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); @@ -326,563 +470,1247 @@ 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() +{ +#ifdef HAVE_MEMCACHED_SASL + if (MEMC_G(sasl_initialised)) { + return 1; + } + if (sasl_client_init(NULL) != SASL_OK) { + php_error_docref(NULL, E_ERROR, "Failed to initialize SASL library"); + return 0; + } + return 1; +#else + php_error_docref(NULL, E_ERROR, "Memcached not built with sasl support"); + return 0; +#endif +} -char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache 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), ZSTR_VAL(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; - } - } - i_obj->obj = m_obj; - } + case IS_STRING: + { + uint64_t val; + char *end; - i_obj->is_persistent = is_persistent; + errno = 0; + val = (uint64_t) strtoull (Z_STRVAL_P (cas), &end, 0); - if (!m_obj) { - m_obj = pecalloc(1, sizeof(*m_obj), is_persistent); - if (m_obj == NULL) { - if (plist_key) { - efree(plist_key); + if (*end || (errno == ERANGE && val == UINT64_MAX) || (errno != 0 && val == 0)) { + php_error_docref(NULL, E_ERROR, "Failed to unmarshall cas token"); + return 0; } - php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory: cannot allocate handle"); - /* not reached */ + return val; } + break; + } + return 0; +} - 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 */ - } - } - 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); +/**************************************** + Iterate over memcached results and mget +****************************************/ - i_obj->obj = m_obj; - i_obj->is_pristine = 1; +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; - 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); - } + memcached_result_create(intern->memc, &result); - i_obj->obj = NULL; - if (is_persistent) { - php_memc_destroy(m_obj, is_persistent TSRMLS_CC); - } + do { + result_ptr = memcached_fetch_result(intern->memc, &result, &rc); - return; - } + if (s_memcached_return_is_error(rc, 0)) { + status = rc; } - if (is_persistent) { - zend_rsrc_list_entry le; + /* nothing returned */ + if (!result_ptr) { + break; + } + else { + zend_string *key; + zval val, zcas; + zend_bool retval; + + uint64_t cas; + uint32_t flags; + + const char *res_key; + size_t res_key_len; + + if (!s_memcached_result_to_zval(intern->memc, &result, &val)) { + if (EG(exception)) { + status = MEMC_RES_PAYLOAD_FAILURE; + memcached_quit(intern->memc); + break; + } + status = MEMCACHED_SOME_ERRORS; + continue; + } + + res_key = memcached_result_key_value(&result); + res_key_len = memcached_result_key_length(&result); + cas = memcached_result_cas(&result); + flags = memcached_result_flags(&result); - 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); + + memcached_result_free(&result); + return status; +} + +static +zend_bool php_memc_mget_apply(php_memc_object_t *intern, zend_string *server_key, php_memc_keys_t *keys, + php_memc_result_apply_fn result_apply_fn, zend_bool with_cas, void *context) +{ + memcached_return status; + int mget_status; + uint64_t orig_cas_flag = 0; + + // Reset status code + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + + if (!keys->num_valid_keys) { + intern->rescode = MEMCACHED_BAD_KEY_PROVIDED; + return 0; } - if (plist_key) { - efree(plist_key); + if (with_cas) { + orig_cas_flag = memcached_behavior_get (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); + + if (!orig_cas_flag) { + memcached_behavior_set (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); + } + } + + if (server_key) { + status = memcached_mget_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), keys->mkeys, keys->mkeys_len, keys->num_valid_keys); + } else { + status = memcached_mget(intern->memc, keys->mkeys, keys->mkeys_len, keys->num_valid_keys); + } + + /* Need to handle result code before restoring cas flags, would mess up errno */ + mget_status = s_memc_status_handle_result_code(intern, status); + + if (with_cas && !orig_cas_flag) { + memcached_behavior_set (intern->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); + } + + /* Return on failure codes */ + if (mget_status == FAILURE) { + return 0; + } + + if (!result_apply_fn) { + /* no callback, for example getDelayed */ + return 1; } + + status = php_memc_result_apply(intern, result_apply_fn, 0, context); + + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + return 0; + } + + return 1; } -/* }}} */ -/* {{{ 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) +/**************************************** + 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) { - php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + 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]); + } + + 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; } -/* }}} */ -/* {{{ 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) +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) { - php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + 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; } -/* }}} */ -/* {{{ -- php_memc_get_impl */ -static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) +/**************************************** + Wrapper for setting from zval +****************************************/ + +static +zend_bool s_compress_value (php_memc_compression_type compression_type, zend_long compression_level, zend_string **payload_in, uint32_t *flags) { - 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; + /* status */ + zend_bool compress_status = 0; + zend_string *payload = *payload_in; + uint32_t compression_type_flag = 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; + /* Additional 5% for the data */ + size_t buffer_size = (size_t) (((double) ZSTR_LEN(payload) * 1.05) + 1.0); + char *buffer = emalloc(buffer_size); + + /* Store compressed size here */ + size_t compressed_size = 0; + uint32_t original_size = ZSTR_LEN(payload); + + switch (compression_type) { + + case COMPRESSION_TYPE_FASTLZ: + { + compressed_size = fastlz_compress(ZSTR_VAL(payload), ZSTR_LEN(payload), buffer); + + if (compressed_size > 0) { + compress_status = 1; + compression_type_flag = MEMC_VAL_COMPRESSION_FASTLZ; + } } - } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f!zz", &key, &key_len, - &fci, &fcc, &cas_token, &udf_flags) == FAILURE) { - return; + break; + +#ifdef HAVE_ZSTD_H + case COMPRESSION_TYPE_ZSTD: + { + compressed_size = ZSTD_compress((void *)buffer, buffer_size, ZSTR_VAL(payload), ZSTR_LEN(payload), compression_level); + + if (compression_level < -22) { + compression_level = -22; + } else if (compression_level > 22) { + compression_level = 22; + } + + if (!ZSTD_isError(compressed_size)) { + compress_status = 1; + compression_type_flag = MEMC_VAL_COMPRESSION_ZSTD; + } } + break; +#endif + + case COMPRESSION_TYPE_ZLIB: + { + unsigned long cs = compressed_size = buffer_size; + + if (compression_level < 0) { + compression_level = 0; + } else if (compression_level > 9) { + compression_level = 9; + } + + int status = compress2((Bytef *) buffer, &cs, (Bytef *) ZSTR_VAL(payload), ZSTR_LEN(payload), compression_level); + + if (status == Z_OK) { + compressed_size = cs; + compress_status = 1; + compression_type_flag = MEMC_VAL_COMPRESSION_ZLIB; + } + } + break; + + default: + compress_status = 0; + break; } - MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + /* This means the value was too small to be compressed and ended up larger */ + if (ZSTR_LEN(payload) <= (compressed_size * MEMC_G(compression_factor))) { + compress_status = 0; + } - if (key_len == 0 || strchr(key, ' ')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FROM_GET; + /* Replace the payload with the compressed copy */ + if (compress_status) { + MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSED | compression_type_flag); + payload = zend_string_realloc(payload, compressed_size + sizeof(uint32_t), 0); + + /* Copy the uin32_t at the beginning */ + memcpy(ZSTR_VAL(payload), &original_size, sizeof(uint32_t)); + memcpy(ZSTR_VAL(payload) + sizeof (uint32_t), buffer, compressed_size); + efree(buffer); + + zend_string_forget_hash_val(payload); + *payload_in = payload; + + return 1; } - keys[0] = key; - key_lens[0] = key_len; + /* Original payload was not modified */ + efree(buffer); + return 0; +} + +static +zend_bool s_serialize_value (php_memc_serializer_type serializer, zval *value, smart_str *buf, uint32_t *flags) +{ + switch (serializer) { - uint64_t orig_cas_flag; - orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); + /* + Igbinary serialization + */ +#ifdef HAVE_MEMCACHED_IGBINARY + case SERIALIZER_IGBINARY: + { + uint8_t *buffer; + size_t buffer_len; - /* - * Enable CAS support, but only if it is currently disabled. + if (igbinary_serialize(&buffer, &buffer_len, value) != 0) { + php_error_docref(NULL, E_WARNING, "could not serialize value with igbinary"); + return 0; + } + smart_str_appendl (buf, (char *)buffer, buffer_len); + efree(buffer); + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_IGBINARY); + } + break; +#endif + + /* + JSON serialization + */ +#ifdef HAVE_JSON_API + case SERIALIZER_JSON: + case SERIALIZER_JSON_ARRAY: + { + php_json_encode(buf, value, 0); + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_JSON); + } + break; +#endif + + /* + msgpack serialization + */ +#ifdef HAVE_MEMCACHED_MSGPACK + case SERIALIZER_MSGPACK: + php_msgpack_serialize(buf, value); + if (!buf->s) { + php_error_docref(NULL, E_WARNING, "could not serialize value with msgpack"); + return 0; + } + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_MSGPACK); + break; +#endif + + /* + PHP serialization */ - if (cas_token && PZVAL_IS_REF(cas_token) && orig_cas_flag == 0) { - memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); + default: + { + php_serialize_data_t var_hash; + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(buf, value, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + if (!buf->s) { + php_error_docref(NULL, E_WARNING, "could not serialize value"); + return 0; + } + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_SERIALIZED); + } + break; + } + + /* Check for exceptions caused by serializers */ + if (EG(exception) && buf->s->len) { + return 0; + } + return 1; +} + +static +zend_string *s_zval_to_payload(php_memc_object_t *intern, zval *value, uint32_t *flags) +{ + zend_string *payload; + php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc); + zend_bool should_compress = memc_user_data->compression_enabled; + + switch (Z_TYPE_P(value)) { + + case IS_STRING: + payload = zval_get_string(value); + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_STRING); + break; + + case IS_LONG: + { + smart_str buffer = {0}; + smart_str_append_long (&buffer, Z_LVAL_P(value)); + smart_str_0(&buffer); + payload = buffer.s; + + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_LONG); + should_compress = 0; + } + break; + + case IS_DOUBLE: + { + char buffer[40]; + php_memcached_g_fmt(buffer, Z_DVAL_P(value)); + payload = zend_string_init (buffer, strlen (buffer), 0); + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_DOUBLE); + should_compress = 0; + } + break; + + case IS_TRUE: + payload = zend_string_init ("1", 1, 0); + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL); + should_compress = 0; + break; + + case IS_FALSE: + payload = zend_string_alloc (0, 0); + MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL); + should_compress = 0; + break; + + default: + { + smart_str buffer = {0}; + + if (!s_serialize_value (memc_user_data->serializer, value, &buffer, flags)) { + smart_str_free(&buffer); + return NULL; + } + payload = buffer.s; + } + break; } - status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, keys, key_lens, 1); + /* turn off compression for values below the threshold */ + if (ZSTR_LEN(payload) == 0 || ZSTR_LEN(payload) < MEMC_G(compression_threshold)) { + should_compress = 0; + } + + /* If we have compression flag, compress the value */ + if (should_compress) { + /* s_compress_value() will always leave a valid payload, even if that payload + * did not actually get compressed. The flags will be set according to the + * to the compression type or no compression. + * + * No need to check the return value because the payload is always valid. + */ + (void)s_compress_value (memc_user_data->compression_type, memc_user_data->compression_level, &payload, flags); + } + + if (memc_user_data->set_udf_flags >= 0) { + MEMC_VAL_SET_USER_FLAGS(*flags, ((uint32_t) memc_user_data->set_udf_flags)); + } + + return payload; +} + +static +zend_bool s_is_payload_too_big(php_memc_object_t *intern, zend_string *payload) +{ + php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc); + + /* An item size limit of 0 implies no limit enforced */ + if (memc_user_data->item_size_limit == 0) { + return 0; + } + if (ZSTR_LEN(payload) > memc_user_data->item_size_limit) { + return 1; + } + return 0; +} + +static +zend_bool s_should_retry_write (php_memc_object_t *intern, memcached_return status) +{ + if (memcached_server_count (intern->memc) == 0) { + return 0; + } + + return s_memcached_return_is_error (status, 1); +} + +static +zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, zend_string *server_key, zend_string *key, zval *value, time_t expiration) +{ + uint32_t flags = 0; + zend_string *payload = NULL; + memcached_return status = 0; + php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc); + zend_long retries = memc_user_data->store_retry_count; + + if (value) { + payload = s_zval_to_payload(intern, value, &flags); + + if (!payload) { + s_memc_set_status(intern, MEMC_RES_PAYLOAD_FAILURE, 0); + return 0; + } + + if (s_is_payload_too_big(intern, payload)) { + s_memc_set_status(intern, MEMCACHED_E2BIG, 0); + zend_string_release(payload); + return 0; + } + } + +#define memc_write_using_fn(fn_name) payload ? fn_name(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags) : MEMC_RES_PAYLOAD_FAILURE; +#define memc_write_using_fn_by_key(fn_name) payload ? fn_name(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags) : MEMC_RES_PAYLOAD_FAILURE; + + if (server_key) { + switch (op) { + case MEMC_OP_SET: + status = memc_write_using_fn_by_key(memcached_set_by_key); + break; + + case MEMC_OP_TOUCH: + status = php_memcached_touch_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), expiration); + break; + + case MEMC_OP_ADD: + status = memc_write_using_fn_by_key(memcached_add_by_key); + break; + + case MEMC_OP_REPLACE: + status = memc_write_using_fn_by_key(memcached_replace_by_key); + break; + + case MEMC_OP_APPEND: + status = memc_write_using_fn_by_key(memcached_append_by_key); + break; + + case MEMC_OP_PREPEND: + status = memc_write_using_fn_by_key(memcached_prepend_by_key); + break; + } + + if (status == MEMCACHED_END) { + status = MEMCACHED_SUCCESS; + } + } + else { +retry: + switch (op) { + case MEMC_OP_SET: + status = memc_write_using_fn(memcached_set); + break; + + case MEMC_OP_TOUCH: + status = php_memcached_touch(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), expiration); + break; + + case MEMC_OP_ADD: + status = memc_write_using_fn(memcached_add); + break; + + case MEMC_OP_REPLACE: + status = memc_write_using_fn(memcached_replace); + break; + + case MEMC_OP_APPEND: + status = memc_write_using_fn(memcached_append); + break; + + case MEMC_OP_PREPEND: + status = memc_write_using_fn(memcached_prepend); + break; + } + if (status == MEMCACHED_END) { + status = MEMCACHED_SUCCESS; + } + } + + if (s_should_retry_write (intern, status) && retries-- > 0) { + goto retry; + } + +#undef memc_write_using_fn +#undef memc_write_using_fn_by_key + + if (payload) { + zend_string_release(payload); + } + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + return 0; + } + return 1; +} + + +/**************************************** + Methods +****************************************/ + + +/* {{{ Memcached::__construct([string persistent_id[, callback on_new[, string connection_str]]])) + Creates a Memcached object, optionally using persistent memcache connection */ +static PHP_METHOD(Memcached, __construct) +{ + php_memc_object_t *intern; + php_memc_user_data_t *memc_user_data; + + zend_string *persistent_id = NULL; + zend_string *conn_str = NULL; + zend_string *plist_key = NULL; + zend_fcall_info fci = {0}; + zend_fcall_info_cache fci_cache; + + zend_bool is_persistent = 0; + + /* "|S!f!S" */ + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_OPTIONAL + Z_PARAM_STR_EX(persistent_id, 1, 0) + Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0) + Z_PARAM_STR(conn_str) + ZEND_PARSE_PARAMETERS_END(); + + intern = Z_MEMC_OBJ_P(getThis()); + intern->is_pristine = 1; + + if (persistent_id && persistent_id->len) { + zend_resource *le; + + plist_key = zend_string_alloc(sizeof("memcached:id=") + persistent_id->len - 1, 0); + snprintf(ZSTR_VAL(plist_key), plist_key->len + 1, "memcached:id=%s", ZSTR_VAL(persistent_id)); + + if ((le = zend_hash_find_ptr(&EG(persistent_list), plist_key)) != NULL) { + if (le->type == php_memc_list_entry()) { + intern->memc = le->ptr; + intern->is_pristine = 0; + zend_string_release (plist_key); + return; + } + } + is_persistent = 1; + } + + if (conn_str && conn_str->len > 0) { + intern->memc = memcached (ZSTR_VAL(conn_str), ZSTR_LEN(conn_str)); + } + else { + intern->memc = memcached (NULL, 0); + } + + if (!intern->memc) { + php_error_docref(NULL, E_ERROR, "Failed to allocate memory for memcached structure"); + /* never reached */ + } + + memc_user_data = pecalloc (1, sizeof(*memc_user_data), is_persistent); + memc_user_data->serializer = MEMC_G(serializer_type); + memc_user_data->compression_type = MEMC_G(compression_type); + memc_user_data->compression_level = MEMC_G(compression_level); + memc_user_data->compression_enabled = 1; + memc_user_data->encoding_enabled = 0; + memc_user_data->store_retry_count = MEMC_G(store_retry_count); + memc_user_data->set_udf_flags = -1; + memc_user_data->item_size_limit = MEMC_G(item_size_limit); + memc_user_data->is_persistent = is_persistent; + + memcached_set_user_data(intern->memc, memc_user_data); + + /* Set default behaviors */ + if (MEMC_G(default_behavior.consistent_hash_enabled)) { + memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, MEMCACHED_DISTRIBUTION_CONSISTENT); + if (rc != MEMCACHED_SUCCESS) { + php_error_docref(NULL, E_WARNING, "Failed to turn on consistent hash: %s", memcached_strerror(intern->memc, rc)); + } + } + + if (MEMC_G(default_behavior.binary_protocol_enabled)) { + memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); + if (rc != MEMCACHED_SUCCESS) { + php_error_docref(NULL, E_WARNING, "Failed to turn on binary protocol: %s", memcached_strerror(intern->memc, rc)); + } + /* Also enable TCP_NODELAY when binary protocol is enabled */ + rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1); + if (rc != MEMCACHED_SUCCESS) { + php_error_docref(NULL, E_WARNING, "Failed to set TCP_NODELAY: %s", memcached_strerror(intern->memc, rc)); + } + } + + if (MEMC_G(default_behavior.connect_timeout)) { + memcached_return rc = memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, MEMC_G(default_behavior.connect_timeout)); + if (rc != MEMCACHED_SUCCESS) { + php_error_docref(NULL, E_WARNING, "Failed to set connect timeout: %s", memcached_strerror(intern->memc, rc)); + } + } + + if (fci.size) { + if (!s_invoke_new_instance_cb(getThis(), &fci, &fci_cache, persistent_id) || EG(exception)) { + /* error calling or exception thrown from callback */ + if (plist_key) { + zend_string_release(plist_key); + } + /* + Setting intern->memc null prevents object destruction from freeing the memcached_st + We free it manually here because it might be persistent and has not been + registered to persistent_list yet + */ + php_memc_destroy(intern->memc, memc_user_data); + intern->memc = NULL; + return; + } + } + + if (plist_key) { + zend_resource le; + + le.type = php_memc_list_entry(); + le.ptr = intern->memc; + + GC_SET_REFCOUNT(&le, 1); + + /* plist_key is not a persistent allocated key, thus we use str_update here */ + if (zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(plist_key), ZSTR_LEN(plist_key), &le, sizeof(le)) == NULL) { + zend_string_release(plist_key); + php_error_docref(NULL, E_ERROR, "could not register persistent entry"); + /* not reached */ + } + zend_string_release(plist_key); + } +} +/* }}} */ + + + +static +void s_hash_to_keys(php_memc_keys_t *keys_out, HashTable *hash_in, zend_bool preserve_order, zval *return_value) +{ + size_t idx = 0, alloc_count; + zval *zv; + + keys_out->num_valid_keys = 0; - 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); + 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 *)); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - RETURN_FROM_GET; - } + ZEND_HASH_FOREACH_VAL(hash_in, zv) { + zend_string *key = zval_get_string(zv); - status = MEMCACHED_SUCCESS; - memcached_result_create(m_obj->memc, &result); + if (preserve_order && return_value) { + add_assoc_null_ex(return_value, ZSTR_VAL(key), ZSTR_LEN(key)); + } - if (memcached_fetch_result(m_obj->memc, &result, &status) == NULL) { - /* This is for historical reasons */ - if (status == MEMCACHED_END) - status = MEMCACHED_NOTFOUND; + 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); - /* - * 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); + keys_out->strings[idx] = key; + idx++; } - - if (status == MEMCACHED_NOTFOUND && fci.size != 0) { - status = php_memc_do_cache_callback(getThis(), &fci, &fcc, key, key_len, return_value TSRMLS_CC); + else { + zend_string_release (key); } - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - memcached_result_free(&result); - RETURN_FROM_GET; - } + } ZEND_HASH_FOREACH_END(); - /* if we have a callback, all processing is done */ - if (fci.size != 0) { - memcached_result_free(&result); - return; - } + if (!idx) { + efree (keys_out->mkeys); + efree (keys_out->mkeys_len); + efree (keys_out->strings); } + keys_out->num_valid_keys = idx; +} - /* 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); +static +void s_key_to_keys(php_memc_keys_t *keys_out, zend_string *key) +{ + zval zv_keys; - payload = memcached_result_value(&result); - payload_len = memcached_result_length(&result); - flags = memcached_result_flags(&result); - if (cas_token) { - cas = memcached_result_cas(&result); - } + array_init(&zv_keys); + add_next_index_str(&zv_keys, zend_string_copy(key)); - 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; - } + s_hash_to_keys(keys_out, Z_ARRVAL(zv_keys), 0, NULL); + zval_ptr_dtor(&zv_keys); +} - if (cas_token) { - zval_dtor(cas_token); - ZVAL_DOUBLE(cas_token, (double)cas); - } +static +void s_clear_keys(php_memc_keys_t *keys) +{ + size_t i; - if (udf_flags) { - zval_dtor(udf_flags); - ZVAL_LONG(udf_flags, MEMC_VAL_GET_USER_FLAGS(flags)); + if (!keys->num_valid_keys) { + return; } - memcached_result_free(&result); + 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); } -/* }}} */ -/* {{{ Memcached::getMulti(array keys [, array &cas_tokens [, array &udf_flags ] ]) - Returns values for the given keys or false */ -PHP_METHOD(Memcached, getMulti) -{ - php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ +typedef struct { + zend_bool extended; + zval *return_value; +} php_memc_get_ctx_t; -/* {{{ 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) +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_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + 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 */ } -/* }}} */ -/* {{{ -- php_memc_getMulti_impl */ -static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) +static +void php_memc_get_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; + php_memc_get_ctx_t context = {0}; + php_memc_keys_t keys = {0}; + zend_long get_flags = 0; + zend_string *key; + zend_string *server_key = NULL; + zend_bool mget_status; memcached_return status = MEMCACHED_SUCCESS; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|zlz", &server_key, - &server_key_len, &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { - return; - } + /* "SS|f!l" */ + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STR(server_key) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_FUNC_EX(fci, fcc, 1, 0) + Z_PARAM_LONG(get_flags) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|zlz", &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { - return; - } + /* "S|f!l" */ + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_FUNC_EX(fci, fcc, 1, 0) + Z_PARAM_LONG(get_flags) + ZEND_PARSE_PARAMETERS_END(); } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + MEMC_CHECK_KEY(intern, key); - 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); + context.extended = (get_flags & MEMC_GET_EXTENDED); - /* - * 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))) { + context.return_value = return_value; - if (Z_TYPE_PP(entry) != IS_STRING) { - convert_to_string_ex(entry); - } + 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 (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 (!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 (preserve_order) { - add_assoc_null_ex(return_value, mkeys[i], mkeys_len[i] + 1); + if (!status) { + zval_ptr_dtor(return_value); + RETURN_FROM_GET; } - i++; } } - if (i == 0) { - i_obj->rescode = MEMCACHED_NOTFOUND; - efree(mkeys); - efree(mkeys_len); - return; + if (s_memc_status_has_error(intern)) { + zval_ptr_dtor(return_value); + RETURN_FROM_GET; } +} - /* - * 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); - } - } +/* {{{ Memcached::get(string key [, mixed callback [, int get_flags = 0]) + Returns a value for the given key or false */ +PHP_METHOD(Memcached, get) +{ + php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ Memcached::getByKey(string server_key, string key [, mixed callback [, int get_flags = 0]) + Returns a value for key from the server identified by the server key or false */ +PHP_METHOD(Memcached, getByKey) +{ + php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ - 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); +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; - /* - * 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); - } + Z_TRY_ADDREF_P(value); - efree(mkeys); - efree(mkeys_len); + if (context->extended) { + zval node; - /* - * 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; - } - } + Z_TRY_ADDREF_P(cas); - /* - * 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); + 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; +} - memcached_result_create(m_obj->memc, &result); - while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { - char res_key [MEMCACHED_MAX_KEY]; +/* {{{ -- php_memc_getMulti_impl */ +static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) +{ + php_memc_get_ctx_t context; + php_memc_keys_t keys_out; - if (status != MEMCACHED_SUCCESS) { - status = MEMCACHED_SOME_ERRORS; - php_memc_handle_error(i_obj, status TSRMLS_CC); - continue; - } + zval *keys = NULL; + zend_string *server_key = NULL; + zend_long flags = 0; + MEMC_METHOD_INIT_VARS; + zend_bool retval, preserve_order; - 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); + if (by_key) { + /* "Sa|l" */ + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(server_key) + Z_PARAM_ARRAY(keys) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + } else { + /* "a|l" */ + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY(keys) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + } - /* - * 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'; + MEMC_METHOD_FETCH_OBJECT; + + array_init(return_value); + if (zend_hash_num_elements(Z_ARRVAL_P(keys)) == 0) { + /* BC compatible */ + s_memc_set_status(intern, MEMCACHED_NOTFOUND, 0); + return; + } - ALLOC_INIT_ZVAL(value); + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - 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); + preserve_order = (flags & MEMC_GET_PRESERVE_ORDER); + s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), preserve_order, return_value); - break; - } - status = MEMCACHED_SOME_ERRORS; - i_obj->rescode = MEMCACHED_SOME_ERRORS; + context.extended = (flags & MEMC_GET_EXTENDED); + context.return_value = return_value; - continue; - } + retval = php_memc_mget_apply(intern, server_key, &keys_out, s_get_multi_apply_fn, context.extended, &context); - 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)); - } - } + s_clear_keys(&keys_out); - memcached_result_free(&result); + if (!retval && (s_memc_status_has_result_code(intern, MEMCACHED_NOTFOUND) || s_memc_status_has_result_code(intern, MEMCACHED_SOME_ERRORS))) { + return; + } - 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); - } + if (!retval || EG(exception)) { zval_dtor(return_value); - RETURN_FALSE; + RETURN_FROM_GET; } } /* }}} */ +/* {{{ Memcached::getMulti(array keys[, long flags = 0 ]) + Returns values for the given keys or false */ +PHP_METHOD(Memcached, getMulti) +{ + php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ Memcached::getMultiByKey(string server_key, array keys[, long flags = 0 ]) + Returns values for the given keys from the server identified by the server key or false */ +PHP_METHOD(Memcached, getMultiByKey) +{ + php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + /* {{{ Memcached::getDelayed(array keys [, bool with_cas [, mixed callback ] ]) Sends a request for the given keys and returns immediately */ PHP_METHOD(Memcached, getDelayed) @@ -899,140 +1727,120 @@ 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) { - return; - } + /* "Sa/|bf!" */ + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STR(server_key) + Z_PARAM_ARRAY_EX(keys, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(with_cas) + Z_PARAM_FUNC_EX(fci, fcc, 1, 0) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|bf!", &keys, &with_cas, - &fci, &fcc) == FAILURE) { - return; - } + /* "a/|bf!" */ + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ARRAY_EX(keys, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(with_cas) + Z_PARAM_FUNC_EX(fci, fcc, 1, 0) + ZEND_PARSE_PARAMETERS_END(); } MEMC_METHOD_FETCH_OBJECT; - 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); - - 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); - 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); - } - } + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - /* - * 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); + s_hash_to_keys(&keys_out, Z_ARRVAL_P(keys), 0, NULL); - /* - * 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; @@ -1041,58 +1849,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; @@ -1101,53 +1886,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) { @@ -1155,7 +1906,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) { @@ -1163,12 +1914,11 @@ PHP_METHOD(Memcached, setByKey) } /* }}} */ -#ifdef HAVE_MEMCACHED_TOUCH /* {{{ Memcached::touch(string key, [, int expiration ]) Sets a new expiration for the given key */ PHP_METHOD(Memcached, touch) { - php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 0); + php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 0); } /* }}} */ @@ -1176,13 +1926,11 @@ PHP_METHOD(Memcached, touch) Sets a new expiration for the given key */ PHP_METHOD(Memcached, touchByKey) { - php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 1); + php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 1); } /* }}} */ -#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) { @@ -1190,7 +1938,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) { @@ -1198,127 +1946,65 @@ 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; - 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]; + zend_string *server_key = NULL; + zend_long expiration = 0; + 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) { - return; - } + if (by_key) { + /* "Sa|ll" */ + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(server_key) + Z_PARAM_ARRAY(entries) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ll", &entries, &expiration, &udf_flags) == FAILURE) { - return; - } + /* "a|ll" */ + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY(entries) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } 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); - } - } + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - 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); + ZEND_HASH_FOREACH_KEY_VAL (Z_ARRVAL_P(entries), num_key, skey, value) { + zend_string *str_key = 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))); - - 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; - } - - 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 this failed to write a value, intern stores the error for the return value */ + s_memc_write_zval (intern, MEMC_OP_SET, server_key, str_key, value, expiration); -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) { @@ -1326,7 +2012,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) { @@ -1366,7 +2052,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) { @@ -1374,7 +2060,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) { @@ -1382,258 +2068,156 @@ 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) { - return; - } - INIT_ZVAL(s_zvalue); + /* "SSS" */ + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(server_key) + Z_PARAM_STR(key) + Z_PARAM_STR(s_value) + ZEND_PARSE_PARAMETERS_END(); value = &s_zvalue; - ZVAL_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) { - return; - } + /* "SS|l" */ + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(server_key) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|ll", &server_key, - &server_key_len, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { - return; - } + /* "SSz|l" */ + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STR(server_key) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(value) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } } else { if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, - &s_value, &s_value_len) == FAILURE) { - return; - } - INIT_ZVAL(s_zvalue); + /* "SS" */ + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_STR(s_value) + ZEND_PARSE_PARAMETERS_END(); 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) { - return; - } + /* "S|l */ + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|ll", &key, &key_len, - &value, &expiration, &udf_flags) == FAILURE) { - return; - } + /* "Sz|l" */ + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(value) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - if (key_len == 0 || strchr(key, ' ')) { - 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; - } - 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; + php_error_docref(NULL, E_WARNING, "cannot append/prepend with compression turned on"); + RETURN_NULL(); } } -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_long expiration = 0; + 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) { - return; - } + /* "zSSz|l" */ + ZEND_PARSE_PARAMETERS_START(4, 5) + Z_PARAM_ZVAL(zv_cas) + Z_PARAM_STR(server_key) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(value) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dsz|ll", &cas_d, &key, &key_len, - &value, &expiration, &udf_flags) == FAILURE) { - return; - } + /* "zSz|l" */ + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_ZVAL(zv_cas) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(value) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - if (key_len == 0 || strchr(key, ' ')) { - i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; - RETURN_FALSE; - } + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + MEMC_CHECK_KEY(intern, key); - DVAL_TO_LVAL(cas_d, cas); + cas = s_zval_to_uint64(zv_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 = s_zval_to_payload(intern, value, &flags); + if (payload == NULL) { + intern->rescode = MEMC_RES_PAYLOAD_FAILURE; + RETURN_FALSE; } - 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; + if (s_is_payload_too_big(intern, payload)) { + intern->rescode = MEMCACHED_E2BIG; + zend_string_release(payload); 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; } @@ -1641,7 +2225,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) { @@ -1649,7 +2233,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) { @@ -1692,40 +2276,41 @@ 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; - time_t expiration = 0; + zend_string *key, *server_key; + zend_long 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) { - return; - } + /* "SS|l" */ + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(server_key) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, - &expiration) == FAILURE) { - return; - } - server_key = key; - server_key_len = key_len; + /* "S|l" */ + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); + server_key = key; } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + MEMC_CHECK_KEY(intern, key); - if (key_len == 0 || strchr(key, ' ')) { - 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; } @@ -1736,55 +2321,57 @@ 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; - time_t expiration = 0; - zval **entry; + zval *entries, *zv, ret; + zend_string *server_key = NULL; + zend_long expiration = 0; + zend_string *entry; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|l", &server_key, - &server_key_len, &entries, &expiration) == FAILURE) { - return; - } + /* "Sa/|l" */ + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(server_key) + Z_PARAM_ARRAY_EX(entries, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|l", &entries, &expiration) == FAILURE) { - return; - } + /* "a/|l" */ + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY_EX(entries, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(expiration) + ZEND_PARSE_PARAMETERS_END(); } MEMC_METHOD_FETCH_OBJECT; - 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; } @@ -1793,77 +2380,97 @@ 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; - long offset = 1; - uint64_t value, initial = 0; - time_t expiry = 0; + zend_string *key, *server_key = NULL; + zend_long offset = 1; + zend_long expiry = 0; + zend_long initial = 0; + uint64_t value = UINT64_MAX; memcached_return status; int n_args = ZEND_NUM_ARGS(); - 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) { - return; - } + /* "S|lll" */ + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(initial) + Z_PARAM_LONG(expiry) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(n_args TSRMLS_CC, "ss|lll", &server_key, &server_key_len, &key, &key_len, &offset, &initial, &expiry) == FAILURE) { - return; - } + /* "SS|lll" */ + ZEND_PARSE_PARAMETERS_START(2, 5) + Z_PARAM_STR(server_key) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + Z_PARAM_LONG(initial) + Z_PARAM_LONG(expiry) + ZEND_PARSE_PARAMETERS_END(); } MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; - - if (key_len == 0 || strchr(key, ' ')) { - 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 cannot be a negative value"); 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), 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), offset, &value); } } else { + /* The libmemcached API has a quirk that memcached_increment() takes only a 32-bit + * offset, but memcached_increment_by_key() and all other increment and decrement + * functions take a 64-bit offset. The memcached protocol allows increment/decrement + * greater than UINT_MAX, so we just work around memcached_increment() here. + */ if (incr) { - status = memcached_increment(m_obj->memc, key, key_len, (unsigned int)offset, &value); + status = memcached_increment_by_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(key), ZSTR_LEN(key), offset, &value); } else { - status = memcached_decrement(m_obj->memc, key, key_len, (unsigned int)offset, &value); + status = memcached_decrement_by_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(key), ZSTR_LEN(key), 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), offset, initial, (time_t)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), offset, initial, (time_t)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), offset, initial, (time_t)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), offset, initial, (time_t)expiry, &value); } } + if (s_should_retry_write(intern, status) && retries-- > 0) { + goto retry_inc_dec; + } } - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { - PHP_MEMC_FAILOVER_RETRY + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + RETURN_FALSE; + } + + if (value == UINT64_MAX) { RETURN_FALSE; } @@ -1907,33 +2514,25 @@ PHP_METHOD(Memcached, incrementByKey) Adds the given memcache server to the list */ PHP_METHOD(Memcached, addServer) { - char *host; - int host_len; - long port, weight = 0; + zend_string *host; + zend_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) { - return; - } + /* "Sa/|l" */ + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(host) + Z_PARAM_LONG(port) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(weight) + ZEND_PARSE_PARAMETERS_END(); MEMC_METHOD_FETCH_OBJECT; - 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); - } else { - status = memcached_server_add_with_weight(m_obj->memc, host, port, weight); - } -#else - status = memcached_server_add_with_weight(m_obj->memc, host, port, weight); -#endif + status = memcached_server_add_with_weight(intern->memc, ZSTR_VAL(host), port, weight); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } @@ -1946,78 +2545,84 @@ 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) { - return; - } + /* "a/" */ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_EX(servers, 0, 1) + ZEND_PARSE_PARAMETERS_END(); MEMC_METHOD_FETCH_OBJECT; - i_obj->rescode = MEMCACHED_SUCCESS; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); - 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++) { - - 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); + + zend_string_release(host); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) == 0) { + 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; } @@ -2025,12 +2630,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; @@ -2040,10 +2643,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); } /* }}} */ @@ -2051,32 +2653,27 @@ 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) { - return; - } + /* "S" */ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(server_key) + ZEND_PARSE_PARAMETERS_END(); 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); } @@ -2086,16 +2683,16 @@ PHP_METHOD(Memcached, getServerByKey) Reset the server list in use */ PHP_METHOD(Memcached, resetServerList) { - MEMC_METHOD_INIT_VARS; + MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } - MEMC_METHOD_FETCH_OBJECT; + MEMC_METHOD_FETCH_OBJECT; - memcached_servers_reset(m_obj->memc); - RETURN_TRUE; + memcached_servers_reset(intern->memc); + RETURN_TRUE; } /* }}} */ @@ -2103,16 +2700,16 @@ PHP_METHOD(Memcached, resetServerList) Close any open connections */ PHP_METHOD(Memcached, quit) { - MEMC_METHOD_INIT_VARS; + MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } - MEMC_METHOD_FETCH_OBJECT; + MEMC_METHOD_FETCH_OBJECT; - memcached_quit(m_obj->memc); - RETURN_TRUE; + memcached_quit(intern->memc); + RETURN_TRUE; } /* }}} */ @@ -2126,12 +2723,11 @@ PHP_METHOD(Memcached, flushBuffers) return; } - MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(memcached_flush_buffers(m_obj->memc) == MEMCACHED_SUCCESS); + MEMC_METHOD_FETCH_OBJECT; + RETURN_BOOL(memcached_flush_buffers(intern->memc) == MEMCACHED_SUCCESS); } /* }}} */ -#ifdef HAVE_LIBMEMCACHED_CHECK_CONFIGURATION /* {{{ Memcached::getLastErrorMessage() Returns the last error message that occurred */ PHP_METHOD(Memcached, getLastErrorMessage) @@ -2144,7 +2740,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)); } /* }}} */ @@ -2160,7 +2756,7 @@ PHP_METHOD(Memcached, getLastErrorCode) MEMC_METHOD_FETCH_OBJECT; - RETURN_LONG(memcached_last_error(m_obj->memc)); + RETURN_LONG(memcached_last_error(intern->memc)); } /* }}} */ @@ -2176,10 +2772,9 @@ 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 /* {{{ Memcached::getLastDisconnectedServer() Returns the last disconnected server @@ -2195,56 +2790,124 @@ 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", (int)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]; + char *args = NULL; + zend_string *args_string = NULL; + uint64_t orig_no_block, orig_protocol; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters_none() == FAILURE) { - return; - } + /* "|S!" */ + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_EX(args_string, 1, 0) + ZEND_PARSE_PARAMETERS_END(); MEMC_METHOD_FETCH_OBJECT; - if (memcached_server_count(m_obj->memc) == 0) { - array_init(return_value); - return; - } + if (args_string) + args = ZSTR_VAL(args_string); - 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); - RETURN_FALSE; - } + /* stats hangs in nonblocking mode, turn off during the call. Only change the + * value if needed, because libmemcached reconnects for this behavior_set. */ + orig_no_block = memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK); + orig_protocol = memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL); + if (orig_no_block && orig_protocol) + memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 0); array_init(return_value); + status = memcached_stat_execute(intern->memc, args, s_stat_execute_cb, return_value); - 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); + if (orig_no_block && orig_protocol) + memcached_behavior_set(intern->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, orig_no_block); - memcached_stat_free(m_obj->memc, stats); + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { + zval_ptr_dtor(return_value); + RETURN_FALSE; + } } /* }}} */ @@ -2252,8 +2915,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; @@ -2263,28 +2925,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; } @@ -2295,12 +2958,25 @@ 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); + + /* Ignore two errors. libmemcached has a hardcoded loop of 200 slab + * classes that matches memcached < 1.4.24, at which version the server + * has only 63 slabs and throws an error when requesting the 64th slab. + * + * In multi-server some non-deterministic number of elements will be dropped. + */ + if (rc != MEMCACHED_CLIENT_ERROR && rc != MEMCACHED_SERVER_ERROR + && s_memc_status_handle_result_code(intern, rc) == FAILURE) { zval_dtor(return_value); RETURN_FALSE; } @@ -2311,19 +2987,21 @@ PHP_METHOD(Memcached, getAllKeys) Flushes the data on all the servers */ static PHP_METHOD(Memcached, flush) { - time_t delay = 0; + zend_long delay = 0; memcached_return status; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &delay) == FAILURE) { - return; - } + /* "|l" */ + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(delay) + ZEND_PARSE_PARAMETERS_END(); 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; } @@ -2335,53 +3013,60 @@ static PHP_METHOD(Memcached, flush) Returns the value for the given option constant */ static PHP_METHOD(Memcached, getOption) { - long option; + zend_long option; uint64_t result; memcached_behavior flag; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &option) == FAILURE) { - return; - } + /* "l" */ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(option) + ZEND_PARSE_PARAMETERS_END(); MEMC_METHOD_FETCH_OBJECT; switch (option) { case MEMC_OPT_COMPRESSION_TYPE: - RETURN_LONG(m_obj->compression_type); + RETURN_LONG(memc_user_data->compression_type); + + case MEMC_OPT_COMPRESSION_LEVEL: + RETURN_LONG(memc_user_data->compression_level); case MEMC_OPT_COMPRESSION: - RETURN_BOOL(m_obj->compression); + RETURN_BOOL(memc_user_data->compression_enabled); + + case MEMC_OPT_ITEM_SIZE_LIMIT: + RETURN_LONG(memc_user_data->item_size_limit); 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); -#else - RETURN_STRING(result, 1); -#endif + RETURN_STRING(result); } else { RETURN_EMPTY_STRING(); } } 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; } @@ -2390,73 +3075,82 @@ 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: + lval = zval_get_long(value); + if (lval == COMPRESSION_TYPE_FASTLZ || +#ifdef HAVE_ZSTD_H + lval == COMPRESSION_TYPE_ZSTD || +#endif + lval == COMPRESSION_TYPE_ZLIB) { + memc_user_data->compression_type = lval; + } else { + /* invalid compression type */ + intern->rescode = MEMCACHED_INVALID_ARGUMENTS; + return 0; + } + break; + + case MEMC_OPT_COMPRESSION_LEVEL: + lval = zval_get_long(value); + memc_user_data->compression_level = lval; 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); - } else { - /* invalid compression type */ - i_obj->rescode = MEMCACHED_INVALID_ARGUMENTS; + case MEMC_OPT_ITEM_SIZE_LIMIT: + lval = zval_get_long(value); + if (lval < 0) { + php_error_docref(NULL, E_WARNING, "ITEM_SIZE_LIMIT must be >= 0"); return 0; } + memc_user_data->item_size_limit = lval; 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_LEN(str) == 0) { key = NULL; } else { - /* - work-around a bug in libmemcached in version 0.49 that truncates the trailing - 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)); - key = tmp; -#else - key = Z_STRVAL_P(value); -#endif + key = ZSTR_VAL(str); } - 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; } @@ -2465,55 +3159,65 @@ 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 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); -#else - m_obj->memc->hash = 0; - m_obj->memc->distribution = 0; -#endif + if (!lval) { + (void)memcached_behavior_set_key_hash(intern->memc, MEMCACHED_HASH_DEFAULT); + (void)memcached_behavior_set_distribution_hash(intern->memc, MEMCACHED_HASH_DEFAULT); + (void)memcached_behavior_set_distribution(intern->memc, MEMCACHED_DISTRIBUTION_MODULA); } break; case MEMC_OPT_SERIALIZER: { - 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: @@ -2525,18 +3229,23 @@ 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)); + // don't reset the option when the option value wasn't modified, + // while the libmemcached may shutdown all connections. + if (memcached_behavior_get(intern->memc, flag) == (uint64_t)lval) { + return 1; + } + 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; @@ -2545,9 +3254,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; @@ -2559,34 +3268,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; } @@ -2597,7 +3291,7 @@ PHP_METHOD(Memcached, setBucket) { zval *zserver_map; zval *zforward_map = NULL; - long replicas = 0; + zend_long replicas = 0; zend_bool retval = 1; uint32_t *server_map = NULL, *forward_map = NULL; @@ -2605,35 +3299,38 @@ 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) { - return; - } + /* "aa!l" */ + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_ARRAY(zserver_map) + Z_PARAM_ARRAY_EX(zforward_map, 1, 0) + Z_PARAM_LONG(replicas) + ZEND_PARSE_PARAMETERS_END(); MEMC_METHOD_FETCH_OBJECT; if (zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) == 0) { - php_error_docref(NULL 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); @@ -2641,10 +3338,10 @@ 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) { - retval = 0;; + if (s_memc_status_handle_result_code(intern, rc) == FAILURE) { + retval = 0; } efree(server_map); @@ -2662,38 +3359,30 @@ static PHP_METHOD(Memcached, setOptions) { zval *options; zend_bool ok = 1; - uint key_len; - char *key; - ulong key_index; - zval **value; + zend_string *key; + zend_ulong key_index; + zval *value; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &options) == FAILURE) { - return; - } - - MEMC_METHOD_FETCH_OBJECT; + /* "a" */ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(options) + ZEND_PARSE_PARAMETERS_END(); - 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(©); + MEMC_METHOD_FETCH_OBJECT; - 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); } @@ -2703,17 +3392,19 @@ static PHP_METHOD(Memcached, setOptions) Sets the value for the given option constant */ static PHP_METHOD(Memcached, setOption) { - long option; + zend_long option; zval *value; MEMC_METHOD_INIT_VARS; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz/", &option, &value) == FAILURE) { - return; - } + /* "lz/" */ + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(option) + Z_PARAM_ZVAL_EX(value, 0, 1) + ZEND_PARSE_PARAMETERS_END(); MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(php_memc_set_option(i_obj, option, value TSRMLS_CC)); + RETURN_BOOL(php_memc_set_option(intern, option, value)); } /* }}} */ @@ -2724,35 +3415,70 @@ static PHP_METHOD(Memcached, setSaslAuthData) { MEMC_METHOD_INIT_VARS; memcached_return status; + zend_string *user, *pass; - char *user, *pass; - int user_len, pass_len; + /* "SS/" */ + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(user) + Z_PARAM_STR(pass) + ZEND_PARSE_PARAMETERS_END(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &user, &user_len, &pass, &pass_len) == FAILURE) { - return; + if (!php_memc_init_sasl_if_needed()) { + RETURN_FALSE; } - if (!MEMC_G(use_sasl)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "SASL support (memcached.use_sasl) isn't enabled in php.ini"); + MEMC_METHOD_FETCH_OBJECT; + + if (!memcached_behavior_get(intern->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { + php_error_docref(NULL, E_WARNING, "SASL is only supported with binary protocol"); + RETURN_FALSE; + } + memc_user_data->has_sasl_data = 1; + status = memcached_set_sasl_auth_data(intern->memc, ZSTR_VAL(user), ZSTR_VAL(pass)); + + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } + RETURN_TRUE; +} +/* }}} */ +#endif /* HAVE_MEMCACHED_SASL */ + +#ifdef HAVE_MEMCACHED_SET_ENCODING_KEY +/* {{{ Memcached::setEncodingKey(string key) + Sets AES encryption key (libmemcached 1.0.6 and higher) */ +static PHP_METHOD(Memcached, setEncodingKey) +{ + MEMC_METHOD_INIT_VARS; + memcached_return status; + zend_string *key; + + /* "S" */ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(key) + ZEND_PARSE_PARAMETERS_END(); MEMC_METHOD_FETCH_OBJECT; - 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"); + // libmemcached < 1.0.18 cannot handle a change of encoding key. Warn about this and return false. +#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000018 + if (memc_user_data->encoding_enabled) { + php_error_docref(NULL, E_WARNING, "libmemcached versions less than 1.0.18 cannot change encoding key"); RETURN_FALSE; } - m_obj->has_sasl_data = 1; - status = memcached_set_sasl_auth_data(m_obj->memc, user, pass); +#endif + + status = memcached_set_encoding_key(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key)); - if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { + if (s_memc_status_handle_result_code(intern, status) == FAILURE) { RETURN_FALSE; } + + memc_user_data->encoding_enabled = 1; RETURN_TRUE; } /* }}} */ -#endif /* HAVE_MEMCACHED_SASL */ +#endif /* HAVE_MEMCACHED_SET_ENCODING_KEY */ /* {{{ Memcached::getResultCode() Returns the result code from the last operation */ @@ -2766,7 +3492,7 @@ static PHP_METHOD(Memcached, getResultCode) MEMC_METHOD_FETCH_OBJECT; - RETURN_LONG(i_obj->rescode); + RETURN_LONG(intern->rescode); } /* }}} */ @@ -2782,24 +3508,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; } @@ -2818,7 +3542,7 @@ static PHP_METHOD(Memcached, isPersistent) MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(i_obj->is_persistent); + RETURN_BOOL(memc_user_data->is_persistent); } /* }}} */ @@ -2834,524 +3558,255 @@ static PHP_METHOD(Memcached, isPristine) MEMC_METHOD_FETCH_OBJECT; - RETURN_BOOL(i_obj->is_pristine); + RETURN_BOOL(intern->is_pristine); } /* }}} */ -/**************************************** - Internal support code -****************************************/ - -/* {{{ 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) +/* {{{ bool Memcached::checkKey(string key) + Checks if a key is valid */ +PHP_METHOD(Memcached, checkKey) { - zend_object_std_dtor(&i_obj->zo TSRMLS_CC); + zend_string *key; + MEMC_METHOD_INIT_VARS; - if (i_obj->obj && !i_obj->is_persistent) { - php_memc_destroy(i_obj->obj, 0 TSRMLS_CC); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(key) + ZEND_PARSE_PARAMETERS_END(); - i_obj->obj = NULL; - efree(i_obj); + MEMC_METHOD_FETCH_OBJECT; + s_memc_set_status(intern, MEMCACHED_SUCCESS, 0); + MEMC_CHECK_KEY(intern, key); + RETURN_TRUE; } +/* }}} */ -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; -} +/**************************************** + Internal support code +****************************************/ -#ifdef HAVE_MEMCACHED_PROTOCOL +/* {{{ constructor/destructor */ 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; +#ifdef 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) +void php_memc_object_free_storage(zend_object *object) { - /* 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; - - compress_status = (compress((Bytef *)buffer, &compressed_size, (Bytef *)payload, *payload_len) == Z_OK); - MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_ZLIB); - break; - - default: - compress_status = 0; - break; - } - buffer -= sizeof(uint32_t); - *payload_len = compressed_size + sizeof(uint32_t); + php_memc_object_t *intern = php_memc_fetch_object(object); - if (!compress_status) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not compress value"); - MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); + if (intern->memc) { + php_memc_user_data_t *memc_user_data = memcached_get_user_data(intern->memc); - efree (buffer); - *payload_len = 0; - return NULL; + if (!memc_user_data->is_persistent) { + php_memc_destroy(intern->memc, memc_user_data); + } } - 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; + intern->memc = NULL; + zend_object_std_dtor(&intern->zo); } static -zend_bool s_serialize_value (enum memcached_serializer serializer, zval *value, smart_str *buf, uint32_t *flags TSRMLS_DC) +zend_object *php_memc_object_new(zend_class_entry *ce) { - switch (serializer) { - - /* - 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 - - /* - 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 - - /* - 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 - - /* - 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); - - 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; - } - - /* Check for exceptions caused by serializers */ - if (EG(exception) && buf->len) { - return 0; - } - return 1; + php_memc_object_t *intern = ecalloc(1, sizeof(php_memc_object_t) + zend_object_properties_size(ce)); + + zend_object_std_init(&intern->zo, ce); + object_properties_init(&intern->zo, ce); + + intern->zo.handlers = &memcached_object_handlers; + return &intern->zo; } +#ifdef HAVE_MEMCACHED_PROTOCOL static -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) +void php_memc_server_free_storage(zend_object *object) { - const char *pl; - size_t pl_len = 0; + php_memc_server_t *intern = php_memc_server_fetch_object(object); - char *payload = NULL; - smart_str buf = {0}; - char tmp[40] = {0}; + php_memc_proto_handler_destroy(&intern->handler); + zend_object_std_dtor(&intern->zo); +} - switch (Z_TYPE_P(value)) { +zend_object *php_memc_server_new(zend_class_entry *ce) +{ + php_memc_server_t *intern; - case IS_STRING: - pl = Z_STRVAL_P(value); - pl_len = Z_STRLEN_P(value); - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_STRING); - break; + intern = ecalloc(1, sizeof(php_memc_server_t) + zend_object_properties_size(ce)); - case IS_LONG: - pl_len = sprintf(tmp, "%ld", Z_LVAL_P(value)); - pl = tmp; - MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_LONG); - break; + zend_object_std_init(&intern->zo, ce); + object_properties_init(&intern->zo, ce); - 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; + intern->zo.handlers = &memcached_server_object_handlers; + intern->handler = php_memc_proto_handler_new(); - 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; + return &intern->zo; +} +#endif - 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; +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; } +} - /* 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); - } +/* }}} */ - /* 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); - } +/* {{{ internal API functions */ +static +memcached_return s_server_cursor_list_servers_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) +{ + zval array; + zval *return_value = (zval *) in_context; - /* 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); - } + 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); + */ - if (buf.len) { - smart_str_free(&buf); - } - return payload; + add_next_index_zval(return_value, &array); + return MEMCACHED_SUCCESS; } static -char *s_decompress_value (const char *payload, size_t *payload_len, uint32_t flags TSRMLS_DC) +memcached_return s_server_cursor_version_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) { - char *buffer = NULL; - uint32_t len; + zend_string *address, *version; + zval rv, *return_value = (zval *)in_context; + +#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000009 + version = strpprintf(0, "%d.%d.%d", + memcached_server_major_version(instance), + memcached_server_minor_version(instance), + memcached_server_micro_version(instance)); +#else + version = strpprintf(0, "%d.%d.%d", + instance->major_version, + instance->minor_version, + instance->micro_version); +#endif + + address = strpprintf(0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance)); + + ZVAL_STR(&rv, version); + zend_hash_add(Z_ARRVAL_P(return_value), address, &rv); + + zend_string_release(address); + + return MEMCACHED_SUCCESS; +} + + +static +zend_string *s_decompress_value (const char *payload, size_t payload_len, uint32_t flags) +{ + zend_string *buffer; + + uint32_t stored_length; unsigned long length; zend_bool decompress_status = 0; + zend_bool is_fastlz = 0, is_zlib = 0, is_zstd = 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_zstd = MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZSTD); + is_zlib = MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB); + +#ifndef HAVE_ZSTD_H + if (is_zstd) { + php_error_docref(NULL, E_WARNING, "could not decompress value: value was compressed with zstd but zstd support has not been compiled in"); + return NULL; + } +#endif + + if (!is_fastlz && !is_zlib && !is_zstd) { + php_error_docref(NULL, E_WARNING, "could not decompress value: unrecognised compression type"); + return NULL; + } + + memcpy(&stored_length, payload, sizeof (uint32_t)); + + payload += sizeof (uint32_t); + payload_len -= sizeof (uint32_t); + + buffer = zend_string_alloc (stored_length, 0); + +#ifdef HAVE_ZSTD_H + if (is_zstd) { + length = ZSTD_getFrameContentSize(payload, payload_len); + if (length == ZSTD_CONTENTSIZE_ERROR) { + php_error_docref(NULL, E_WARNING, "value was not compressed by zstd"); + zend_string_release (buffer); + return NULL; + } else if (length == ZSTD_CONTENTSIZE_UNKNOWN) { + php_error_docref(NULL, E_WARNING, "zstd streaming decompression not supported"); + zend_string_release (buffer); + return NULL; } + decompress_status = !ZSTD_isError(ZSTD_decompress(&buffer->val, buffer->len, payload, payload_len)); + + } + else +#endif + if (is_fastlz) { + decompress_status = ((length = fastlz_decompress(payload, payload_len, &buffer->val, buffer->len)) > 0); + } + else if (is_zlib) { + unsigned long ds = buffer->len; + + decompress_status = (uncompress((Bytef *) buffer->val, &ds, (Bytef *)payload, payload_len) == Z_OK); + buffer->len = ds; } + 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); @@ -3360,38 +3815,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: + 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; @@ -3399,122 +3853,82 @@ 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"); + retval = 0; break; } + zend_string_release(data); - if (payload_emalloc) { - efree(pl); - } return retval; } + PHP_MEMCACHED_API zend_class_entry *php_memc_get_ce(void) { @@ -3528,175 +3942,28 @@ 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; + return zend_ce_exception; } -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 @@ -3705,8 +3972,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); } } } @@ -3716,20 +3983,20 @@ 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) { - return; - } + /* "S" */ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(address) + ZEND_PARSE_PARAMETERS_END(); - 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); @@ -3738,28 +4005,30 @@ PHP_METHOD(MemcachedServer, run) static PHP_METHOD(MemcachedServer, on) { - long event; + zend_long event; zend_fcall_info fci; 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) { - return; - } + /* "lf!" */ + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(event) + Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0) + ZEND_PARSE_PARAMETERS_END(); if (event <= MEMC_SERVER_ON_MIN || event >= MEMC_SERVER_ON_MAX) { RETURN_FALSE; } if (fci.size > 0) { - s_destroy_cb (&MEMC_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_ADDREF(fci.object); } } RETURN_BOOL(rc); @@ -3767,405 +4036,11 @@ PHP_METHOD(MemcachedServer, on) #endif -/* {{{ methods arginfo */ -ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0) - ZEND_ARG_INFO(0, persistent_id) - ZEND_ARG_INFO(0, callback) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getResultCode, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getResultMessage, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_get, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, cache_cb) - ZEND_ARG_INFO(2, cas_token) - ZEND_ARG_INFO(1, udf_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_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_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_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayed, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, keys, 0) - ZEND_ARG_INFO(0, with_cas) - ZEND_ARG_INFO(0, value_cb) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayedByKey, 0, 0, 2) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_ARRAY_INFO(0, keys, 0) - ZEND_ARG_INFO(0, with_cas) - ZEND_ARG_INFO(0, value_cb) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_fetch, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_fetchAll, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_touch, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expiration) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_touchByKey, 0, 0, 3) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expiration) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_setMulti, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, items, 0) - ZEND_ARG_INFO(0, expiration) - ZEND_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) - ZEND_ARG_INFO(0, server_key) - 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) - ZEND_ARG_INFO(0, server_key) - 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) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_appendByKey, 0, 0, 3) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_prepend, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_prependByKey, 0, 0, 3) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_cas, 0, 0, 3) - ZEND_ARG_INFO(0, cas_token) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4) - ZEND_ARG_INFO(0, cas_token) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, expiration) - ZEND_ARG_INFO(0, udf_flags) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_delete, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, time) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMulti, 0, 0, 1) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, time) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteByKey, 0, 0, 2) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, time) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMultiByKey, 0, 0, 2) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, time) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_increment, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, initial_value) - ZEND_ARG_INFO(0, expiry) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_decrement, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, initial_value) - ZEND_ARG_INFO(0, expiry) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_incrementByKey, 0, 0, 2) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, initial_value) - ZEND_ARG_INFO(0, expiry) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_decrementByKey, 0, 0, 2) - ZEND_ARG_INFO(0, server_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, initial_value) - ZEND_ARG_INFO(0, expiry) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0) - ZEND_ARG_INFO(0, delay) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_addServer, 0, 0, 2) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, weight) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_addServers, 0) - ZEND_ARG_ARRAY_INFO(0, servers, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getServerList, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_resetServerList, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_quit, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_flushBuffers, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getServerByKey, 0) - ZEND_ARG_INFO(0, server_key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getLastErrorMessage, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getLastErrorCode, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getLastErrorErrno, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getLastDisconnectedServer, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getOption, 0) - ZEND_ARG_INFO(0, option) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_setSaslAuthData, 0) - ZEND_ARG_INFO(0, username) - ZEND_ARG_INFO(0, password) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_setOption, 0) - ZEND_ARG_INFO(0, option) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_setOptions, 0) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_setBucket, 3) - ZEND_ARG_INFO(0, host_map) - ZEND_ARG_INFO(0, forward_map) - ZEND_ARG_INFO(0, replicas) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getStats, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getVersion, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_isPersistent, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_isPristine, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_getAllKeys, 0) -ZEND_END_ARG_INFO() -/* }}} */ - -/* {{{ memcached_class_methods */ -#define MEMC_ME(name, args) PHP_ME(Memcached, name, args, ZEND_ACC_PUBLIC) -static zend_function_entry memcached_class_methods[] = { - MEMC_ME(__construct, arginfo___construct) - - MEMC_ME(getResultCode, arginfo_getResultCode) - MEMC_ME(getResultMessage, arginfo_getResultMessage) - - MEMC_ME(get, arginfo_get) - MEMC_ME(getByKey, arginfo_getByKey) - MEMC_ME(getMulti, arginfo_getMulti) - MEMC_ME(getMultiByKey, arginfo_getMultiByKey) - MEMC_ME(getDelayed, arginfo_getDelayed) - MEMC_ME(getDelayedByKey, arginfo_getDelayedByKey) - MEMC_ME(fetch, arginfo_fetch) - MEMC_ME(fetchAll, arginfo_fetchAll) - - MEMC_ME(set, arginfo_set) - MEMC_ME(setByKey, arginfo_setByKey) -#ifdef HAVE_MEMCACHED_TOUCH - MEMC_ME(touch, arginfo_touch) - MEMC_ME(touchByKey, arginfo_touchByKey) -#endif - MEMC_ME(setMulti, arginfo_setMulti) - MEMC_ME(setMultiByKey, arginfo_setMultiByKey) - - MEMC_ME(cas, arginfo_cas) - MEMC_ME(casByKey, arginfo_casByKey) - MEMC_ME(add, arginfo_add) - MEMC_ME(addByKey, arginfo_addByKey) - MEMC_ME(append, arginfo_append) - MEMC_ME(appendByKey, arginfo_appendByKey) - MEMC_ME(prepend, arginfo_prepend) - MEMC_ME(prependByKey, arginfo_prependByKey) - MEMC_ME(replace, arginfo_replace) - MEMC_ME(replaceByKey, arginfo_replaceByKey) - MEMC_ME(delete, arginfo_delete) - MEMC_ME(deleteMulti, arginfo_deleteMulti) - MEMC_ME(deleteByKey, arginfo_deleteByKey) - MEMC_ME(deleteMultiByKey, arginfo_deleteMultiByKey) - - MEMC_ME(increment, arginfo_increment) - MEMC_ME(decrement, arginfo_decrement) - MEMC_ME(incrementByKey, arginfo_incrementByKey) - MEMC_ME(decrementByKey, arginfo_decrementByKey) - - MEMC_ME(addServer, arginfo_addServer) - MEMC_ME(addServers, arginfo_addServers) - MEMC_ME(getServerList, arginfo_getServerList) - MEMC_ME(getServerByKey, arginfo_getServerByKey) - MEMC_ME(resetServerList, arginfo_resetServerList) - MEMC_ME(quit, arginfo_quit) - MEMC_ME(flushBuffers, arginfo_flushBuffers) - -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 - MEMC_ME(getLastErrorMessage, arginfo_getLastErrorMessage) - MEMC_ME(getLastErrorCode, arginfo_getLastErrorCode) - MEMC_ME(getLastErrorErrno, arginfo_getLastErrorErrno) -#endif - MEMC_ME(getLastDisconnectedServer, arginfo_getLastDisconnectedServer) - - MEMC_ME(getStats, arginfo_getStats) - MEMC_ME(getVersion, arginfo_getVersion) - MEMC_ME(getAllKeys, arginfo_getAllKeys) - - MEMC_ME(flush, arginfo_flush) - - MEMC_ME(getOption, arginfo_getOption) - MEMC_ME(setOption, arginfo_setOption) - MEMC_ME(setOptions, arginfo_setOptions) - MEMC_ME(setBucket, arginfo_setBucket) -#ifdef HAVE_MEMCACHED_SASL - MEMC_ME(setSaslAuthData, arginfo_setSaslAuthData) -#endif - MEMC_ME(isPersistent, arginfo_isPersistent) - MEMC_ME(isPristine, arginfo_isPristine) - { NULL, NULL, NULL } -}; -#undef MEMC_ME -/* }}} */ - -#ifdef HAVE_MEMCACHED_PROTOCOL -/* {{{ */ -#define MEMC_SE_ME(name, args) PHP_ME(MemcachedServer, name, args, ZEND_ACC_PUBLIC) -static -zend_function_entry memcached_server_class_methods[] = { - MEMC_SE_ME(run, NULL) - MEMC_SE_ME(on, NULL) - { NULL, NULL, NULL } -}; -#undef MEMC_SE_ME -/* }}} */ +#if PHP_VERSION_ID < 80000 +#include "php_memcached_legacy_arginfo.h" +#else +#include "zend_attributes.h" +#include "php_memcached_arginfo.h" #endif /* {{{ memcached_module_entry @@ -4182,10 +4057,8 @@ static const zend_module_dep memcached_deps[] = { #ifdef HAVE_MEMCACHED_MSGPACK ZEND_MOD_REQUIRED("msgpack") #endif -#ifdef HAVE_SPL ZEND_MOD_REQUIRED("spl") -#endif - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif @@ -4193,39 +4066,52 @@ 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 + php_memcached_globals->session.lock_enabled = 0; + php_memcached_globals->session.lock_wait_max = 150; + php_memcached_globals->session.lock_wait_min = 150; + php_memcached_globals->session.lock_retries = 200; + 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.consistent_hash_type = MEMCACHED_BEHAVIOR_KETAMA; + php_memcached_globals->session.consistent_hash_name = NULL; + 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->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; + +#ifdef HAVE_MEMCACHED_PROTOCOL + memset(&php_memcached_globals->server, 0, sizeof(php_memcached_globals->server)); #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.compression_level = 6; + php_memcached_globals->memc.store_retry_count = 2; + php_memcached_globals->memc.item_size_limit = 0; + + 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), @@ -4242,12 +4128,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 @@ -4256,47 +4143,69 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION, MEMC_OPT_COMPRESSION); REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION_TYPE, MEMC_OPT_COMPRESSION_TYPE); + REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION_LEVEL, MEMC_OPT_COMPRESSION_LEVEL); 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); + REGISTER_MEMC_CLASS_CONST_LONG(OPT_ITEM_SIZE_LIMIT, MEMC_OPT_ITEM_SIZE_LIMIT); /* * Indicate whether igbinary serializer is available */ #ifdef HAVE_MEMCACHED_IGBINARY - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_IGBINARY, 1); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_IGBINARY, 1); +#else + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_IGBINARY, 0); +#endif + + /* + * Indicate whether zstd compression is available + */ +#ifdef HAVE_ZSTD_H + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ZSTD, 1); #else - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_IGBINARY, 0); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ZSTD, 0); #endif /* * Indicate whether json serializer is available */ #ifdef HAVE_JSON_API - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_JSON, 1); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_JSON, 1); #else - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_JSON, 0); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_JSON, 0); #endif - /* - * Indicate whether msgpack serializer is available - */ + /* + * Indicate whether msgpack serializer is available + */ #ifdef HAVE_MEMCACHED_MSGPACK - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_MSGPACK, 1); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_MSGPACK, 1); +#else + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_MSGPACK, 0); +#endif + + /* + * Indicate whether set_encoding_key is available + */ +#ifdef HAVE_MEMCACHED_SET_ENCODING_KEY + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ENCODING, 1); #else - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_MSGPACK, 0); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_ENCODING, 0); #endif #ifdef HAVE_MEMCACHED_SESSION - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SESSION, 1); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SESSION, 1); #else - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SESSION, 0); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SESSION, 0); #endif #ifdef HAVE_MEMCACHED_SASL - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SASL, 1); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SASL, 1); #else - REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SASL, 0); + REGISTER_MEMC_CLASS_CONST_BOOL(HAVE_SASL, 0); #endif /* @@ -4316,9 +4225,7 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_LONG(OPT_DISTRIBUTION, MEMCACHED_BEHAVIOR_DISTRIBUTION); REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_MODULA, MEMCACHED_DISTRIBUTION_MODULA); REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_CONSISTENT, MEMCACHED_DISTRIBUTION_CONSISTENT); -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_VIRTUAL_BUCKET, MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET); -#endif REGISTER_MEMC_CLASS_CONST_LONG(OPT_LIBKETAMA_COMPATIBLE, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED); REGISTER_MEMC_CLASS_CONST_LONG(OPT_LIBKETAMA_HASH, MEMCACHED_BEHAVIOR_KETAMA_HASH); REGISTER_MEMC_CLASS_CONST_LONG(OPT_TCP_KEEPALIVE, MEMCACHED_BEHAVIOR_TCP_KEEPALIVE); @@ -4344,14 +4251,10 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_LONG(OPT_SORT_HOSTS, MEMCACHED_BEHAVIOR_SORT_HOSTS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_VERIFY_KEY, MEMCACHED_BEHAVIOR_VERIFY_KEY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_USE_UDP, MEMCACHED_BEHAVIOR_USE_UDP); -#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00037000 REGISTER_MEMC_CLASS_CONST_LONG(OPT_NUMBER_OF_REPLICAS, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_RANDOMIZE_REPLICA_READ, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ); -#endif -#ifdef HAVE_MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS REGISTER_MEMC_CLASS_CONST_LONG(OPT_REMOVE_FAILED_SERVERS, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS); -#endif -#ifdef HAVE_MEMCACHED_BEHAVIOR_SERVER_TIMEOUT_LIMIT +#if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000018 REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERVER_TIMEOUT_LIMIT, MEMCACHED_BEHAVIOR_SERVER_TIMEOUT_LIMIT); #endif @@ -4359,38 +4262,50 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) * libmemcached result codes */ - REGISTER_MEMC_CLASS_CONST_LONG(RES_SUCCESS, MEMCACHED_SUCCESS); - REGISTER_MEMC_CLASS_CONST_LONG(RES_FAILURE, MEMCACHED_FAILURE); - REGISTER_MEMC_CLASS_CONST_LONG(RES_HOST_LOOKUP_FAILURE, MEMCACHED_HOST_LOOKUP_FAILURE); - REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_READ_FAILURE, MEMCACHED_UNKNOWN_READ_FAILURE); - REGISTER_MEMC_CLASS_CONST_LONG(RES_PROTOCOL_ERROR, MEMCACHED_PROTOCOL_ERROR); - REGISTER_MEMC_CLASS_CONST_LONG(RES_CLIENT_ERROR, MEMCACHED_CLIENT_ERROR); - REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_ERROR, MEMCACHED_SERVER_ERROR); - REGISTER_MEMC_CLASS_CONST_LONG(RES_WRITE_FAILURE, MEMCACHED_WRITE_FAILURE); - REGISTER_MEMC_CLASS_CONST_LONG(RES_DATA_EXISTS, MEMCACHED_DATA_EXISTS); - REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTSTORED, MEMCACHED_NOTSTORED); - REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTFOUND, MEMCACHED_NOTFOUND); - REGISTER_MEMC_CLASS_CONST_LONG(RES_PARTIAL_READ, MEMCACHED_PARTIAL_READ); - REGISTER_MEMC_CLASS_CONST_LONG(RES_SOME_ERRORS, MEMCACHED_SOME_ERRORS); - REGISTER_MEMC_CLASS_CONST_LONG(RES_NO_SERVERS, MEMCACHED_NO_SERVERS); - REGISTER_MEMC_CLASS_CONST_LONG(RES_END, MEMCACHED_END); - REGISTER_MEMC_CLASS_CONST_LONG(RES_ERRNO, MEMCACHED_ERRNO); - REGISTER_MEMC_CLASS_CONST_LONG(RES_BUFFERED, MEMCACHED_BUFFERED); - REGISTER_MEMC_CLASS_CONST_LONG(RES_TIMEOUT, MEMCACHED_TIMEOUT); - REGISTER_MEMC_CLASS_CONST_LONG(RES_BAD_KEY_PROVIDED, MEMCACHED_BAD_KEY_PROVIDED); - REGISTER_MEMC_CLASS_CONST_LONG(RES_STORED, MEMCACHED_STORED); - REGISTER_MEMC_CLASS_CONST_LONG(RES_DELETED, MEMCACHED_DELETED); - REGISTER_MEMC_CLASS_CONST_LONG(RES_STAT, MEMCACHED_STAT); - REGISTER_MEMC_CLASS_CONST_LONG(RES_ITEM, MEMCACHED_ITEM); - REGISTER_MEMC_CLASS_CONST_LONG(RES_NOT_SUPPORTED, MEMCACHED_NOT_SUPPORTED); - REGISTER_MEMC_CLASS_CONST_LONG(RES_FETCH_NOTFINISHED, MEMCACHED_FETCH_NOTFINISHED); - REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_MARKED_DEAD, MEMCACHED_SERVER_MARKED_DEAD); - REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_STAT_KEY, MEMCACHED_UNKNOWN_STAT_KEY); - REGISTER_MEMC_CLASS_CONST_LONG(RES_INVALID_HOST_PROTOCOL, MEMCACHED_INVALID_HOST_PROTOCOL); + REGISTER_MEMC_CLASS_CONST_LONG(RES_SUCCESS, MEMCACHED_SUCCESS); + REGISTER_MEMC_CLASS_CONST_LONG(RES_FAILURE, MEMCACHED_FAILURE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_HOST_LOOKUP_FAILURE, MEMCACHED_HOST_LOOKUP_FAILURE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_FAILURE, MEMCACHED_CONNECTION_FAILURE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_BIND_FAILURE, MEMCACHED_CONNECTION_BIND_FAILURE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_WRITE_FAILURE, MEMCACHED_WRITE_FAILURE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_READ_FAILURE, MEMCACHED_READ_FAILURE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_READ_FAILURE, MEMCACHED_UNKNOWN_READ_FAILURE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_PROTOCOL_ERROR, MEMCACHED_PROTOCOL_ERROR); + REGISTER_MEMC_CLASS_CONST_LONG(RES_CLIENT_ERROR, MEMCACHED_CLIENT_ERROR); + REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_ERROR, MEMCACHED_SERVER_ERROR); + REGISTER_MEMC_CLASS_CONST_LONG(RES_DATA_EXISTS, MEMCACHED_DATA_EXISTS); + REGISTER_MEMC_CLASS_CONST_LONG(RES_DATA_DOES_NOT_EXIST, MEMCACHED_DATA_DOES_NOT_EXIST); + REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTSTORED, MEMCACHED_NOTSTORED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_STORED, MEMCACHED_STORED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTFOUND, MEMCACHED_NOTFOUND); + REGISTER_MEMC_CLASS_CONST_LONG(RES_PARTIAL_READ, MEMCACHED_PARTIAL_READ); + REGISTER_MEMC_CLASS_CONST_LONG(RES_SOME_ERRORS, MEMCACHED_SOME_ERRORS); + REGISTER_MEMC_CLASS_CONST_LONG(RES_NO_SERVERS, MEMCACHED_NO_SERVERS); + REGISTER_MEMC_CLASS_CONST_LONG(RES_END, MEMCACHED_END); + REGISTER_MEMC_CLASS_CONST_LONG(RES_DELETED, MEMCACHED_DELETED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_VALUE, MEMCACHED_VALUE); + REGISTER_MEMC_CLASS_CONST_LONG(RES_STAT, MEMCACHED_STAT); + REGISTER_MEMC_CLASS_CONST_LONG(RES_ITEM, MEMCACHED_ITEM); + REGISTER_MEMC_CLASS_CONST_LONG(RES_ERRNO, MEMCACHED_ERRNO); + REGISTER_MEMC_CLASS_CONST_LONG(RES_FAIL_UNIX_SOCKET, MEMCACHED_FAIL_UNIX_SOCKET); + REGISTER_MEMC_CLASS_CONST_LONG(RES_NOT_SUPPORTED, MEMCACHED_NOT_SUPPORTED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_NO_KEY_PROVIDED, MEMCACHED_NO_KEY_PROVIDED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_FETCH_NOTFINISHED, MEMCACHED_FETCH_NOTFINISHED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_TIMEOUT, MEMCACHED_TIMEOUT); + REGISTER_MEMC_CLASS_CONST_LONG(RES_BUFFERED, MEMCACHED_BUFFERED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_BAD_KEY_PROVIDED, MEMCACHED_BAD_KEY_PROVIDED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_INVALID_HOST_PROTOCOL, MEMCACHED_INVALID_HOST_PROTOCOL); + REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_MARKED_DEAD, MEMCACHED_SERVER_MARKED_DEAD); + REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_STAT_KEY, MEMCACHED_UNKNOWN_STAT_KEY); + REGISTER_MEMC_CLASS_CONST_LONG(RES_INVALID_ARGUMENTS, MEMCACHED_INVALID_ARGUMENTS); + REGISTER_MEMC_CLASS_CONST_LONG(RES_PARSE_ERROR, MEMCACHED_PARSE_ERROR); + REGISTER_MEMC_CLASS_CONST_LONG(RES_PARSE_USER_ERROR, MEMCACHED_PARSE_USER_ERROR); + REGISTER_MEMC_CLASS_CONST_LONG(RES_DEPRECATED, MEMCACHED_DEPRECATED); + REGISTER_MEMC_CLASS_CONST_LONG(RES_IN_PROGRESS, MEMCACHED_IN_PROGRESS); + REGISTER_MEMC_CLASS_CONST_LONG(RES_MAXIMUM_RETURN, MEMCACHED_MAXIMUM_RETURN); 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); @@ -4399,7 +4314,7 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_MEMORY_ALLOCATION_FAILURE, MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE); #endif -#if HAVE_MEMCACHED_SASL +#ifdef HAVE_MEMCACHED_SASL REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_PROBLEM, MEMCACHED_AUTH_PROBLEM); REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_FAILURE, MEMCACHED_AUTH_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_CONTINUE, MEMCACHED_AUTH_CONTINUE); @@ -4414,22 +4329,24 @@ 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 */ REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_FASTLZ, COMPRESSION_TYPE_FASTLZ); REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_ZLIB, COMPRESSION_TYPE_ZLIB); + REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_ZSTD, COMPRESSION_TYPE_ZSTD); /* * 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 /* @@ -4469,19 +4386,17 @@ static void php_memc_register_constants(INIT_FUNC_ARGS) REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_ETMPFAIL, PROTOCOL_BINARY_RESPONSE_ETMPFAIL); #endif - #undef REGISTER_MEMC_CLASS_CONST_LONG - /* * Return value from simple get errors */ REGISTER_MEMC_CLASS_CONST_BOOL(GET_ERROR_RETURN_VALUE, 0); -} -/* }}} */ -int php_memc_sess_list_entry(void) -{ - return le_memc_sess; + #undef REGISTER_MEMC_CLASS_CONST_LONG + #undef REGISTER_MEMC_CLASS_CONST_BOOL + #undef REGISTER_MEMC_CLASS_CONST_NULL + } +/* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(memcached) @@ -4489,44 +4404,36 @@ 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 = register_class_Memcached(); + 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 = register_class_MemcachedServer(); 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; } @@ -4535,8 +4442,8 @@ PHP_MINIT_FUNCTION(memcached) /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(memcached) { -#if HAVE_MEMCACHED_SASL - if (MEMC_G(use_sasl)) { +#ifdef HAVE_MEMCACHED_SASL + if (MEMC_G(sasl_initialised)) { sasl_done(); } #endif @@ -4552,9 +4459,24 @@ PHP_MINFO_FUNCTION(memcached) php_info_print_table_start(); php_info_print_table_header(2, "memcached support", "enabled"); php_info_print_table_row(2, "Version", PHP_MEMCACHED_VERSION); - php_info_print_table_row(2, "libmemcached version", memcached_lib_version()); -#if HAVE_MEMCACHED_SASL +#ifdef LIBMEMCACHED_AWESOME + if (strcmp(LIBMEMCACHED_VERSION_STRING, memcached_lib_version())) { + php_info_print_table_row(2, "libmemcached-awesome headers version", LIBMEMCACHED_VERSION_STRING); + php_info_print_table_row(2, "libmemcached-awesome library version", memcached_lib_version()); + } else { + php_info_print_table_row(2, "libmemcached-awesome version", memcached_lib_version()); + } +#else + if (strcmp(LIBMEMCACHED_VERSION_STRING, memcached_lib_version())) { + php_info_print_table_row(2, "libmemcached headers version", LIBMEMCACHED_VERSION_STRING); + php_info_print_table_row(2, "libmemcached library version", memcached_lib_version()); + } else { + php_info_print_table_row(2, "libmemcached version", memcached_lib_version()); + } +#endif + +#ifdef HAVE_MEMCACHED_SASL php_info_print_table_row(2, "SASL support", "yes"); #else php_info_print_table_row(2, "SASL support", "no"); @@ -4584,6 +4506,12 @@ PHP_MINFO_FUNCTION(memcached) php_info_print_table_row(2, "msgpack support", "no"); #endif +#ifdef HAVE_ZSTD_H + php_info_print_table_row(2, "zstd support", "yes"); +#else + php_info_print_table_row(2, "zstd support", "no"); +#endif + php_info_print_table_end(); DISPLAY_INI_ENTRIES(); diff --git a/php_memcached.h b/php_memcached.h index bce8e37b..60b916e9 100644 --- a/php_memcached.h +++ b/php_memcached.h @@ -14,19 +14,23 @@ +----------------------------------------------------------------------+ */ -/* $ Id: $ */ - #ifndef PHP_MEMCACHED_H #define PHP_MEMCACHED_H #include "php.h" +#include "Zend/zend_smart_str.h" + +#ifdef PHP_WIN32 +#include "main/config.w32.h" +#else #include "main/php_config.h" +#endif #ifdef HAVE_CONFIG_H # include "config.h" #endif -#define PHP_MEMCACHED_VERSION "2.2.0" +#define PHP_MEMCACHED_VERSION "3.4.1dev" #if defined(PHP_WIN32) && defined(MEMCACHED_EXPORTS) #define PHP_MEMCACHED_API __declspec(dllexport) @@ -36,7 +40,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.stub.php b/php_memcached.stub.php new file mode 100644 index 00000000..7e85ef32 --- /dev/null +++ b/php_memcached.stub.php @@ -0,0 +1,95 @@ += 80200) + +#if defined(HAVE_MEMCACHED_SASL) + + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setsaslauthdata", sizeof("setsaslauthdata") - 1), 1, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); +#endif +#endif + + return class_entry; +} + +#if defined(HAVE_MEMCACHED_PROTOCOL) +static zend_class_entry *register_class_MemcachedServer(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "MemcachedServer", class_MemcachedServer_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + return class_entry; +} +#endif diff --git a/php_memcached_legacy_arginfo.h b/php_memcached_legacy_arginfo.h new file mode 100644 index 00000000..66d5bb91 --- /dev/null +++ b/php_memcached_legacy_arginfo.h @@ -0,0 +1,433 @@ +/* This is a generated file, edit the .stub.php file instead. + * Stub hash: 75604abd7f58655a9ebda6f0ea579840311c1f08 */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached___construct, 0, 0, 0) + ZEND_ARG_INFO(0, persistent_id) + ZEND_ARG_INFO(0, callback) + ZEND_ARG_INFO(0, connection_str) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getResultCode, 0, 0, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_getResultMessage arginfo_class_Memcached_getResultCode + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_get, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, cache_cb) + ZEND_ARG_INFO(0, get_flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, cache_cb) + ZEND_ARG_INFO(0, get_flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getMulti, 0, 0, 1) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, get_flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getMultiByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, get_flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getDelayed, 0, 0, 1) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, with_cas) + ZEND_ARG_INFO(0, value_cb) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getDelayedByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, with_cas) + ZEND_ARG_INFO(0, value_cb) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_fetch arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_fetchAll arginfo_class_Memcached_getResultCode + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_set, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setByKey, 0, 0, 3) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_touch, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_touchByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setMulti, 0, 0, 1) + ZEND_ARG_INFO(0, items) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setMultiByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, items) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_cas, 0, 0, 3) + ZEND_ARG_INFO(0, cas_token) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_casByKey, 0, 0, 4) + ZEND_ARG_INFO(0, cas_token) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, expiration) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_add arginfo_class_Memcached_set + +#define arginfo_class_Memcached_addByKey arginfo_class_Memcached_setByKey + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_append, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_appendByKey, 0, 0, 3) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_prepend arginfo_class_Memcached_append + +#define arginfo_class_Memcached_prependByKey arginfo_class_Memcached_appendByKey + +#define arginfo_class_Memcached_replace arginfo_class_Memcached_set + +#define arginfo_class_Memcached_replaceByKey arginfo_class_Memcached_setByKey + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_delete, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, time) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_deleteMulti, 0, 0, 1) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, time) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_deleteByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, time) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_deleteMultiByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, keys) + ZEND_ARG_INFO(0, time) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_increment, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, initial_value) + ZEND_ARG_INFO(0, expiry) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_decrement arginfo_class_Memcached_increment + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_incrementByKey, 0, 0, 2) + ZEND_ARG_INFO(0, server_key) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, initial_value) + ZEND_ARG_INFO(0, expiry) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_decrementByKey arginfo_class_Memcached_incrementByKey + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_addServer, 0, 0, 2) + ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, port) + ZEND_ARG_INFO(0, weight) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_addServers, 0, 0, 1) + ZEND_ARG_INFO(0, servers) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_getServerList arginfo_class_Memcached_getResultCode + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getServerByKey, 0, 0, 1) + ZEND_ARG_INFO(0, server_key) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_resetServerList arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_quit arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_flushBuffers arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_getLastErrorMessage arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_getLastErrorCode arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_getLastErrorErrno arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_getLastDisconnectedServer arginfo_class_Memcached_getResultCode + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getStats, 0, 0, 0) + ZEND_ARG_INFO(0, type) +ZEND_END_ARG_INFO() + +#define arginfo_class_Memcached_getVersion arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_getAllKeys arginfo_class_Memcached_getResultCode + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_flush, 0, 0, 0) + ZEND_ARG_INFO(0, delay) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_getOption, 0, 0, 1) + ZEND_ARG_INFO(0, option) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setOption, 0, 0, 2) + ZEND_ARG_INFO(0, option) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setOptions, 0, 0, 1) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setBucket, 0, 0, 3) + ZEND_ARG_INFO(0, host_map) + ZEND_ARG_INFO(0, forward_map) + ZEND_ARG_INFO(0, replicas) +ZEND_END_ARG_INFO() + +#if defined(HAVE_MEMCACHED_SASL) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setSaslAuthData, 0, 0, 2) + ZEND_ARG_INFO(0, username) + ZEND_ARG_INFO(0, password) +ZEND_END_ARG_INFO() +#endif + +#if defined(HAVE_MEMCACHED_SET_ENCODING_KEY) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_setEncodingKey, 0, 0, 1) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() +#endif + +#define arginfo_class_Memcached_isPersistent arginfo_class_Memcached_getResultCode + +#define arginfo_class_Memcached_isPristine arginfo_class_Memcached_getResultCode + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Memcached_checkKey, 0, 0, 1) + ZEND_ARG_INFO(0, key) +ZEND_END_ARG_INFO() + +#if defined(HAVE_MEMCACHED_PROTOCOL) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MemcachedServer_run, 0, 0, 1) + ZEND_ARG_INFO(0, address) +ZEND_END_ARG_INFO() +#endif + +#if defined(HAVE_MEMCACHED_PROTOCOL) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MemcachedServer_on, 0, 0, 2) + ZEND_ARG_INFO(0, event) + ZEND_ARG_INFO(0, callback) +ZEND_END_ARG_INFO() +#endif + + +ZEND_METHOD(Memcached, __construct); +ZEND_METHOD(Memcached, getResultCode); +ZEND_METHOD(Memcached, getResultMessage); +ZEND_METHOD(Memcached, get); +ZEND_METHOD(Memcached, getByKey); +ZEND_METHOD(Memcached, getMulti); +ZEND_METHOD(Memcached, getMultiByKey); +ZEND_METHOD(Memcached, getDelayed); +ZEND_METHOD(Memcached, getDelayedByKey); +ZEND_METHOD(Memcached, fetch); +ZEND_METHOD(Memcached, fetchAll); +ZEND_METHOD(Memcached, set); +ZEND_METHOD(Memcached, setByKey); +ZEND_METHOD(Memcached, touch); +ZEND_METHOD(Memcached, touchByKey); +ZEND_METHOD(Memcached, setMulti); +ZEND_METHOD(Memcached, setMultiByKey); +ZEND_METHOD(Memcached, cas); +ZEND_METHOD(Memcached, casByKey); +ZEND_METHOD(Memcached, add); +ZEND_METHOD(Memcached, addByKey); +ZEND_METHOD(Memcached, append); +ZEND_METHOD(Memcached, appendByKey); +ZEND_METHOD(Memcached, prepend); +ZEND_METHOD(Memcached, prependByKey); +ZEND_METHOD(Memcached, replace); +ZEND_METHOD(Memcached, replaceByKey); +ZEND_METHOD(Memcached, delete); +ZEND_METHOD(Memcached, deleteMulti); +ZEND_METHOD(Memcached, deleteByKey); +ZEND_METHOD(Memcached, deleteMultiByKey); +ZEND_METHOD(Memcached, increment); +ZEND_METHOD(Memcached, decrement); +ZEND_METHOD(Memcached, incrementByKey); +ZEND_METHOD(Memcached, decrementByKey); +ZEND_METHOD(Memcached, addServer); +ZEND_METHOD(Memcached, addServers); +ZEND_METHOD(Memcached, getServerList); +ZEND_METHOD(Memcached, getServerByKey); +ZEND_METHOD(Memcached, resetServerList); +ZEND_METHOD(Memcached, quit); +ZEND_METHOD(Memcached, flushBuffers); +ZEND_METHOD(Memcached, getLastErrorMessage); +ZEND_METHOD(Memcached, getLastErrorCode); +ZEND_METHOD(Memcached, getLastErrorErrno); +ZEND_METHOD(Memcached, getLastDisconnectedServer); +ZEND_METHOD(Memcached, getStats); +ZEND_METHOD(Memcached, getVersion); +ZEND_METHOD(Memcached, getAllKeys); +ZEND_METHOD(Memcached, flush); +ZEND_METHOD(Memcached, getOption); +ZEND_METHOD(Memcached, setOption); +ZEND_METHOD(Memcached, setOptions); +ZEND_METHOD(Memcached, setBucket); +#if defined(HAVE_MEMCACHED_SASL) +ZEND_METHOD(Memcached, setSaslAuthData); +#endif +#if defined(HAVE_MEMCACHED_SET_ENCODING_KEY) +ZEND_METHOD(Memcached, setEncodingKey); +#endif +ZEND_METHOD(Memcached, isPersistent); +ZEND_METHOD(Memcached, isPristine); +ZEND_METHOD(Memcached, checkKey); +#if defined(HAVE_MEMCACHED_PROTOCOL) +ZEND_METHOD(MemcachedServer, run); +#endif +#if defined(HAVE_MEMCACHED_PROTOCOL) +ZEND_METHOD(MemcachedServer, on); +#endif + + +static const zend_function_entry class_Memcached_methods[] = { + ZEND_ME(Memcached, __construct, arginfo_class_Memcached___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getResultCode, arginfo_class_Memcached_getResultCode, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getResultMessage, arginfo_class_Memcached_getResultMessage, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, get, arginfo_class_Memcached_get, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getByKey, arginfo_class_Memcached_getByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getMulti, arginfo_class_Memcached_getMulti, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getMultiByKey, arginfo_class_Memcached_getMultiByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getDelayed, arginfo_class_Memcached_getDelayed, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getDelayedByKey, arginfo_class_Memcached_getDelayedByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, fetch, arginfo_class_Memcached_fetch, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, fetchAll, arginfo_class_Memcached_fetchAll, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, set, arginfo_class_Memcached_set, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, setByKey, arginfo_class_Memcached_setByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, touch, arginfo_class_Memcached_touch, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, touchByKey, arginfo_class_Memcached_touchByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, setMulti, arginfo_class_Memcached_setMulti, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, setMultiByKey, arginfo_class_Memcached_setMultiByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, cas, arginfo_class_Memcached_cas, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, casByKey, arginfo_class_Memcached_casByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, add, arginfo_class_Memcached_add, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, addByKey, arginfo_class_Memcached_addByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, append, arginfo_class_Memcached_append, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, appendByKey, arginfo_class_Memcached_appendByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, prepend, arginfo_class_Memcached_prepend, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, prependByKey, arginfo_class_Memcached_prependByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, replace, arginfo_class_Memcached_replace, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, replaceByKey, arginfo_class_Memcached_replaceByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, delete, arginfo_class_Memcached_delete, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, deleteMulti, arginfo_class_Memcached_deleteMulti, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, deleteByKey, arginfo_class_Memcached_deleteByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, deleteMultiByKey, arginfo_class_Memcached_deleteMultiByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, increment, arginfo_class_Memcached_increment, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, decrement, arginfo_class_Memcached_decrement, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, incrementByKey, arginfo_class_Memcached_incrementByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, decrementByKey, arginfo_class_Memcached_decrementByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, addServer, arginfo_class_Memcached_addServer, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, addServers, arginfo_class_Memcached_addServers, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getServerList, arginfo_class_Memcached_getServerList, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getServerByKey, arginfo_class_Memcached_getServerByKey, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, resetServerList, arginfo_class_Memcached_resetServerList, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, quit, arginfo_class_Memcached_quit, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, flushBuffers, arginfo_class_Memcached_flushBuffers, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getLastErrorMessage, arginfo_class_Memcached_getLastErrorMessage, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getLastErrorCode, arginfo_class_Memcached_getLastErrorCode, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getLastErrorErrno, arginfo_class_Memcached_getLastErrorErrno, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getLastDisconnectedServer, arginfo_class_Memcached_getLastDisconnectedServer, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getStats, arginfo_class_Memcached_getStats, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getVersion, arginfo_class_Memcached_getVersion, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getAllKeys, arginfo_class_Memcached_getAllKeys, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, flush, arginfo_class_Memcached_flush, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, getOption, arginfo_class_Memcached_getOption, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, setOption, arginfo_class_Memcached_setOption, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, setOptions, arginfo_class_Memcached_setOptions, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, setBucket, arginfo_class_Memcached_setBucket, ZEND_ACC_PUBLIC) +#if defined(HAVE_MEMCACHED_SASL) + ZEND_ME(Memcached, setSaslAuthData, arginfo_class_Memcached_setSaslAuthData, ZEND_ACC_PUBLIC) +#endif +#if defined(HAVE_MEMCACHED_SET_ENCODING_KEY) + ZEND_ME(Memcached, setEncodingKey, arginfo_class_Memcached_setEncodingKey, ZEND_ACC_PUBLIC) +#endif + ZEND_ME(Memcached, isPersistent, arginfo_class_Memcached_isPersistent, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, isPristine, arginfo_class_Memcached_isPristine, ZEND_ACC_PUBLIC) + ZEND_ME(Memcached, checkKey, arginfo_class_Memcached_checkKey, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + +#if defined(HAVE_MEMCACHED_PROTOCOL) +static const zend_function_entry class_MemcachedServer_methods[] = { + ZEND_ME(MemcachedServer, run, arginfo_class_MemcachedServer_run, ZEND_ACC_PUBLIC) + ZEND_ME(MemcachedServer, on, arginfo_class_MemcachedServer_on, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; +#endif + +static zend_class_entry *register_class_Memcached(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Memcached", class_Memcached_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + return class_entry; +} + +#if defined(HAVE_MEMCACHED_PROTOCOL) +static zend_class_entry *register_class_MemcachedServer(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "MemcachedServer", class_MemcachedServer_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + return class_entry; +} +#endif diff --git a/php_memcached_private.h b/php_memcached_private.h index 0ec2d76e..b7d2a5e1 100644 --- a/php_memcached_private.h +++ b/php_memcached_private.h @@ -14,12 +14,14 @@ +----------------------------------------------------------------------+ */ -/* $ Id: $ */ - #ifndef PHP_MEMCACHED_PRIVATE_H #define PHP_MEMCACHED_PRIVATE_H +#ifdef PHP_WIN32 +#include "main/config.w32.h" +#else #include "main/php_config.h" +#endif #ifdef HAVE_CONFIG_H # include "config.h" @@ -41,12 +43,20 @@ #include #include #include -#include +#if PHP_VERSION_ID < 70200 +#include +#else +#include +#endif #include #include #ifdef PHP_WIN32 -# include "win32/php_stdint.h" + # if PHP_VERSION_ID >= 80000 + # include + #else + # include "win32/php_stdint.h" + #endif #else /* Used to store the size of the block */ # if defined(HAVE_INTTYPES_H) @@ -72,32 +82,48 @@ typedef unsigned long int uint32_t; # endif #endif +/* Backwards compatibility for GC API change in PHP 7.3 */ +#if PHP_VERSION_ID < 70300 +# define GC_ADDREF(p) ++GC_REFCOUNT(p) +# define GC_DELREF(p) --GC_REFCOUNT(p) +# define GC_SET_REFCOUNT(p, rc) GC_REFCOUNT(p) = rc +#endif + /**************************************** 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, -}; -#ifdef HAVE_MEMCACHED_IGBINARY -#define SERIALIZER_DEFAULT SERIALIZER_IGBINARY -#define SERIALIZER_DEFAULT_NAME "igbinary" -#elif HAVE_MEMCACHED_MSGPACK -#define SERIALIZER_DEFAULT SERIALIZER_MSGPACK -#define SERIALIZER_DEFAULT_NAME "msgpack" + SERIALIZER_MSGPACK = 5 +} php_memc_serializer_type; + +typedef enum { + COMPRESSION_TYPE_ZLIB = 1, + COMPRESSION_TYPE_FASTLZ = 2, + COMPRESSION_TYPE_ZSTD = 3 +} php_memc_compression_type; + +typedef struct { + const char *name; + php_memc_serializer_type type; +} php_memc_serializer; + +#if defined(HAVE_MEMCACHED_IGBINARY) +# define SERIALIZER_DEFAULT SERIALIZER_IGBINARY +# define SERIALIZER_DEFAULT_NAME "igbinary" +#elif defined(HAVE_MEMCACHED_MSGPACK) +# define SERIALIZER_DEFAULT SERIALIZER_MSGPACK +# define SERIALIZER_DEFAULT_NAME "msgpack" #else -#define SERIALIZER_DEFAULT SERIALIZER_PHP -#define SERIALIZER_DEFAULT_NAME "php" +# define SERIALIZER_DEFAULT SERIALIZER_PHP +# define SERIALIZER_DEFAULT_NAME "php" #endif /* HAVE_MEMCACHED_IGBINARY / HAVE_MEMCACHED_MSGPACK */ -#if LIBMEMCACHED_WITH_SASL_SUPPORT -# if defined(HAVE_SASL_SASL_H) -# include -# define HAVE_MEMCACHED_SASL 1 -# endif +#ifdef HAVE_MEMCACHED_SASL +# include #endif #ifdef HAVE_MEMCACHED_PROTOCOL @@ -129,68 +155,97 @@ 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; + char *consistent_hash_name; + int consistent_hash_type; + + zend_long server_failure_limit; + zend_long number_of_replicas; + zend_bool randomize_replica_read_enabled; + zend_bool remove_failed_servers_enabled; + + zend_long connect_timeout; - char *compression_type; - int compression_type_real; - int compression_threshold; + char *prefix; + zend_bool persistent_enabled; - double compression_factor; -#if HAVE_MEMCACHED_SASL - zend_bool use_sasl; + 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; + zend_long compression_level; + zend_long item_size_limit; + + /* 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) +/* Globals accessor macros */ +#ifdef ZTS +# define MEMC_G(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, memc.v) +# define MEMC_SERVER_G(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, server.v) +# define MEMC_SESS_INI(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, session.v) +#else +# define MEMC_G(v) (php_memcached_globals.memc.v) +# define MEMC_SERVER_G(v) (php_memcached_globals.server.v) +# 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) + PHP_RINIT_FUNCTION(memcached); PHP_RSHUTDOWN_FUNCTION(memcached); 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..24c328f8 100644 --- a/php_memcached_server.c +++ b/php_memcached_server.c @@ -17,31 +17,26 @@ #include "php_memcached.h" #include "php_memcached_private.h" #include "php_memcached_server.h" +#include "php_network.h" #include -#undef NDEBUG -#undef _NDEBUG #include -#define MEMC_GET_CB(cb_type) (MEMC_G(server.callbacks)[cb_type]) +#define MEMC_GET_CB(cb_type) (MEMC_SERVER_G(callbacks)[cb_type]) #define MEMC_HAS_CB(cb_type) (MEMC_GET_CB(cb_type).fci.size > 0) #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,29 +55,24 @@ 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; + zval retval; - cb->fci.params = params; + cb->fci.retval = &retval; + cb->fci.params = params; cb->fci.param_count = param_count; +#if PHP_VERSION_ID < 80000 + cb->fci.no_separation = 1; +#endif - /* Call the cb */ - 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); - } - return retval; + + return Z_ISUNDEF(retval) ? PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND : zval_get_long(&retval); } // memcached protocol callbacks @@ -91,10 +81,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 +90,30 @@ 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); - - MAKE_STD_ZVAL(zresult_cas); - ZVAL_NULL(zresult_cas); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_STRINGL(&zvalue, data, data_len); + ZVAL_LONG(&zflags, flags); + ZVAL_LONG(&zexptime, exptime); + ZVAL_NULL(&zresult_cas); + ZVAL_MAKE_REF(&zresult_cas); - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zvalue; - params [3] = &zflags; - params [4] = &zexptime; - params [5] = &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); - 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 +129,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 +138,27 @@ 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); - - MAKE_STD_ZVAL(zcas); - ZVAL_DOUBLE(zcas, cas); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_STRINGL(&zvalue, data, data_len); + ZVAL_DOUBLE(&zcas, cas); + ZVAL_NULL(&zresult_cas); + ZVAL_MAKE_REF(&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], &zcas); + ZVAL_COPY(¶ms[4], &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 +189,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 +198,36 @@ 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, (zend_long) delta); + ZVAL_LONG(&zinital, (zend_long) initial); + ZVAL_LONG(&zexpiration, (zend_long) expiration); + ZVAL_LONG(&zresult, 0); + ZVAL_MAKE_REF(&zresult); + ZVAL_NULL(&zresult_cas); + ZVAL_MAKE_REF(&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,27 +291,25 @@ 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; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); + ZVAL_LONG(&zwhen, when); - MAKE_STD_ZVAL(zwhen); - ZVAL_LONG(zwhen, (long) when); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zwhen); - params [0] = &zcookie; - params [1] = &zwhen; + retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_FLUSH), params, 2); - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_FLUSH), params, 2 TSRMLS_CC); - - 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,63 +318,66 @@ 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; } 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); - - 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); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_NULL(&zvalue); + ZVAL_MAKE_REF(&zvalue); + ZVAL_NULL(&zflags); + ZVAL_MAKE_REF(&zflags); + ZVAL_NULL(&zresult_cas); + ZVAL_MAKE_REF(&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); + + 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 +390,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 +399,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 +411,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 +421,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 +437,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 +446,33 @@ 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); + ZVAL_STRINGL(&zkey, key, key_len); + ZVAL_STRINGL(&zdata, data, data_len); + ZVAL_LONG(&zflags, (zend_long) flags); + ZVAL_LONG(&zexpiration, (zend_long) expiration); + ZVAL_DOUBLE(&zcas, (double) cas); + ZVAL_NULL(&zresult_cas); + ZVAL_MAKE_REF(&zresult_cas); - MAKE_STD_ZVAL(zexpiration); - ZVAL_LONG(zexpiration, (long) expiration); + 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); - MAKE_STD_ZVAL(zcas); - ZVAL_DOUBLE(zcas, (double) cas); - - MAKE_STD_ZVAL(zresult_cas); - ZVAL_NULL(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 +504,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, zstats; if (!MEMC_HAS_CB(MEMC_SERVER_ON_STAT)) { return retval; @@ -547,32 +514,57 @@ 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); - - MAKE_STD_ZVAL(zbody); - ZVAL_NULL(zbody); + if (key && key_len) { + ZVAL_STRINGL(&zkey, key, key_len); + } else { + ZVAL_NULL(&zkey); + } + array_init(&zstats); + ZVAL_MAKE_REF(&zstats); - params [0] = &zcookie; - params [1] = &zkey; - params [2] = &zbody; + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zkey); + ZVAL_COPY(¶ms[2], &zstats); - 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) { - retval = response_handler(cookie, NULL, 0, NULL, 0); + zval *zarray = &zstats; + zend_string *key; + zend_long idx; + zval *val; + + ZVAL_DEREF(zarray); + if (Z_TYPE_P(zarray) != IS_ARRAY) { + convert_to_array(zarray); } - else { - if (Z_TYPE_P (zbody) != IS_STRING) { - convert_to_string (zbody); + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zarray), idx, key, val) + { + zend_string *val_str = zval_get_string(val); + + if (key) { + retval = response_handler(cookie, key->val, key->len, val_str->val, val_str->len); + } else { + char buf[0x20], *ptr, *end = &buf[sizeof(buf) - 1]; + ptr = zend_print_long_to_buf(end, idx); + retval = response_handler(cookie, ptr, end - ptr, val_str->val, val_str->len); + } + zend_string_release(val_str); + + if (retval != PROTOCOL_BINARY_RESPONSE_SUCCESS) { + break; } - retval = response_handler(cookie, key, key_len, Z_STRVAL_P (zbody), (uint32_t) Z_STRLEN_P (zbody)); } + ZEND_HASH_FOREACH_END(); } + + 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); + zval_ptr_dtor (&zstats); return retval; } @@ -580,11 +572,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 +582,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); + ZVAL_MAKE_REF(&zversion); - params [0] = &zcookie; - params [1] = &zversion; - - retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_VERSION), params, 2 TSRMLS_CC); + ZVAL_COPY(¶ms[0], &zcookie); + ZVAL_COPY(¶ms[1], &zversion); + 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)); + retval = response_handler (cookie, Z_STRVAL(zversion), (uint32_t) Z_STRLEN(zversion)); } + + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zversion); return retval; @@ -623,36 +615,27 @@ 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]; + zend_string *zremoteaddr_str; + zval zremoteaddr; + zval params[1]; 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); + ZVAL_NULL(&zremoteaddr); - 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)); + if (SUCCESS == php_network_get_peer_name (fd, &zremoteaddr_str, NULL, NULL)) { + ZVAL_STR(&zremoteaddr, zremoteaddr_str); } 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)); } - params [0] = &zremoteip; - params [1] = &zremoteport; + ZVAL_COPY(¶ms[0], &zremoteaddr); - 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, 1); - zval_ptr_dtor (&zremoteip); - zval_ptr_dtor (&zremoteport); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(&zremoteaddr); if (retval != PROTOCOL_BINARY_RESPONSE_SUCCESS) { memcached_protocol_client_destroy (client->protocol_client); @@ -683,7 +666,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 +681,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 +696,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 +706,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); @@ -763,90 +744,84 @@ php_memc_proto_handler_t *php_memc_proto_handler_new () } static -evutil_socket_t s_create_listening_socket (const char *spec) +evutil_socket_t s_create_listening_socket (const zend_string *spec) { evutil_socket_t sock; struct sockaddr_storage addr; - int addr_len; - + socklen_t addr_len; 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"); + if (SUCCESS != php_network_parse_network_address_with_port(spec->val, spec->len, (struct sockaddr *) &addr, &addr_len)) { + php_error_docref(NULL, E_WARNING, "Failed to parse bind address: %s", spec->val); return -1; } - sock = socket (AF_INET, SOCK_STREAM, 0); + sock = socket (addr.ss_family, 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(); - if (sock == -1) { return 0; } 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 +840,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 a3ef5678..e509cb84 100644 --- a/php_memcached_session.c +++ b/php_memcached_session.c @@ -18,275 +18,404 @@ #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 + +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); + +#ifdef 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_STRL("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; + + 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; - } - if (lock_wait > 0) { - usleep(lock_wait); + 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; +/* This macro looks like a function but returns errors directly */ +#define check_set_behavior(behavior, value) \ +{ \ + int b = (behavior); \ + uint64_t v = (value); \ + if (v != memcached_behavior_get(memc, b)) { \ + memcached_return rc; \ + if ((rc = memcached_behavior_set(memc, b, v)) != MEMCACHED_SUCCESS) { \ + if (!silent) { \ + php_error_docref(NULL, E_WARNING, "failed to initialise session memcached configuration: %s", memcached_strerror(memc, rc)); \ + } \ + return 0; \ + } \ + } \ +} - if (!strncmp((char *)save_path, "PERSISTENT=", sizeof("PERSISTENT=") - 1)) { - zend_rsrc_list_entry *le = NULL; - char *e; + if (MEMC_SESS_INI(binary_protocol_enabled)) { + check_set_behavior(MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); + /* Also enable TCP_NODELAY when binary protocol is enabled */ + check_set_behavior(MEMCACHED_BEHAVIOR_TCP_NODELAY, 1); + } - 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(consistent_hash_enabled)) { + check_set_behavior(MEMC_SESS_INI(consistent_hash_type), 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 check_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; + + // Fail on incompatible PERSISTENT identifier (removed in php-memcached 3.0) + if (strstr(save_path, "PERSISTENT=")) { + php_error_docref(NULL, E_WARNING, "failed to parse session.save_path: PERSISTENT is replaced by memcached.sess_persistent = On"); + PS_SET_MOD_DATA(NULL); + return FAILURE; + } + + // 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); + memcached_server_list_free(servers); + 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_SET_REFCOUNT(&le, 1); + + /* plist_key is not a persistent allocated key, thus we use str_update here */ + if (zend_hash_str_update_mem(&EG(persistent_list), 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(); + php_memcached_user_data *user_data; + memcached_st *memc = PS_GET_MOD_DATA(); - if (MEMC_G(sess_locking_enabled)) { - php_memc_sess_unlock(memc_sess->memc_sess TSRMLS_CC); + if (!memc) { + php_error_docref(NULL, E_WARNING, "Session is not allocated, check session.save_path value"); + return FAILURE; } - 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); + + user_data = memcached_get_user_data(memc); + + if (user_data->is_locked) { + s_unlock_session(memc); } + if (!user_data->is_persistent) { + s_destroy_mod_data(memc); + } + + PS_SET_MOD_DATA(NULL); return SUCCESS; } @@ -294,84 +423,86 @@ 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(); - 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 (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); + zend_bool *is_persistent = memcached_get_user_data(memc); + *val = zend_string_init(payload, payload_len, 0); + pefree(payload, *is_persistent); + return SUCCESS; + } else if (status == MEMCACHED_NOTFOUND) { + *val = ZSTR_EMPTY_ALLOC(); return SUCCESS; } else { + php_error_docref(NULL, E_WARNING, "error getting session from memcached: %s", memcached_last_error_message(memc)); return FAILURE; } } 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; + 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--; + php_error_docref(NULL, E_WARNING, "error saving session to memcached: %s", memcached_last_error_message(memc)); } - } 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(); - 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); + if (!memc) { + php_error_docref(NULL, E_WARNING, "Session is not allocated, check session.save_path value"); + return FAILURE; } + 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; } @@ -379,4 +510,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 (php_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/server-example/run-server.php b/server-example/run-server.php index a02c6a60..b7612c80 100644 --- a/server-example/run-server.php +++ b/server-example/run-server.php @@ -3,8 +3,8 @@ $server = new MemcachedServer(); $server->on (Memcached::ON_CONNECT, - function ($remote_ip, $remote_port) { - echo "Incoming connection from {$remote_ip}:{$remote_port}" . PHP_EOL; + function ($remote_addr) { + echo "Incoming connection from {$remote_addr}" . PHP_EOL; return Memcached::RESPONSE_SUCCESS; }); @@ -89,4 +89,4 @@ function ($client_id) { return Memcached::RESPONSE_SUCCESS; }); -$server->run ("127.0.0.1:3434"); \ No newline at end of file +$server->run ("127.0.0.1:3434"); 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-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $m->delete('foo'); var_dump($m->addByKey('foo', 'foo', 1, 10)); @@ -15,7 +15,7 @@ echo $m->getResultMessage(), "\n"; var_dump($m->addByKey('foo', '', 1, 10)); echo $m->getResultMessage(), "\n"; // This is OK for the binary protocol -$rv = $m->addByKey('foo', ' asd ���', 1, 1); +$rv = $m->addByKey('foo', ' asd åäö', 1, 1); if ($m->getOption(Memcached::OPT_BINARY_PROTOCOL)) { if ($rv !== true and $m->getResultCode() !== Memcached::RES_SUCCESS) { var_dump($rv); diff --git a/tests/experimental/addserver_unixdomain.phpt b/tests/addserver_unixdomain.phpt similarity index 87% rename from tests/experimental/addserver_unixdomain.phpt rename to tests/addserver_unixdomain.phpt index aaec92ae..7e16834c 100644 --- a/tests/experimental/addserver_unixdomain.phpt +++ b/tests/addserver_unixdomain.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::addServer() unix doamin socket --SKIPIF-- - + --CLEAN-- + --FILE-- delete('foo'); $m->setOption(Memcached::OPT_COMPRESSION, true); var_dump($m->append('foo', 'a')); -echo $php_errormsg, "\n"; +echo error_get_last()["message"], "\n"; $m->setOption(Memcached::OPT_COMPRESSION, false); $m->delete('foo'); diff --git a/tests/experimental/append_bykey.phpt b/tests/append_bykey.phpt similarity index 76% rename from tests/experimental/append_bykey.phpt rename to tests/append_bykey.phpt index 9e18f3b1..d02c257d 100644 --- a/tests/experimental/append_bykey.phpt +++ b/tests/append_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::appendByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include 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/bad_construct.phpt b/tests/bad_construct.phpt index 06bc7236..0c278796 100644 --- a/tests/bad_construct.phpt +++ b/tests/bad_construct.phpt @@ -1,12 +1,15 @@ --TEST-- Memcached construct with bad arguments --SKIPIF-- - += 80000) die("skip PHP 7 only"); +?> --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/bad_construct_8.phpt b/tests/bad_construct_8.phpt new file mode 100644 index 00000000..10928cfd --- /dev/null +++ b/tests/bad_construct_8.phpt @@ -0,0 +1,36 @@ +--TEST-- +Memcached construct with bad arguments +--SKIPIF-- + +--FILE-- +getMessage() . PHP_EOL; +} + +class extended extends Memcached { + public function __construct () { + } +} + +error_reporting(E_ALL); +try { + $extended = new extended (); + var_dump ($extended->setOption (Memcached::OPT_BINARY_PROTOCOL, true)); +} catch (Error $e) { + echo $e->getMessage() . PHP_EOL; +} + +echo "OK" . PHP_EOL; + +--EXPECTF-- +Memcached::__construct(): Argument #1 ($persistent_id) must be of type ?string, stdClass given +Memcached constructor was not called +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/experimental/cas_bykey.phpt b/tests/cas_bykey.phpt similarity index 67% rename from tests/experimental/cas_bykey.phpt rename to tests/cas_bykey.phpt index f33e24ad..32808813 100644 --- a/tests/experimental/cas_bykey.phpt +++ b/tests/cas_bykey.phpt @@ -1,19 +1,19 @@ --TEST-- Memcached::casByKey() --SKIPIF-- - + --FILE-- addServer('127.0.0.1', 11211, 1); +include 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/cas_e2big.phpt b/tests/cas_e2big.phpt new file mode 100644 index 00000000..99c3562b --- /dev/null +++ b/tests/cas_e2big.phpt @@ -0,0 +1,32 @@ +--TEST-- +set data exceeding size limit +--SKIPIF-- + +--FILE-- + 100, +)); + +$m->delete('cas_e2big_test'); + +$m->set('cas_e2big_test', 'hello'); +$result = $m->get('cas_e2big_test', null, Memcached::GET_EXTENDED); +var_dump(is_array($result) && isset($result['cas']) && isset($result['value']) && $result['value'] == 'hello'); + +$value = str_repeat('a large payload', 1024 * 1024); + +var_dump($m->cas($result['cas'], 'cas_e2big_test', $value, 360)); +var_dump($m->getResultCode() == Memcached::RES_E2BIG); +var_dump($m->getResultMessage() == 'ITEM TOO BIG'); +var_dump($m->get('cas_e2big_test') == 'hello'); +var_dump($m->getResultCode() == Memcached::RES_SUCCESS); +?> +--EXPECT-- +bool(true) +bool(false) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/cas_invalid_key.phpt b/tests/cas_invalid_key.phpt new file mode 100644 index 00000000..9cb7293c --- /dev/null +++ b/tests/cas_invalid_key.phpt @@ -0,0 +1,24 @@ +--TEST-- +Memcached::cas() with strange key +--SKIPIF-- + +--FILE-- + false, + Memcached::OPT_VERIFY_KEY => true +)); + +error_reporting(0); +var_dump($m->cas(0, '', true, 10)); +echo $m->getResultMessage(), "\n"; + +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) +A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE 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-- + false, + Memcached::OPT_VERIFY_KEY => true + )); + +$keys = [ + 'foo', + 'foo bar', + str_repeat('a',65), + str_repeat('b',250), + str_repeat('c',251), + 'Montréal', + 'København', + 'Düsseldorf', + 'Kraków', + 'İstanbul', + 'ﺎﺨﺘﺑﺍﺭ PHP', + '測試', + 'Тестирование', + 'پی ایچ پی کی جانچ ہو رہی ہے', + 'Testataan PHP: tä', + 'Að prófa PHP', + 'د پی ایچ پی ازمول', + 'Pruvà PHP' +]; +foreach($keys as $key) { + echo "Checking \"$key\"" . PHP_EOL; + echo "MEMC_CHECK_KEY: "; + var_dump($m->checkKey($key)); + echo "libmemcached: "; + var_dump($m->set($key, "this is a test")); + var_dump($m->getResultMessage()); + echo "\n"; +} +--EXPECT-- +Checking "foo" +MEMC_CHECK_KEY: bool(true) +libmemcached: bool(true) +string(7) "SUCCESS" + +Checking "foo bar" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +MEMC_CHECK_KEY: bool(true) +libmemcached: bool(true) +string(7) "SUCCESS" + +Checking "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" +MEMC_CHECK_KEY: bool(true) +libmemcached: bool(true) +string(7) "SUCCESS" + +Checking "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "Montréal" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "København" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "Düsseldorf" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "Kraków" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "İstanbul" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "ﺎﺨﺘﺑﺍﺭ PHP" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "測試" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "Тестирование" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "پی ایچ پی کی جانچ ہو رہی ہے" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "Testataan PHP: tä" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "Að prófa PHP" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "د پی ایچ پی ازمول" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + +Checking "Pruvà PHP" +MEMC_CHECK_KEY: bool(false) +libmemcached: bool(false) +string(46) "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE" + diff --git a/tests/clone.phpt b/tests/clone.phpt index a0c49cd7..22e14376 100644 --- a/tests/clone.phpt +++ b/tests/clone.phpt @@ -1,7 +1,7 @@ --TEST-- Test cloning --SKIPIF-- - + --FILE-- +--FILE-- +setOption(Memcached::OPT_COMPRESSION, false); + } else { + $m->setOption(Memcached::OPT_COMPRESSION, true); + $m->setOption(Memcached::OPT_COMPRESSION_TYPE, get_compression($set_compression)); + } + + $m->set($key, $value, 1800); + + $value_back = $m->get($key); + var_dump($value === $value_back); +} + +fetch_with_compression($m, 'hello01', $data, 'zlib', 1.3, 4); +fetch_with_compression($m, 'hello02', $data, 'fastlz', 1.3, 4); +fetch_with_compression($m, 'hello03', $data, '', 1.3, 4); +fetch_with_compression($m, 'hello04', $short_data, 'zlib', 1.3, 4); +fetch_with_compression($m, 'hello05', $short_data, 'fastlz', 1.3, 4); +fetch_with_compression($m, 'hello06', $short_data, '', 1.3, 4); +fetch_with_compression($m, 'hello11', $data, 'zlib', 0.3, 4); +fetch_with_compression($m, 'hello12', $data, 'fastlz', 0.3, 4); +fetch_with_compression($m, 'hello13', $data, '', 0.3, 4); +fetch_with_compression($m, 'hello14', $short_data, 'zlib', 0.3, 4); +fetch_with_compression($m, 'hello15', $short_data, 'fastlz', 0.3, 4); +fetch_with_compression($m, 'hello16', $short_data, '', 0.3, 4); +fetch_with_compression($m, 'hello21', $data, 'zlib', 1.3, 2000); +fetch_with_compression($m, 'hello22', $data, 'fastlz', 1.3, 2000); +fetch_with_compression($m, 'hello23', $data, '', 1.3, 2000); +fetch_with_compression($m, 'hello24', $short_data, 'zlib', 1.3, 2000); +fetch_with_compression($m, 'hello25', $short_data, 'fastlz', 1.3, 2000); +fetch_with_compression($m, 'hello26', $short_data, '', 1.3, 2000); +fetch_with_compression($m, 'hello31', $data, 'zlib', 0.3, 2000); +fetch_with_compression($m, 'hello32', $data, 'fastlz', 0.3, 2000); +fetch_with_compression($m, 'hello33', $data, '', 0.3, 2000); +fetch_with_compression($m, 'hello34', $short_data, 'zlib', 0.3, 2000); +fetch_with_compression($m, 'hello35', $short_data, 'fastlz', 0.3, 2000); +fetch_with_compression($m, 'hello36', $short_data, '', 0.3, 2000); +?> +--EXPECT-- +len=[4877] set=[zlib] factor=[1.3] threshold=[4] +bool(true) +len=[4877] set=[fastlz] factor=[1.3] threshold=[4] +bool(true) +len=[4877] set=[] factor=[1.3] threshold=[4] +bool(true) +len=[7] set=[zlib] factor=[1.3] threshold=[4] +bool(true) +len=[7] set=[fastlz] factor=[1.3] threshold=[4] +bool(true) +len=[7] set=[] factor=[1.3] threshold=[4] +bool(true) +len=[4877] set=[zlib] factor=[0.3] threshold=[4] +bool(true) +len=[4877] set=[fastlz] factor=[0.3] threshold=[4] +bool(true) +len=[4877] set=[] factor=[0.3] threshold=[4] +bool(true) +len=[7] set=[zlib] factor=[0.3] threshold=[4] +bool(true) +len=[7] set=[fastlz] factor=[0.3] threshold=[4] +bool(true) +len=[7] set=[] factor=[0.3] threshold=[4] +bool(true) +len=[4877] set=[zlib] factor=[1.3] threshold=[2000] +bool(true) +len=[4877] set=[fastlz] factor=[1.3] threshold=[2000] +bool(true) +len=[4877] set=[] factor=[1.3] threshold=[2000] +bool(true) +len=[7] set=[zlib] factor=[1.3] threshold=[2000] +bool(true) +len=[7] set=[fastlz] factor=[1.3] threshold=[2000] +bool(true) +len=[7] set=[] factor=[1.3] threshold=[2000] +bool(true) +len=[4877] set=[zlib] factor=[0.3] threshold=[2000] +bool(true) +len=[4877] set=[fastlz] factor=[0.3] threshold=[2000] +bool(true) +len=[4877] set=[] factor=[0.3] threshold=[2000] +bool(true) +len=[7] set=[zlib] factor=[0.3] threshold=[2000] +bool(true) +len=[7] set=[fastlz] factor=[0.3] threshold=[2000] +bool(true) +len=[7] set=[] factor=[0.3] threshold=[2000] +bool(true) diff --git a/tests/compression_types.phpt b/tests/compression_types.phpt index a7c8a219..81d7867c 100644 --- a/tests/compression_types.phpt +++ b/tests/compression_types.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached compression test --SKIPIF-- - + --FILE-- --EXPECT-- set=[zlib] get=[zlib] @@ -74,3 +98,13 @@ set=[fastlz] get=[] bool(true) set=[] get=[] bool(true) +set=[zstd] get=[zstd] +bool(true) +set=[zstd] get=[fastlz] +bool(true) +set=[fastlz] get=[zstd] +bool(true) +set=[] get=[zstd] +bool(true) +set=[zstd] get=[] +bool(true) diff --git a/tests/conf_persist.phpt b/tests/conf_persist.phpt index 3b896a2b..15674804 100644 --- a/tests/conf_persist.phpt +++ b/tests/conf_persist.phpt @@ -1,7 +1,7 @@ --TEST-- Conf settings persist. --SKIPIF-- - + --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/experimental/delete_bykey.phpt b/tests/delete_bykey.phpt similarity index 72% rename from tests/experimental/delete_bykey.phpt rename to tests/delete_bykey.phpt index 87219539..6aa589c5 100644 --- a/tests/experimental/delete_bykey.phpt +++ b/tests/delete_bykey.phpt @@ -1,11 +1,14 @@ --TEST-- Memcached::deleteByKey() --SKIPIF-- - + --FILE-- addServer('127.0.0.1', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (array ( + Memcached::OPT_BINARY_PROTOCOL => false, + Memcached::OPT_VERIFY_KEY => true +)); $m->setByKey('keffe', 'eisaleeoo', "foo"); var_dump($m->getByKey('keffe', 'eisaleeoo')); @@ -21,7 +24,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 +40,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/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-- addServer('127.0.0.1', 11211, 1); -$m->addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $data = array( 1 => '1-data', diff --git a/tests/deletemultitypes.phpt b/tests/deletemultitypes.phpt index 0063e5f8..27b7fff7 100644 --- a/tests/deletemultitypes.phpt +++ b/tests/deletemultitypes.phpt @@ -1,7 +1,7 @@ --TEST-- Delete multi key types --SKIPIF-- - + --FILE-- ---FILE-- -addServer('localhost', 11211, 1); - -error_reporting(0); -var_dump($m->cas(0, '', true, 10)); -echo $m->getResultMessage(), "\n"; - -var_dump($m->cas(0, ' �� jas kjjhask d ', true, 10)); -echo $m->getResultMessage(), "\n"; - ---EXPECTF-- -bool(false) -A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE -bool(false) -%rCLIENT ERROR|NOT FOUND%r 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/get_bykey_cas.phpt b/tests/experimental/get_bykey_cas.phpt deleted file mode 100644 index ad94013e..00000000 --- a/tests/experimental/get_bykey_cas.phpt +++ /dev/null @@ -1,70 +0,0 @@ ---TEST-- -Memcached::getByKey() with CAS ---SKIPIF-- - ---FILE-- -addServer('localhost', 11211, 1); - -function the_callback(Memcached $memc, $key, &$value) { - echo "called\n"; - $value = "1234"; - return 1; -} - -$m->set('foo', 1, 10); - -$cas = null; -var_dump($m->getByKey('foo', 'foo', null, $cas)); -var_dump($cas); -echo $m->getResultMessage(), "\n"; - -$cas = null; -var_dump($m->getByKey('', 'foo', null, $cas)); -var_dump($cas); -echo $m->getResultMessage(), "\n"; - -$m->set('bar', "asdf", 10); - -$cas = null; -var_dump($m->getByKey('foo', 'bar', null, $cas)); -var_dump($cas); -echo $m->getResultMessage(), "\n"; - -$m->delete('foo'); -$cas = null; -var_dump($m->getByKey(' � foo jkh a s ���', 'foo', null, $cas)); -var_dump($cas); -echo $m->getResultMessage(), "\n"; - -$cas = null; -var_dump($m->getByKey(' � foo jkh a s ���', '', null, $cas)); -var_dump($cas); -echo $m->getResultMessage(), "\n"; - -$m->delete('foo'); -$cas = null; -var_dump($m->getByKey('foo', 'foo', 'the_callback', $cas)); -var_dump($cas); -var_dump($m->getByKey('foo', 'foo')); ---EXPECTF-- -int(1) -float(%d) -SUCCESS -int(1) -float(%d) -SUCCESS -string(4) "asdf" -float(%d) -SUCCESS -bool(false) -NULL -NOT FOUND -bool(false) -NULL -A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE -called -string(4) "1234" -float(0) -string(4) "1234" 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_cbthrows.phpt b/tests/experimental/getdelayed_cbthrows.phpt index 482034b6..b0427ac9 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', @@ -24,7 +24,7 @@ error_reporting(0); try { $m->getDelayedByKey('kef', array_keys($data), false, 'myfunc'); } catch (Exception $e) { - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } diff --git a/tests/experimental/getmulti_bykey.phpt b/tests/experimental/getmulti_bykey.phpt deleted file mode 100644 index 70ae01c5..00000000 --- a/tests/experimental/getmulti_bykey.phpt +++ /dev/null @@ -1,34 +0,0 @@ ---TEST-- -Memcached::getMultiByKey() ---SKIPIF-- - ---FILE-- -addServer('localhost', 11211, 1); - -$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)); -echo $m->getResultMessage(), "\n"; - -$cas = array(); -var_dump($m->getMultiByKey('foo', array(), $cas, Memcached::GET_PRESERVE_ORDER)); -echo $m->getResultMessage(), "\n"; - ---EXPECT-- -array(3) { - ["foo"]=> - int(1) - ["bar"]=> - int(2) - ["baz"]=> - NULL -} -SUCCESS -array(0) { -} -NOT FOUND diff --git a/tests/experimental/moduleinfo.phpt b/tests/experimental/moduleinfo.phpt deleted file mode 100644 index a1f59b44..00000000 --- a/tests/experimental/moduleinfo.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -Memcached::phpinfo() ---SKIPIF-- - ---FILE-- - 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 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..76d3daca 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'])); @@ -44,7 +44,7 @@ error_reporting(0); try { var_dump($m->set('foo', new Foo(), 10)); } catch (Exception $e) { - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } try { diff --git a/tests/experimental/serializer/serializer_php_bad_unserialize.phpt b/tests/experimental/serializer/serializer_php_bad_unserialize.phpt index 86fba091..341ddedc 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'])); @@ -40,7 +40,7 @@ var_dump($m->set('foo', new Foo(), 10)); try { var_dump($m->get('foo')); } catch (Exception $e) { - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } 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/setmulti_badserialize.phpt b/tests/experimental/setmulti_badserialize.phpt index b12b1d3a..2d8dde53 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() { @@ -36,12 +36,11 @@ try { } } - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } var_dump($m->getByKey('kef', 'foo')); --EXPECT-- - 1234 int(10) 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-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/experimental/fetch_badunserialize.phpt b/tests/fetch_badunserialize.phpt similarity index 70% rename from tests/experimental/fetch_badunserialize.phpt rename to tests/fetch_badunserialize.phpt index 36a3049e..58f4e0aa 100644 --- a/tests/experimental/fetch_badunserialize.phpt +++ b/tests/fetch_badunserialize.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::fetch() with bad unserialize --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public $serialize_throws = false; @@ -31,6 +31,17 @@ class Foo implements Serializable { public function unserialize($str) { throw new Exception("123456"); } + + public function __serialize() { + if ($this->serialize_throws) { + throw new Exception("1234"); + } + return ["1234"]; + } + + public function __unserialize($str) { + throw new Exception("123456"); + } } $data = new Foo(); @@ -42,7 +53,7 @@ var_dump($m->getDelayed(array('foo'), false)); try { var_dump($m->fetch()); } catch (Exception $e) { - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } diff --git a/tests/experimental/fetchall_badunserialize.phpt b/tests/fetchall_badunserialize.phpt similarity index 70% rename from tests/experimental/fetchall_badunserialize.phpt rename to tests/fetchall_badunserialize.phpt index 6108498a..815c9e21 100644 --- a/tests/experimental/fetchall_badunserialize.phpt +++ b/tests/fetchall_badunserialize.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::fetch() with bad unserialize --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public $serialize_throws = false; @@ -31,6 +31,17 @@ class Foo implements Serializable { public function unserialize($str) { throw new Exception("123456"); } + + public function __serialize() { + if ($this->serialize_throws) { + throw new Exception("1234"); + } + return ["1234"]; + } + + public function __unserialize($str) { + throw new Exception("123456"); + } } $data = new Foo(); @@ -42,7 +53,7 @@ var_dump($m->getDelayed(array('foo'), false)); try { var_dump($m->fetchAll()); } catch (Exception $e) { - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } 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-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (array ( + Memcached::OPT_BINARY_PROTOCOL => false, + Memcached::OPT_VERIFY_KEY => true +)); $m->delete('foo'); @@ -20,7 +23,7 @@ var_dump($m->get('foo')); echo $m->getResultMessage(), "\n"; $m->delete('foo'); -var_dump($m->get(' � foo jkh a s ���')); +var_dump($m->get(' ä foo jkh a s åäö')); echo $m->getResultMessage(), "\n"; --EXPECT-- bool(false) @@ -30,4 +33,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/get_bykey.phpt similarity index 73% rename from tests/experimental/get_bykey.phpt rename to tests/get_bykey.phpt index 220dd44a..704e8f04 100644 --- a/tests/experimental/get_bykey.phpt +++ b/tests/get_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $m->set('foo', 1, 10); @@ -20,7 +20,7 @@ var_dump($m->getByKey('foo', 'bar')); echo $m->getResultMessage(), "\n"; $m->delete('foo'); -var_dump($m->getByKey(' � foo jkh a s ���', 'foo')); +var_dump($m->getByKey(' ä foo jkh a s åäö', 'foo')); echo $m->getResultMessage(), "\n"; --EXPECTF-- diff --git a/tests/get_bykey_cas.phpt b/tests/get_bykey_cas.phpt new file mode 100644 index 00000000..90b566c4 --- /dev/null +++ b/tests/get_bykey_cas.phpt @@ -0,0 +1,61 @@ +--TEST-- +Memcached::getByKey() with CAS +--SKIPIF-- + +--FILE-- +set('foo', 1, 10); + +$v = $m->getByKey('foo', 'foo', null, Memcached::GET_EXTENDED); +var_dump($v['value']); +var_dump($v['cas']); +echo $m->getResultMessage(), "\n"; + +$v = $m->getByKey('', 'foo', null, Memcached::GET_EXTENDED); +var_dump($v['value']); +var_dump($v['cas']); +echo $m->getResultMessage(), "\n"; + +$m->set('bar', "asdf", 10); + +$v = $m->getByKey('foo', 'bar', null, Memcached::GET_EXTENDED); +var_dump($v['value']); +var_dump($v['cas']); +echo $m->getResultMessage(), "\n"; + +$m->delete('foo'); +var_dump($m->getByKey(' ä foo jkh a s åäö', 'foo', null, Memcached::GET_EXTENDED)); +echo $m->getResultMessage(), "\n"; + +var_dump($m->getByKey(' ä foo jkh a s åäö', '', null, Memcached::GET_EXTENDED)); +echo $m->getResultMessage(), "\n"; + +$m->delete('foo'); +var_dump($m->getByKey('foo', 'foo', 'the_callback', Memcached::GET_EXTENDED)); +var_dump($m->getByKey('foo', 'foo')); +--EXPECTF-- +int(1) +int(%d) +SUCCESS +int(1) +int(%d) +SUCCESS +string(4) "asdf" +int(%d) +SUCCESS +bool(false) +NOT FOUND +bool(false) +A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE +called +bool(false) +bool(false) \ No newline at end of file diff --git a/tests/get_flags.phpt b/tests/get_flags.phpt new file mode 100644 index 00000000..4950cb7f --- /dev/null +++ b/tests/get_flags.phpt @@ -0,0 +1,65 @@ +--TEST-- +Memcached::get/getMulti() flags +--SKIPIF-- + +--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/experimental/getdelayed_badserver.phpt b/tests/getdelayed_badserver.phpt similarity index 87% rename from tests/experimental/getdelayed_badserver.phpt rename to tests/getdelayed_badserver.phpt index dc0508fb..5d274efb 100644 --- a/tests/experimental/getdelayed_badserver.phpt +++ b/tests/getdelayed_badserver.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::getDelayedByKey() with bad server --SKIPIF-- - + --FILE-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public $serialize_throws = false; @@ -31,6 +31,17 @@ class Foo implements Serializable { public function unserialize($str) { throw new Exception("123456"); } + + public function __serialize() { + if ($this->serialize_throws) { + throw new Exception("1234"); + } + return ["1234"]; + } + + public function __unserialize($str) { + throw new Exception("123456"); + } } function mycb($memc, $key, $value) { @@ -45,7 +56,7 @@ error_reporting(0); try { var_dump($m->getDelayed(array('foo'), false, 'mycb')); } catch (Exception $e) { - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } diff --git a/tests/experimental/getdelayed_bykey.phpt b/tests/getdelayed_bykey.phpt similarity index 89% rename from tests/experimental/getdelayed_bykey.phpt rename to tests/getdelayed_bykey.phpt index 4b3ddf6b..442320a2 100644 --- a/tests/experimental/getdelayed_bykey.phpt +++ b/tests/getdelayed_bykey.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getDelayedByKey() --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/experimental/getdelayed_bykey_cas.phpt b/tests/getdelayed_bykey_cas.phpt similarity index 74% rename from tests/experimental/getdelayed_bykey_cas.phpt rename to tests/getdelayed_bykey_cas.phpt index b874e739..c38ff60f 100644 --- a/tests/experimental/getdelayed_bykey_cas.phpt +++ b/tests/getdelayed_bykey_cas.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getDelayedByKey() with CAS --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', @@ -33,43 +33,53 @@ $m->getDelayedByKey('kef', array_keys($data), true, 'myfunc'); ?> --EXPECTF-- -array(3) { +array(4) { ["key"]=> string(3) "foo" ["value"]=> string(8) "foo-data" ["cas"]=> - float(%d) + int(%d) + ["flags"]=> + int(0) } -array(3) { +array(4) { ["key"]=> string(3) "bar" ["value"]=> string(8) "bar-data" ["cas"]=> - float(%d) + int(%d) + ["flags"]=> + int(0) } -array(3) { +array(4) { ["key"]=> string(3) "baz" ["value"]=> string(8) "baz-data" ["cas"]=> - float(%d) + int(%d) + ["flags"]=> + int(0) } -array(3) { +array(4) { ["key"]=> string(3) "lol" ["value"]=> string(8) "lol-data" ["cas"]=> - float(%d) + int(%d) + ["flags"]=> + int(0) } -array(3) { +array(4) { ["key"]=> string(3) "kek" ["value"]=> string(8) "kek-data" ["cas"]=> - float(%d) + int(%d) + ["flags"]=> + int(0) } diff --git a/tests/experimental/getdelayed_nonstring_keys.phpt b/tests/getdelayed_nonstring_keys.phpt similarity index 89% rename from tests/experimental/getdelayed_nonstring_keys.phpt rename to tests/getdelayed_nonstring_keys.phpt index 0e68344f..005d0579 100644 --- a/tests/experimental/getdelayed_nonstring_keys.phpt +++ b/tests/getdelayed_nonstring_keys.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached getDelayed non string keys --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); class Bar { public function __toString() { 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-- getResultCode()) { } --EXPECTF-- -array(0) { -} +bool(false) NO SERVERS DEFINED -array(0) { -} +bool(false) %d: %s diff --git a/tests/experimental/getmulti_badunserialize.phpt b/tests/getmulti_badunserialize.phpt similarity index 70% rename from tests/experimental/getmulti_badunserialize.phpt rename to tests/getmulti_badunserialize.phpt index 674e3e12..280feed3 100644 --- a/tests/experimental/getmulti_badunserialize.phpt +++ b/tests/getmulti_badunserialize.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getMulti() with bad unserialize --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); class Foo implements Serializable { public function __sleep() { @@ -23,6 +23,14 @@ class Foo implements Serializable { public function unserialize($str) { throw new Exception("123456"); } + + public function __serialize() { + return ["1234"]; + } + + public function __unserialize($str) { + throw new Exception("123456"); + } } var_dump($m->set('bar', "12", 10)); @@ -32,7 +40,7 @@ error_reporting(0); try { var_dump($m->getMulti(array('bar', 'foo'))); } catch (Exception $e) { - echo $php_errormsg, "\n"; + echo error_get_last()["message"], "\n"; echo $e->getMessage(), "\n"; } diff --git a/tests/getmulti_bykey.phpt b/tests/getmulti_bykey.phpt new file mode 100644 index 00000000..b615dc66 --- /dev/null +++ b/tests/getmulti_bykey.phpt @@ -0,0 +1,32 @@ +--TEST-- +Memcached::getMultiByKey() +--SKIPIF-- + +--FILE-- +set('foo', 1, 10); +$m->set('bar', 2, 10); +$m->delete('baz'); + +var_dump($m->getMultiByKey('foo', array('foo', 'bar', 'baz'), Memcached::GET_PRESERVE_ORDER)); +echo $m->getResultMessage(), "\n"; + +var_dump($m->getMultiByKey('foo', array(), Memcached::GET_PRESERVE_ORDER)); +echo $m->getResultMessage(), "\n"; + +--EXPECT-- +array(3) { + ["foo"]=> + int(1) + ["bar"]=> + int(2) + ["baz"]=> + NULL +} +SUCCESS +array(0) { +} +NOT FOUND diff --git a/tests/experimental/getmulti_empty.phpt b/tests/getmulti_empty.phpt similarity index 54% rename from tests/experimental/getmulti_empty.phpt rename to tests/getmulti_empty.phpt index 7c8621da..3550e814 100644 --- a/tests/experimental/getmulti_empty.phpt +++ b/tests/getmulti_empty.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getMulti() with empty array --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include 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/getmulti_partial_error.phpt similarity index 82% rename from tests/experimental/getmulti_partial_error.phpt rename to tests/getmulti_partial_error.phpt index dc4daa50..928f49f4 100644 --- a/tests/experimental/getmulti_partial_error.phpt +++ b/tests/getmulti_partial_error.phpt @@ -1,11 +1,11 @@ --TEST-- Memcached::getMulti() partial error --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $data = array(); for ($i = 0; $i < 1000; $i++) { diff --git a/tests/getserverbykey.phpt b/tests/getserverbykey.phpt index 507bfb42..638f2575 100644 --- a/tests/getserverbykey.phpt +++ b/tests/getserverbykey.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::getServerByKey() --SKIPIF-- - + --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/experimental/getversion.phpt b/tests/getversion.phpt similarity index 69% rename from tests/experimental/getversion.phpt rename to tests/getversion.phpt index 509ff5d5..24d23843 100644 --- a/tests/experimental/getversion.phpt +++ b/tests/getversion.phpt @@ -1,13 +1,14 @@ --TEST-- Memcached::getVersion() --SKIPIF-- - + --FILE-- getVersion()); -$m->addServer('localhost', 11211, 1); +include dirname(__FILE__) . "/config.inc"; +$m = memc_get_instance (); $stats = $m->getVersion(); var_dump($stats); diff --git a/tests/gh_155.phpt b/tests/gh_155.phpt index c1f02bc6..699e8eca 100644 --- a/tests/gh_155.phpt +++ b/tests/gh_155.phpt @@ -1,14 +1,21 @@ --TEST-- Test for bug 155 --SKIPIF-- - + --FILE-- setOption(Memcached::OPT_BINARY_PROTOCOL,true); +$m->setOption(Memcached::OPT_BINARY_PROTOCOL, true); $m->addServer(MEMC_SERVER_HOST, MEMC_SERVER_PORT); $key = 'bug_155_' . uniqid(); @@ -38,4 +45,4 @@ TOUCH: SUCCESS TOUCH: SUCCESS GET: SUCCESS GET: SUCCESS -DONE \ No newline at end of file +DONE diff --git a/tests/gh_21.phpt b/tests/gh_21.phpt index 7302220e..920fb659 100644 --- a/tests/gh_21.phpt +++ b/tests/gh_21.phpt @@ -1,7 +1,7 @@ --TEST-- Test for Github issue 21 --SKIPIF-- - + --FILE-- +--FILE-- +addServers($newServers); + +$m->set('floatpoint', 100.2); +$n = $m->get('floatpoint'); +var_dump($n); + +$m->set('floatpoint_neg', -300.4); +$n = $m->get('floatpoint_neg'); +var_dump($n); +?> +--EXPECT-- +float(100.2) +float(-300.4) diff --git a/tests/gh_77.phpt b/tests/gh_77.phpt index ead8803d..8f241ff4 100644 --- a/tests/gh_77.phpt +++ b/tests/gh_77.phpt @@ -1,8 +1,10 @@ --TEST-- Test for Github issue #77 --SKIPIF-- - --FILE-- + --FILE-- int(1) -} \ No newline at end of file +} diff --git a/tests/gh_93.phpt b/tests/gh_93.phpt index fb7e61f9..b5c0fdc0 100644 --- a/tests/gh_93.phpt +++ b/tests/gh_93.phpt @@ -1,7 +1,7 @@ --TEST-- Test for Github issue #93 (double and long overflow) --SKIPIF-- - + --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); @@ -26,27 +29,44 @@ $m->decrement('foo', 2); var_dump($m->get('foo')); error_reporting(0); -echo "Invalid offset\n"; + +echo "Negative offset\n"; +error_clear_last(); $m->increment('foo', -1); -echo $php_errormsg, "\n"; +echo error_get_last()["message"], "\n"; var_dump($m->get('foo')); + +error_clear_last(); $m->decrement('foo', -1); -echo $php_errormsg, "\n"; +echo error_get_last()["message"], "\n"; +var_dump($m->get('foo')); + +echo "Enormous offset\n"; +$m->increment('foo', 0x7f000000); +var_dump($m->get('foo')); + +$m->decrement('foo', 0x7f000000); 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) int(4) int(3) int(1) -Invalid offset -Memcached::increment(): offset has to be > 0 +Negative offset +Memcached::increment(): offset cannot be a negative value +int(1) +Memcached::decrement(): offset cannot be a negative value int(1) -Memcached::decrement(): offset has to be > 0 +Enormous offset +int(2130706433) int(1) diff --git a/tests/incrdecr_64.phpt b/tests/incrdecr_64.phpt new file mode 100644 index 00000000..dfa32c6a --- /dev/null +++ b/tests/incrdecr_64.phpt @@ -0,0 +1,39 @@ +--TEST-- +64-bit Memcached::increment() decrement() incrementByKey() decrementByKey() +--SKIPIF-- + +--FILE-- +set('foo', 1); +var_dump($m->get('foo')); + +echo "Enormous offset 64-bit\n"; +$m->increment('foo', 0x100000000); +var_dump($m->get('foo')); + +$m->decrement('foo', 0x100000000); +var_dump($m->get('foo')); + +echo "Enormous offset 64-bit by key\n"; +$m->incrementByKey('foo', 'foo', 0x100000000); +var_dump($m->get('foo')); + +$m->decrementByKey('foo', 'foo', 0x100000000); +var_dump($m->get('foo')); + +--EXPECT-- +Normal +int(1) +Enormous offset 64-bit +int(4294967297) +int(1) +Enormous offset 64-bit by key +int(4294967297) +int(1) diff --git a/tests/incrdecr_bykey.phpt b/tests/incrdecr_bykey.phpt index 9986b7dc..ee089938 100644 --- a/tests/incrdecr_bykey.phpt +++ b/tests/incrdecr_bykey.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::incrementByKey() Memcached::decrementByKey() --SKIPIF-- - + --FILE-- decrementByKey('foo', 'foo', 2); var_dump($m->get('foo')); error_reporting(0); -echo "Invalid offset\n"; + +echo "Negative offset\n"; +error_clear_last(); $m->incrementByKey('foo', 'foo', -1); -echo $php_errormsg, "\n"; +echo error_get_last()["message"], "\n"; var_dump($m->get('foo')); + +error_clear_last(); $m->decrementByKey('foo', 'foo', -1); -echo $php_errormsg, "\n"; +echo error_get_last()["message"], "\n"; +var_dump($m->get('foo')); + +echo "Enormous offset\n"; +$m->incrementByKey('foo', 'foo', 0x7f000000); +var_dump($m->get('foo')); + +$m->decrementByKey('foo', 'foo', 0x7f000000); var_dump($m->get('foo')); --EXPECT-- @@ -45,8 +56,11 @@ int(2) int(4) int(3) int(1) -Invalid offset -Memcached::incrementByKey(): offset has to be > 0 +Negative offset +Memcached::incrementByKey(): offset cannot be a negative value +int(1) +Memcached::decrementByKey(): offset cannot be a negative value int(1) -Memcached::decrementByKey(): offset has to be > 0 +Enormous offset +int(2130706433) int(1) diff --git a/tests/incrdecr_initial.phpt b/tests/incrdecr_initial.phpt index b5530707..0c71423c 100644 --- a/tests/incrdecr_initial.phpt +++ b/tests/incrdecr_initial.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::increment() Memcached::decrement() with initial support --SKIPIF-- - + --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..9749ed18 100644 --- a/tests/invoke_callback_2.phpt +++ b/tests/invoke_callback_2.phpt @@ -1,7 +1,7 @@ --TEST-- Use callback initializer --SKIPIF-- - + --FILE-- isPristine()); var_dump($this->isPersistent()); var_dump($id); } } -error_reporting(0); - echo "cb call\n"; $m1 = new Memcached('foo1', 'init_cb'); @@ -50,7 +49,6 @@ $m1 = new Memcached('foo1', 'init_cb_fail'); echo "cb arg without arg\n"; $m1 = new Memcached('foo3', 'init_cb_arg'); -echo $php_errormsg, "\n"; echo "cb arg not persistent\n"; $m1 = new Memcached(null, 'init_nopersist_cb'); @@ -63,7 +61,7 @@ $m1 = new Foo('baz'); echo "cb second persistent in object\n"; $m1 = new Foo('baz'); - +?> --EXPECT-- cb call string(9) "Memcached" @@ -71,17 +69,18 @@ bool(true) string(4) "foo1" cb not run cb arg without arg +int(2) string(4) "foo3" -NULL - cb arg not persistent bool(false) NULL cb in object +int(2) bool(true) bool(false) NULL cb persistent in object +int(2) bool(true) bool(true) string(3) "baz" diff --git a/tests/invoke_callback_twice.phpt b/tests/invoke_callback_twice.phpt index 7cf63647..41e3474c 100644 --- a/tests/invoke_callback_twice.phpt +++ b/tests/invoke_callback_twice.phpt @@ -1,7 +1,7 @@ --TEST-- Test that callback is invoked on new object only once --SKIPIF-- - + --FILE-- ---FILE-- - true, - )); - -$ascii = memc_get_instance (); - -var_dump ($binary->set ('binary key with spaces', 'this is a test')); -var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); - -var_dump ($ascii->set ('ascii key with spaces', 'this is a test')); -var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); - -var_dump ($ascii->set (str_repeat ('1234567890', 512), 'this is a test')); -var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); - -echo "OK" . PHP_EOL; - ---EXPECT-- -bool(false) -bool(true) -bool(false) -bool(true) -bool(false) -bool(true) -OK diff --git a/tests/keys_ascii.phpt b/tests/keys_ascii.phpt new file mode 100644 index 00000000..e7846e99 --- /dev/null +++ b/tests/keys_ascii.phpt @@ -0,0 +1,187 @@ +--TEST-- +Test valid and invalid keys - ascii +--SKIPIF-- + +--FILE-- + false, + Memcached::OPT_VERIFY_KEY => true + )); + +echo 'ASCII: SPACES' . PHP_EOL; +var_dump ($ascii->set ('ascii key with spaces', 'this is a test')); +var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + +echo 'ASCII: NEWLINE' . PHP_EOL; +var_dump ($ascii->set ('asciikeywithnewline' . PHP_EOL, 'this is a test')); +var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + +echo 'ASCII: EMPTY' . PHP_EOL; +var_dump ($ascii->set (''/*empty key*/, 'this is a test')); +var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + +echo 'ASCII: TOO LONG' . PHP_EOL; +var_dump ($ascii->set (str_repeat ('1234567890', 512), 'this is a test')); +var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + +echo 'ASCII: GET' . PHP_EOL; +for ($i=0;$i<32;$i++) { + var_dump ($ascii->get ('asciikeywithnonprintablechar-' . chr($i) . '-here')); + var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); +} + +echo 'ASCII: SET' . PHP_EOL; +for ($i=0;$i<32;$i++) { + var_dump ($ascii->set ('asciikeywithnonprintablechar-' . chr($i) . '-here', 'this is a test')); + var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); +} + +echo 'OK' . PHP_EOL; + +--EXPECT-- +ASCII: SPACES +bool(false) +bool(true) +ASCII: NEWLINE +bool(false) +bool(true) +ASCII: EMPTY +bool(false) +bool(true) +ASCII: TOO LONG +bool(false) +bool(true) +ASCII: GET +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +ASCII: SET +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +OK diff --git a/tests/keys_binary.phpt b/tests/keys_binary.phpt new file mode 100644 index 00000000..f0ae67bd --- /dev/null +++ b/tests/keys_binary.phpt @@ -0,0 +1,190 @@ +--TEST-- +Test valid and invalid keys - binary +--SKIPIF-- + +--FILE-- + true, + )); + +echo 'BINARY: SPACES' . PHP_EOL; +var_dump ($binary->set ('binary key with spaces', 'this is a test')); +var_dump ($binary->getResultCode () == Memcached::RES_SUCCESS); + +echo 'BINARY: NEWLINE' . PHP_EOL; +var_dump ($binary->set ('binarykeywithnewline' . PHP_EOL, 'this is a test')); +var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + +echo 'BINARY: EMPTY' . PHP_EOL; +var_dump ($binary->set (''/*empty key*/, 'this is a test')); +var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + +echo 'BINARY: TOO LONG' . PHP_EOL; +var_dump ($binary->set (str_repeat ('1234567890', 512), 'this is a test')); +var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + +echo 'BINARY: GET' . PHP_EOL; +// Only newline fails in binary mode (char 10) +for ($i=0;$i<32;$i++) { + $binary->delete ('binarykeywithnonprintablechar-' . chr($i) . '-here'); + var_dump ($binary->get ('binarykeywithnonprintablechar-' . chr($i) . '-here')); + var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); +} + +echo 'BINARY: SET' . PHP_EOL; +// Only newline fails in binary mode (char 10) +for ($i=0;$i<32;$i++) { + var_dump ($binary->set ('binarykeywithnonprintablechar-' . chr($i) . '-here', 'this is a test')); + var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); + $binary->delete ('binarykeywithnonprintablechar-' . chr($i) . '-here'); +} + +echo 'OK' . PHP_EOL; + +--EXPECT-- +BINARY: SPACES +bool(true) +bool(true) +BINARY: NEWLINE +bool(false) +bool(true) +BINARY: EMPTY +bool(false) +bool(true) +BINARY: TOO LONG +bool(false) +bool(true) +BINARY: GET +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(true) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +BINARY: SET +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +OK diff --git a/tests/experimental/locale_float.phpt b/tests/locale_float.phpt similarity index 55% rename from tests/experimental/locale_float.phpt rename to tests/locale_float.phpt index e6967bdb..2f25ea73 100644 --- a/tests/experimental/locale_float.phpt +++ b/tests/locale_float.phpt @@ -2,14 +2,15 @@ Float should not consider locale --SKIPIF-- addServer('127.0.0.1', 11211); + +include dirname(__FILE__) . '/config.inc'; +$memcache = memc_get_instance (); setlocale(LC_NUMERIC, "fi_FI", 'sv_SV', 'nl_NL'); 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-- +setOption(Memcached::OPT_BINARY_PROTOCOL, true); +$cache->setOption(Memcached::OPT_COMPRESSION, false); +$cache->addServer('127.0.0.1', 3434); + +$cache->add("add_key", "hello", 500); +$cache->append("append_key", "world"); +$cache->prepend("prepend_key", "world"); + +$cache->increment("incr", 2, 1, 500); +$cache->decrement("decr", 2, 1, 500); + +$cache->delete("delete_k"); +$cache->flush(1); + +var_dump($cache->get('get_this')); + +$cache->set ('set_key', 'value 1', 100); +$cache->replace ('replace_key', 'value 2', 200); + +var_dump($cache->getVersion()); +var_dump($cache->getStats()); +var_dump($cache->getStats("empty")); +var_dump($cache->getStats("foobar")); +var_dump($cache->getStats("scalar")); +var_dump($cache->getStats("numeric array")); + +$cache->quit(); +usleep(50000); + +memcached_server_stop($server); +?> +Done +--EXPECTF-- +Listening on 127.0.0.1:3434 +Incoming connection from 127.0.0.1:%s +Incoming connection from 127.0.0.1:%s +client_id=[%s]: Add key=[add_key], value=[hello], flags=[0], expiration=[500] +client_id=[%s]: Append key=[append_key], value=[world], cas=[0] +client_id=[%s]: Prepend key=[prepend_key], value=[world], cas=[0] +client_id=[%s]: Incrementing key=[incr], delta=[2], initial=[1], expiration=[500] +client_id=[%s]: Decrementing key=[decr], delta=[2], initial=[1], expiration=[500] +client_id=[%s]: Delete key=[delete_k], cas=[0] +client_id=[%s]: Flush when=[1] +client_id=[%s]: Get key=[get_this] +client_id=[%s]: Noop +string(20) "Hello to you client!" +client_id=[%s]: Set key=[set_key], value=[value 1], flags=[0], expiration=[100], cas=[0] +client_id=[%s]: Replace key=[replace_key], value=[value 2], flags=[0], expiration=[200], cas=[0] +client_id=[%s]: Version +array(1) { + ["127.0.0.1:3434"]=> + string(5) "1.1.1" +} +client_id=[%s]: Stat key=[] +array(1) { + ["127.0.0.1:3434"]=> + array(2) { + ["key"]=> + string(0) "" + ["foo"]=> + string(3) "bar" + } +} +client_id=[%s]: Stat key=[empty] +array(0) { +} +client_id=[%s]: Stat key=[foobar] +array(1) { + ["127.0.0.1:3434"]=> + array(2) { + ["key"]=> + string(6) "foobar" + ["foo"]=> + string(3) "bar" + } +} +client_id=[%s]: Stat key=[scalar] +array(1) { + ["127.0.0.1:3434"]=> + array(1) { + [0]=> + string(%d) "you want it, you get it" + } +} +client_id=[%s]: Stat key=[numeric array] +array(1) { + ["127.0.0.1:3434"]=> + array(3) { + [-1]=> + string(3) "one" + [0]=> + string(3) "two" + [1]=> + string(5) "three" + } +} +client_id=[%s]: Client quit +Done diff --git a/tests/memcachedserver6.phpt b/tests/memcachedserver6.phpt new file mode 100644 index 00000000..3d02b244 --- /dev/null +++ b/tests/memcachedserver6.phpt @@ -0,0 +1,118 @@ +--TEST-- +MemcachedServer +--SKIPIF-- + +--FILE-- +setOption(Memcached::OPT_BINARY_PROTOCOL, true); +$cache->setOption(Memcached::OPT_COMPRESSION, false); +$cache->addServer('[::1]', 3434); + +$cache->add("add_key", "hello", 500); +$cache->append("append_key", "world"); +$cache->prepend("prepend_key", "world"); + +$cache->increment("incr", 2, 1, 500); +$cache->decrement("decr", 2, 1, 500); + +$cache->delete("delete_k"); +$cache->flush(1); + +var_dump($cache->get('get_this')); + +$cache->set ('set_key', 'value 1', 100); +$cache->replace ('replace_key', 'value 2', 200); + +var_dump($cache->getVersion()); +var_dump($cache->getStats()); +var_dump($cache->getStats("empty")); +var_dump($cache->getStats("foobar")); +var_dump($cache->getStats("scalar")); +var_dump($cache->getStats("numeric array")); + +$cache->quit(); +usleep(50000); + +memcached_server_stop($server); +?> +Done +--EXPECTF-- +Listening on [::1]:3434 +Incoming connection from [::1]:%s +Incoming connection from [::1]:%s +client_id=[%s]: Add key=[add_key], value=[hello], flags=[0], expiration=[500] +client_id=[%s]: Append key=[append_key], value=[world], cas=[0] +client_id=[%s]: Prepend key=[prepend_key], value=[world], cas=[0] +client_id=[%s]: Incrementing key=[incr], delta=[2], initial=[1], expiration=[500] +client_id=[%s]: Decrementing key=[decr], delta=[2], initial=[1], expiration=[500] +client_id=[%s]: Delete key=[delete_k], cas=[0] +client_id=[%s]: Flush when=[1] +client_id=[%s]: Get key=[get_this] +client_id=[%s]: Noop +string(20) "Hello to you client!" +client_id=[%s]: Set key=[set_key], value=[value 1], flags=[0], expiration=[100], cas=[0] +client_id=[%s]: Replace key=[replace_key], value=[value 2], flags=[0], expiration=[200], cas=[0] +client_id=[%s]: Version +array(1) { + ["[::1]:3434"]=> + string(5) "1.1.1" +} +client_id=[%s]: Stat key=[] +array(1) { + ["[::1]:3434"]=> + array(2) { + ["key"]=> + string(0) "" + ["foo"]=> + string(3) "bar" + } +} +client_id=[%s]: Stat key=[empty] +array(0) { +} +client_id=[%s]: Stat key=[foobar] +array(1) { + ["[::1]:3434"]=> + array(2) { + ["key"]=> + string(6) "foobar" + ["foo"]=> + string(3) "bar" + } +} +client_id=[%s]: Stat key=[scalar] +array(1) { + ["[::1]:3434"]=> + array(1) { + [0]=> + string(%d) "you want it, you get it" + } +} +client_id=[%s]: Stat key=[numeric array] +array(1) { + ["[::1]:3434"]=> + array(3) { + [-1]=> + string(3) "one" + [0]=> + string(3) "two" + [1]=> + string(5) "three" + } +} +client_id=[%s]: Client quit +Done diff --git a/tests/moduleinfo.phpt b/tests/moduleinfo.phpt new file mode 100644 index 00000000..51a5555e --- /dev/null +++ b/tests/moduleinfo.phpt @@ -0,0 +1,22 @@ +--TEST-- +Memcached::phpinfo() +--SKIPIF-- + +--FILE-- + enabled +%A diff --git a/tests/multi_order.phpt b/tests/multi_order.phpt index 4fd5f5b4..50e4120c 100644 --- a/tests/multi_order.phpt +++ b/tests/multi_order.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached GET_PRESERVE_ORDER flag in getMulti --SKIPIF-- - + --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..a096c8f1 100644 --- a/tests/options.phpt +++ b/tests/options.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached options --SKIPIF-- - + --FILE-- getOption(Memcached::OPT_COMPRESSION_TYPE) == Memcached::COMPRESSIO var_dump($m->setOption(Memcached::OPT_COMPRESSION_TYPE, 0)); var_dump($m->getOption(Memcached::OPT_COMPRESSION_TYPE) == Memcached::COMPRESSION_FASTLZ); + +echo "item_size_limit setOption\n"; +var_dump($m->setOption(Memcached::OPT_ITEM_SIZE_LIMIT, 0)); +var_dump($m->getOption(Memcached::OPT_ITEM_SIZE_LIMIT) === 0); +var_dump($m->setOption(Memcached::OPT_ITEM_SIZE_LIMIT, -1)); +var_dump($m->setOption(Memcached::OPT_ITEM_SIZE_LIMIT, 1000000)); +var_dump($m->getOption(Memcached::OPT_ITEM_SIZE_LIMIT) == 1000000); + +echo "item_size_limit ini\n"; +ini_set('memcached.item_size_limit', '0'); +$m = new Memcached(); +var_dump($m->getOption(Memcached::OPT_ITEM_SIZE_LIMIT) === 0); + +ini_set('memcached.item_size_limit', '1000000'); +$m = new Memcached(); +var_dump($m->getOption(Memcached::OPT_ITEM_SIZE_LIMIT) == 1000000); + +ini_set('memcached.item_size_limit', null); +$m = new Memcached(); +var_dump($m->getOption(Memcached::OPT_ITEM_SIZE_LIMIT) === 0); ?> --EXPECTF-- bool(true) @@ -41,3 +61,15 @@ bool(true) bool(true) bool(false) bool(true) +item_size_limit setOption +bool(true) +bool(true) + +Warning: Memcached::setOption(): ITEM_SIZE_LIMIT must be >= 0 in %s on line %d +bool(false) +bool(true) +bool(true) +item_size_limit ini +bool(true) +bool(true) +bool(true) diff --git a/tests/pr_75.phpt b/tests/pr_75.phpt index 610e2c7e..2bcfa24e 100644 --- a/tests/pr_75.phpt +++ b/tests/pr_75.phpt @@ -1,7 +1,7 @@ --TEST-- Wrong return values for binary protocol --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- addServer('localhost', 11211, 1); +include 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/replace.phpt b/tests/replace.phpt index 3f4f1e92..fdb43219 100644 --- a/tests/replace.phpt +++ b/tests/replace.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached::replace() --SKIPIF-- - + --FILE-- + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); error_reporting(0); $m->delete('foo'); var_dump($m->replaceByKey('kef', 'foo', 'bar', 60)); -echo $php_errormsg, "\n"; +echo error_get_last()["message"], "\n"; echo $m->getResultMessage(), "\n"; var_dump($m->getByKey('kef', 'foo')); diff --git a/tests/rescode.phpt b/tests/rescode.phpt index fbda4f78..bbc989f0 100644 --- a/tests/rescode.phpt +++ b/tests/rescode.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached result codes. --SKIPIF-- - + --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/reset_keyprefix.phpt b/tests/reset_keyprefix.phpt new file mode 100644 index 00000000..385a21d5 --- /dev/null +++ b/tests/reset_keyprefix.phpt @@ -0,0 +1,38 @@ +--TEST-- +Cannot reset OPT_PREFIX_KEY #293 +--SKIPIF-- + +--FILE-- +set('key1', 'abc'); +var_dump($m->get('key1')); + +$m->setOption(Memcached::OPT_PREFIX_KEY, 'prefix'); +var_dump($m->get('key1')); + +$m->setOption(Memcached::OPT_PREFIX_KEY, false); +var_dump($m->get('key1')); + +$m->setOption(Memcached::OPT_PREFIX_KEY, 'prefix'); +var_dump($m->get('key1')); + +$m->setOption(Memcached::OPT_PREFIX_KEY, ''); +var_dump($m->get('key1')); + +$m->setOption(Memcached::OPT_PREFIX_KEY, 'prefix'); +var_dump($m->get('key1')); + +$m->setOption(Memcached::OPT_PREFIX_KEY, null); +var_dump($m->get('key1')); +--EXPECTF-- +string(3) "abc" +bool(false) +string(3) "abc" +bool(false) +string(3) "abc" +bool(false) +string(3) "abc" \ No newline at end of file diff --git a/tests/server.inc b/tests/server.inc new file mode 100644 index 00000000..9678f043 --- /dev/null +++ b/tests/server.inc @@ -0,0 +1,82 @@ + STDIN, + 1 => STDOUT, + 2 => STDERR, + ); + + $cmd = "{$php_executable} {$php_args} {$code} {$host}:{$port} "; + if (substr(PHP_OS, 0, 3) == 'WIN') { + $cmd = "{$php_executable} {$php_args} {$code} {$host}:{$port} "; + + $handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, __DIR__, NULL, array("bypass_shell" => true, "suppress_errors" => true)); + } else { + $cmd = "exec {$cmd} 2>/dev/null"; + + $handle = proc_open($cmd, $descriptorspec, $pipes, __DIR__); + } + + // note: even when server prints 'Listening on localhost:8964...Press Ctrl-C to quit.' + // it might not be listening yet...need to wait until fsockopen() call returns + $error = "Unable to connect to server\n"; + for ($i=0; $i < getenv("VALGRIND") ? 1000 : 60; $i++) { + usleep(50000); // 50ms per try + $status = proc_get_status($handle); + $fp = @fsockopen($host, $port); + // Failure, the server is no longer running + if (!($status && $status['running'])) { + $error = "Server is not running {$status['command']}\n"; + break; + } + // Success, Connected to servers + if ($fp) { + $error = ''; + break; + } + } + + if ($fp) { + fclose($fp); + } + + if ($error) { + echo $error; + proc_terminate($handle); + proc_close($handle); + exit(1); + } + + register_shutdown_function( + function($handle) { + if (is_resource($handle)) { + proc_terminate($handle); + proc_close($handle); + } + }, + $handle + ); + + return $handle; +} + +function memcached_server_stop($handle) { + $success = FALSE; + if ($handle) { + proc_terminate($handle); + /* Wait for server to shutdown */ + for ($i = 0; $i < 60; $i++) { + $status = proc_get_status($handle); + if (!($status && $status['running'])) { + $success = TRUE; + break; + } + usleep(50000); + } + proc_close($handle); + } + return $success; +} diff --git a/tests/server.php b/tests/server.php new file mode 100644 index 00000000..9a50eb06 --- /dev/null +++ b/tests/server.php @@ -0,0 +1,111 @@ +on (Memcached::ON_CONNECT, + function ($remote_addr) { + echo "Incoming connection from {$remote_addr}" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_ADD, + function ($client_id, $key, $value, $flags, $expiration, &$cas) { + echo "client_id=[$client_id]: Add key=[$key], value=[$value], flags=[$flags], expiration=[$expiration]" . PHP_EOL; + $cas = 15; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_APPEND, + function ($client_id, $key, $value, $cas, &$result_cas) { + echo "client_id=[$client_id]: Append key=[$key], value=[$value], cas=[$cas]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_PREPEND, + function ($client_id, $key, $value, $cas, &$result_cas) { + echo "client_id=[$client_id]: Prepend key=[$key], value=[$value], cas=[$cas]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_INCREMENT, + function ($client_id, $key, $delta, $initial, $expiration, &$result, &$result_cas) { + echo "client_id=[$client_id]: Incrementing key=[$key], delta=[$delta], initial=[$initial], expiration=[$expiration]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_DECREMENT, + function ($client_id, $key, $delta, $initial, $expiration, &$result, &$result_cas) { + echo "client_id=[$client_id]: Decrementing key=[$key], delta=[$delta], initial=[$initial], expiration=[$expiration]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_DELETE, + function ($client_id, $key, $cas) { + echo "client_id=[$client_id]: Delete key=[$key], cas=[$cas]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_FLUSH, + function ($client_id, $when) { + echo "client_id=[$client_id]: Flush when=[$when]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_GET, + function ($client_id, $key, &$value, &$flags, &$cas) { + echo "client_id=[$client_id]: Get key=[$key]" . PHP_EOL; + $value = "Hello to you client!"; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_NOOP, + function ($client_id) { + echo "client_id=[$client_id]: Noop" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_REPLACE, + function ($client_id, $key, $value, $flags, $expiration, $cas, &$result_cas) { + echo "client_id=[$client_id]: Replace key=[$key], value=[$value], flags=[$flags], expiration=[$expiration], cas=[$cas]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_SET, + function ($client_id, $key, $value, $flags, $expiration, $cas, &$result_cas) { + echo "client_id=[$client_id]: Set key=[$key], value=[$value], flags=[$flags], expiration=[$expiration], cas=[$cas]" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_STAT, + function ($client_id, $key, array &$values) { + echo "client_id=[$client_id]: Stat key=[$key]" . PHP_EOL; + + if ($key === "scalar") { + $values = "you want it, you get it"; + } elseif ($key === "numeric array") { + $values = [-1 => "one", "two", "three"]; + } elseif ($key === "empty") { + $values = []; + } else { + $values["key"] = $key; + $values["foo"] = "bar"; + } + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_VERSION, + function ($client_id, &$value) { + echo "client_id=[$client_id]: Version" . PHP_EOL; + $value = "1.1.1"; + return Memcached::RESPONSE_SUCCESS; + }); + +$server->on (Memcached::ON_QUIT, + function ($client_id) { + echo "client_id=[$client_id]: Client quit" . PHP_EOL; + return Memcached::RESPONSE_SUCCESS; + }); + +$addr = ($_SERVER['argv'][1] ?? "127.0.0.1:3434"); +echo "Listening on $addr" . PHP_EOL; +$server->run($addr); diff --git a/tests/session_badconf_emptyprefix.phpt b/tests/session_badconf_emptyprefix.phpt index 74d681dd..f49a8417 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_persistent.phpt b/tests/session_badconf_persistent.phpt new file mode 100644 index 00000000..7deeb1e1 --- /dev/null +++ b/tests/session_badconf_persistent.phpt @@ -0,0 +1,25 @@ +--TEST-- +Session bad configurations, persistent +--SKIPIF-- + +--INI-- +session.save_handler = "memcached" +session.save_path = "PERSISTENT=1 hello:11211,world:11211" + +--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-- + 70100) print "skip"; ?> --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 - +memcached.sess_binary_protocol = Off --FILE-- +--INI-- +session.save_handler = memcached +memcached.sess_binary_protocol = Off +--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..51482517 --- /dev/null +++ b/tests/session_basic3.phpt @@ -0,0 +1,46 @@ +--TEST-- +Session basic open, write, destroy +--SKIPIF-- + +--INI-- +session.save_handler = memcached +memcached.sess_binary_protocol = Off +--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_lazy_warning.phpt b/tests/session_lazy_warning.phpt new file mode 100644 index 00000000..6847f5b5 --- /dev/null +++ b/tests/session_lazy_warning.phpt @@ -0,0 +1,47 @@ +--TEST-- +Session lazy binary warning old libmemcached +--SKIPIF-- += 0x01000018) die ('skip too recent libmemcached'); +?> +--INI-- +session.save_handler = memcached +memcached.sess_binary_protocol = On +--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(); + + +--EXPECTF-- +NULL +array(1) { + ["foo"]=> + int(1) +} + +Warning: session_write_close(): using touch command with binary protocol is not recommended with libmemcached versions below 1.0.18, please use ascii protocol or upgrade libmemcached in %s on line %d +array(0) { +} diff --git a/tests/session_lock-php71.phpt b/tests/session_lock-php71.phpt new file mode 100644 index 00000000..c0fceb67 --- /dev/null +++ b/tests/session_lock-php71.phpt @@ -0,0 +1,66 @@ +--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." + +# Turn off binary protocol while the test matrix has older versions of +# libmemcached for which the extension warns of a broken touch command. +memcached.sess_binary_protocol = Off + +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 + +Warning: session_start(): Failed to read session data: memcached (path: 127.0.0.1:11211) in %s on line %d +OK diff --git a/tests/session_lock.phpt b/tests/session_lock.phpt new file mode 100644 index 00000000..cedc83bb --- /dev/null +++ b/tests/session_lock.phpt @@ -0,0 +1,64 @@ +--TEST-- +Session lock +--SKIPIF-- += 70100) print "skip"; +?> +--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." + +# Turn off binary protocol while the test matrix has older versions of +# libmemcached for which the extension warns of a broken touch command. +memcached.sess_binary_protocol = Off + +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_persistent.phpt b/tests/session_persistent.phpt new file mode 100644 index 00000000..e096f2b6 --- /dev/null +++ b/tests/session_persistent.phpt @@ -0,0 +1,28 @@ +--TEST-- +Session persistent +--SKIPIF-- + +--INI-- +session.save_handler=memcached +memcached.sess_persistent=1 +--FILE-- + +--EXPECT-- +array(1) { + ["test"]=> + bool(true) +} 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-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); var_dump($m->setByKey('foo', 'foo', 1, 10)); echo $m->getResultMessage(), "\n"; @@ -14,7 +14,7 @@ echo $m->getResultMessage(), "\n"; var_dump($m->setByKey('foo', '', 1, 10)); echo $m->getResultMessage(), "\n"; // This is OK for the binary protocol -$rv = $m->setByKey('foo', ' asd ���', 1, 1); +$rv = $m->setByKey('foo', ' asd åäö', 1, 1); if ($m->getOption(Memcached::OPT_BINARY_PROTOCOL)) { if ($rv !== true and $m->getResultCode() !== Memcached::RES_SUCCESS) { var_dump($rv); diff --git a/tests/experimental/set_comp_below_factor.phpt b/tests/set_comp_below_factor.phpt similarity index 71% rename from tests/experimental/set_comp_below_factor.phpt rename to tests/set_comp_below_factor.phpt index f3eece2f..80d28006 100644 --- a/tests/experimental/set_comp_below_factor.phpt +++ b/tests/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(__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/set_default_serializer.phpt similarity index 95% rename from tests/experimental/set_default_serializer.phpt rename to tests/set_default_serializer.phpt index f9658736..3381ecf9 100644 --- a/tests/experimental/set_default_serializer.phpt +++ b/tests/set_default_serializer.phpt @@ -1,7 +1,7 @@ --TEST-- Set default serializer --SKIPIF-- - + --FILE-- +--FILE-- +setEncodingKey("Hello")); +var_dump ($m->set ($key, 'set using encoding')); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# Change the encryption key. The old value will be inaccessible. +var_dump ($m->setEncodingKey("World")); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# Restore the original key to retrieve old values again. +var_dump ($m->setEncodingKey("Hello")); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# With a new encoding key we can still write new values, +# this works as expected with libmemcached 1.0.18 and higher. +var_dump ($m->setEncodingKey("World")); +var_dump ($m->get ($key)); +var_dump ($m->set ($key, 'set using encoding')); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +?> +--EXPECT-- +bool(true) +bool(true) +string(18) "set using encoding" +OK +bool(true) +bool(false) +OK +bool(true) +string(18) "set using encoding" +OK +bool(true) +bool(false) +bool(true) +string(18) "set using encoding" +OK diff --git a/tests/set_encoding_key2.phpt b/tests/set_encoding_key2.phpt new file mode 100644 index 00000000..f8d21a16 --- /dev/null +++ b/tests/set_encoding_key2.phpt @@ -0,0 +1,37 @@ +--TEST-- +Test libmemcached encryption +--SKIPIF-- += 0x01000018) die ("skip test for libmemcached lower than 1.0.18"); +?> +--FILE-- +setEncodingKey("Hello")); +var_dump ($m->set ($key, 'set using encoding')); +var_dump ($m->get ($key)); + +echo "OK" . PHP_EOL; + +# libmemcached < 1.0.18 goes into a bad state when the encoding key is changed, +# so php-memcached warns and returns false when trying to change the key. +var_dump ($m->setEncodingKey("World")); + +echo "OK" . PHP_EOL; + +?> +--EXPECTF-- +bool(true) +bool(true) +string(18) "set using encoding" +OK + +Warning: Memcached::setEncodingKey(): libmemcached versions less than 1.0.18 cannot change encoding key in %s on line %d +bool(false) +OK diff --git a/tests/experimental/set_invalid_serializer.phpt b/tests/set_invalid_serializer.phpt similarity index 92% rename from tests/experimental/set_invalid_serializer.phpt rename to tests/set_invalid_serializer.phpt index cc734650..6ca07e80 100644 --- a/tests/experimental/set_invalid_serializer.phpt +++ b/tests/set_invalid_serializer.phpt @@ -1,7 +1,7 @@ --TEST-- Set invalid serializer --SKIPIF-- - + --FILE-- + --FILE-- 0, +)); $key = 'foobarbazDEADC0DE'; $value = str_repeat("foo bar", 1024 * 1024); -$m->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/set_large_e2big.phpt b/tests/set_large_e2big.phpt new file mode 100644 index 00000000..498231e4 --- /dev/null +++ b/tests/set_large_e2big.phpt @@ -0,0 +1,27 @@ +--TEST-- +set data exceeding size limit +--SKIPIF-- + +--FILE-- + 100, +)); + +$m->delete('set_large_e2big_test'); + +$value = str_repeat('a large payload', 1024 * 1024); + +var_dump($m->set('set_large_e2big_test', $value)); +var_dump($m->getResultCode() == Memcached::RES_E2BIG); +var_dump($m->getResultMessage() == 'ITEM TOO BIG'); +var_dump($m->get('set_large_e2big_test') === false); +var_dump($m->getResultCode() == Memcached::RES_NOTFOUND); +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/tests/experimental/setget_zero_factor.phpt b/tests/setget_zero_factor.phpt similarity index 66% rename from tests/experimental/setget_zero_factor.phpt rename to tests/setget_zero_factor.phpt index 408268a7..7d7634c2 100644 --- a/tests/experimental/setget_zero_factor.phpt +++ b/tests/setget_zero_factor.phpt @@ -1,11 +1,11 @@ --TEST-- Compress with 0 factor and get --SKIPIF-- - + --FILE-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); ini_set('memcached.compression_factor', 0); $array = range(1, 20000, 1); 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-- addServer('localhost', 11211, 1); +include dirname(__FILE__) . '/config.inc'; +$m = memc_get_instance (); $data = array( 'foo' => 'foo-data', diff --git a/tests/setoptions.phpt b/tests/setoptions.phpt index 97fc37ec..9cefd138 100644 --- a/tests/setoptions.phpt +++ b/tests/setoptions.phpt @@ -1,7 +1,7 @@ --TEST-- Set options using setOptions --SKIPIF-- - + --FILE-- setOptions(array( Memcached::OPT_COMPRESSION => 0, Memcached::OPT_LIBKETAMA_COMPATIBLE => 1, Memcached::OPT_CONNECT_TIMEOUT => 5000, + Memcached::OPT_ITEM_SIZE_LIMIT => 1000000, ))); var_dump($m->getOption(Memcached::OPT_PREFIX_KEY) == 'a_prefix'); var_dump($m->getOption(Memcached::OPT_SERIALIZER) == Memcached::SERIALIZER_PHP); var_dump($m->getOption(Memcached::OPT_COMPRESSION) == 0); var_dump($m->getOption(Memcached::OPT_LIBKETAMA_COMPATIBLE) == 1); +var_dump($m->getOption(Memcached::OPT_ITEM_SIZE_LIMIT) == 1000000); echo "test invalid options\n"; @@ -36,6 +38,7 @@ bool(true) bool(true) bool(true) bool(true) +bool(true) test invalid options Warning: Memcached::setOptions(): invalid configuration option in %s on line %d diff --git a/tests/skipif.inc b/tests/skipif.inc new file mode 100644 index 00000000..9ef73b0c --- /dev/null +++ b/tests/skipif.inc @@ -0,0 +1,16 @@ + +--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/stats_hang.phpt b/tests/stats_hang.phpt new file mode 100644 index 00000000..181e15e0 --- /dev/null +++ b/tests/stats_hang.phpt @@ -0,0 +1,89 @@ +--TEST-- +Check stats does not hang on non-blocking binary protocol +--SKIPIF-- + +--FILE-- +setOption(Memcached::OPT_NO_BLOCK, true); +$m->setOption(Memcached::OPT_BINARY_PROTOCOL, true); + +$stats = $m->getStats(); +$conns1 = $stats[$key]['total_connections']; + +$stats = $m->getStats(); +$conns2 = $stats[$key]['total_connections']; + +var_dump($conns1 == $conns2); +var_dump($m->getOption(Memcached::OPT_NO_BLOCK)); +var_dump($m->getOption(Memcached::OPT_BINARY_PROTOCOL)); +echo "OK" . PHP_EOL; + +// If either or both options are false no need to reconnect +$m->setOption(Memcached::OPT_NO_BLOCK, false); +$m->setOption(Memcached::OPT_BINARY_PROTOCOL, true); + +$stats = $m->getStats(); +$conns1 = $stats[$key]['total_connections']; + +$stats = $m->getStats(); +$conns2 = $stats[$key]['total_connections']; + +var_dump($conns1 == $conns2); +var_dump($m->getOption(Memcached::OPT_NO_BLOCK)); +var_dump($m->getOption(Memcached::OPT_BINARY_PROTOCOL)); +echo "OK" . PHP_EOL; + +// If either or both options are false no need to reconnect +$m->setOption(Memcached::OPT_NO_BLOCK, true); +$m->setOption(Memcached::OPT_BINARY_PROTOCOL, false); + +$stats = $m->getStats(); +$conns1 = $stats[$key]['total_connections']; + +$stats = $m->getStats(); +$conns2 = $stats[$key]['total_connections']; + +var_dump($conns1 == $conns2); +var_dump($m->getOption(Memcached::OPT_NO_BLOCK)); +var_dump($m->getOption(Memcached::OPT_BINARY_PROTOCOL)); +echo "OK" . PHP_EOL; + +// If either or both options are false no need to reconnect +$m->setOption(Memcached::OPT_NO_BLOCK, false); +$m->setOption(Memcached::OPT_BINARY_PROTOCOL, false); + +$stats = $m->getStats(); +$conns1 = $stats[$key]['total_connections']; + +$stats = $m->getStats(); +$conns2 = $stats[$key]['total_connections']; + +var_dump($conns1 == $conns2); +var_dump($m->getOption(Memcached::OPT_NO_BLOCK)); +var_dump($m->getOption(Memcached::OPT_BINARY_PROTOCOL)); +echo "OK" . PHP_EOL; + +?> +--EXPECT-- +bool(false) +int(1) +int(1) +OK +bool(true) +int(0) +int(1) +OK +bool(true) +int(1) +int(0) +OK +bool(true) +int(0) +int(0) +OK diff --git a/tests/touch_binary.phpt b/tests/touch_binary.phpt index b7399616..059ec74c 100644 --- a/tests/touch_binary.phpt +++ b/tests/touch_binary.phpt @@ -1,8 +1,13 @@ --TEST-- Touch in binary mode --SKIPIF-- - --FILE-- getResultcode(); if ($code == $expected) - echo "${op} status code as expected" . PHP_EOL; + echo "{$op} status code as expected" . PHP_EOL; else { $expected = resolve_to_constant ($expected); $code = resolve_to_constant ($code); - echo "${op} status code mismatch, expected ${expected} but got ${code}" . PHP_EOL; + echo "{$op} status code mismatch, expected {$expected} but got {$code}" . PHP_EOL; } } diff --git a/tests/types.inc b/tests/types.inc index 5e0ad5d3..9532c8fc 100644 --- a/tests/types.inc +++ b/tests/types.inc @@ -54,11 +54,12 @@ function memc_types_test ($m, $options) if ($value == $actual && get_class($value) == get_class($actual)) continue; } - echo "=== $types[0] ===\n"; + echo "=== {$key} ===\n"; echo "Expected: "; - var_dump($types[1]); + var_dump($value); echo "Actual: "; var_dump($actual); + } } diff --git a/tests/types_igbinary.phpt b/tests/types_igbinary.phpt index 62fcc29b..da1b26fa 100644 --- a/tests/types_igbinary.phpt +++ b/tests/types_igbinary.phpt @@ -2,7 +2,7 @@ Memcached store & fetch type and value correctness using igbinary serializer --SKIPIF-- --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-- 'bar'); -$rv = $m->set($no_key, $value, 360); +// silent to hide: +// Warning: Undefined variable +// Deprecated: Memcached::set(): Passing null to parameter (PHP 8.1) + +$rv = @$m->set($no_key, $value, 360); var_dump($rv); -$rv = $m->set($key, $no_value, 360); +$rv = @$m->set($key, $no_value, 360); var_dump($rv); -$rv = $m->set($no_key, $no_value, 360); +$rv = @$m->set($no_key, $no_value, 360); var_dump($rv); -$rv = $m->set($key, $value, $no_time); +$rv = @$m->set($key, $value, $no_time); var_dump($rv); ?> --EXPECTF-- -Notice: Undefined variable: no_key in %s bool(false) - -Notice: Undefined variable: no_value in %s bool(true) - -Notice: Undefined variable: no_key in %s - -Notice: Undefined variable: no_value in %s bool(false) - -Notice: Undefined variable: no_time in %s bool(true) diff --git a/tests/user-flags.phpt b/tests/user-flags.phpt index 2ed44c9a..4c987f42 100644 --- a/tests/user-flags.phpt +++ b/tests/user-flags.phpt @@ -1,7 +1,7 @@ --TEST-- Memcached user flags --SKIPIF-- - + --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..a691680d 100644 --- a/tests/vbucket.phpt +++ b/tests/vbucket.phpt @@ -2,7 +2,7 @@ Memcached virtual buckets --SKIPIF-- --FILE-- @@ -18,14 +18,6 @@ var_dump ($m->setBucket (array (1,2,2), array (1,2,2), 2)); var_dump ($m->setBucket (array ('a', 'b', 'c'), null, 2)); -var_dump ($m->setBucket (array (), null, 2)); - -var_dump ($m->setBucket (array (), array (), -1)); - -var_dump ($m->setBucket (null, array (), -1)); - -var_dump ($m->setBucket (array (-1), array (-1), 1)); - echo "OK\n"; ?> @@ -33,16 +25,4 @@ echo "OK\n"; bool(true) bool(true) bool(true) - -Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d -bool(false) - -Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d -bool(false) - -Warning: Memcached::setBucket() expects parameter 1 to be array, null given in %s on line %d -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/vbucket_error_7.phpt b/tests/vbucket_error_7.phpt new file mode 100644 index 00000000..286534af --- /dev/null +++ b/tests/vbucket_error_7.phpt @@ -0,0 +1,40 @@ +--TEST-- +Memcached virtual buckets +--SKIPIF-- += 80000) die("skip PHP 7 only"); +?> +--FILE-- + Memcached::DISTRIBUTION_VIRTUAL_BUCKET + )); + +var_dump ($m->setBucket (array (), null, 2)); + +var_dump ($m->setBucket (array (), array (), -1)); + +var_dump ($m->setBucket (null, array (), -1)); + +var_dump ($m->setBucket (array (-1), array (-1), 1)); + +echo "OK\n"; + +?> +--EXPECTF-- + +Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d +bool(false) + +Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d +bool(false) + +Warning: Memcached::setBucket() expects parameter 1 to be array, null given in %s on line %d +NULL + +Warning: Memcached::setBucket(): the map must contain positive integers in %s on line %d +bool(false) +OK diff --git a/tests/vbucket_error_8.phpt b/tests/vbucket_error_8.phpt new file mode 100644 index 00000000..c8af2315 --- /dev/null +++ b/tests/vbucket_error_8.phpt @@ -0,0 +1,41 @@ +--TEST-- +Memcached virtual buckets +--SKIPIF-- + +--FILE-- + Memcached::DISTRIBUTION_VIRTUAL_BUCKET + )); + +var_dump ($m->setBucket (array (), null, 2)); + +var_dump ($m->setBucket (array (), array (), -1)); + +try { + var_dump ($m->setBucket (null, array (), -1)); +} catch (TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} + +var_dump ($m->setBucket (array (-1), array (-1), 1)); + +echo "OK\n"; + +?> +--EXPECTF-- +Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d +bool(false) + +Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d +bool(false) +Memcached::setBucket(): Argument #1 ($host_map) must be of type array, null given + +Warning: Memcached::setBucket(): the map must contain positive integers in %s on line %d +bool(false) +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