package.xml0000644000076700000240000002517612010120566012326 0ustar andreistaff memcached pecl.php.net PHP extension for interfacing with memcached via libmemcached library This extension uses libmemcached library to provide API for communicating with memcached servers. Andrei Zmievski andrei andrei@php.net yes 2012-08-07 2.1.0 2.1.0 stable stable PHP - 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 5.2.0 6.0.0 6.0.0 1.4.0b1 memcached stable stable 2.1.0 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 stable stable 2.0.1 2.0.1 2012-03-03 - Fix embedded version number to be not -dev stable stable 2.0.0 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 beta beta 2.0.0b2 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. - Improve session support: * Add support for libmemcached config string * Add persistence support via PERSISTENT=persistent_id prefix of the save_path - Add 3rd parameter to the __construct() that allows specification of libmemcached configuration string - Fix a possible crash in __construct() when using persistent connections - Add work-around a bug in libmemcached < 0.50 that causes truncation of last character of server key prefix - When using multiple servers implement transparent fail-over - Fix php_memc_cas_impl() implementation when server_key is not being used - Add support for incrementByKey() and decrementByKey() - Make increment/decrement initialize value when it is not available (when using binary protocol) beta beta 2.0.0b1 2.0.0b1 2011-03-12 - Change the return value for non-existing keys to be NULL rather than 'false', affects simple get only - Add fastlz library that provides better/faster payload compression - Add configure switch to enable/disable JSON serialization support - Add getAllKeys() method - Add deleteMulti() and deleteMultiByKey() methods - Add isPristine() and isPersistent() methods - Add setOptions() method to set multiple options at once - Add SERIALIZER_JSON_ARRAY type that decodes JSON payloads as arrays instead of objects - Add support for Unix domain socket connections - Add memcached.compression_threshold INI setting - Add memcached.compression_factor INI setting - Add memcached.compression_type INI setting - Implement a few speed optimizations - Many bug fixes and memory leaks plugged - Add several more tests - Add constants for libmemcached 0.37+: * Memcached::OPT_NUMBER_OF_REPLICAS * Memcached::OPT_RANDOMIZE_REPLICA_READ - Add 'on_new' callback to constructor - Add SASL support stable stable 1.0.2 1.0.2 2010-05-03 - Fix build for libmemcached-0.39 (memcached_server_list() issue) stable stable 1.0.1 1.0.1 2010-03-11 - Fix build for libmemcached-0.38. stable stable 1.0.0 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). beta beta 0.2.0 0.2.0 2009-06-04 - Refactor the way payload types are stored in memcached flags to optimize the structure and allow for future expansion. WARNING! You have to flush the cache when upgrading from an older version. - 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. - Add 'flags' parameter to getMulti() and getMultiByKey(). - Add GET_PRESERVE_ORDER class constant that can be used with abovementioned flags parameter to make the order of the keys in the response match the request. - Fix an issue with retrieving 0-length payloads (FALSE boolean value). - Add several tests. beta beta 0.1.5 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. beta beta 0.1.4 0.1.4 2009-02-13 - Fix compilation against PHP 5.3. - Add support for 'igbinary' serializer (Oleg Grenrus) beta beta 0.1.3 0.1.3 2009-02-06 - Bludgeon bug #15896 (Memcached setMulti error) into submission. beta beta 0.1.2 0.1.2 2009-02-06 - Fix bug #15896 (Memcached setMulti error). - Check for empty key in getServerByKey(). - Allow passing 'null' for callbacks. - get() with cas token fetching wasn't erroring out properly. - Rename certain parameters in the API to be more clear. - Allow only strings as the append/prepend value. - Remove expiration parameter from append/prepend. beta beta 0.1.1 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. beta beta 0.1.0 0.1.0 2009-01-29 - Initial PECL release memcached-2.1.0/README.markdown0000644000076700000240000000074412010120566015246 0ustar andreistaffDescription ----------- This extension uses libmemcached library to provide API for communicating with 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. Resources --------- * [libmemcached](http://tangent.org/552/libmemcached.html) * [memcached](http://www.danga.com/memcached/) * [igbinary](http://opensource.dynamoid.com/) memcached-2.1.0/CREDITS0000644000076700000240000000007312010120566013560 0ustar andreistaffmemcached Andrei Zmievski, Oleg Grenrus (igbinary support) memcached-2.1.0/LICENSE0000644000076700000240000000622212010120566013547 0ustar andreistaff-------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from ". THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------- This software consists of voluntary contributions made by many individuals on behalf of the PHP Group. The PHP Group can be contacted via Email at group@php.net. For more information on the PHP Group and the PHP project, please see . PHP includes the Zend Engine, freely available at . memcached-2.1.0/ChangeLog0000644000076700000240000001127412010120566014317 0ustar andreistaffmemcached extension changelog Version 2.1.0 ------------- * 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 ------------- * Fix embedded version number to be not -dev Version 2.0.0 ------------- * 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 --------------- * 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. * Improve session support: - Add support for libmemcached config string - Add persistence support via PERSISTENT=persistent_id prefix of the save_path * Add 3rd parameter to the __construct() that allows specification of libmemcached configuration string * Fix a possible crash in __construct() when using persistent connections * Add work-around a bug in libmemcached < 0.50 that causes truncation of last character of server key prefix * When using multiple servers implement transparent fail-over * Fix php_memc_cas_impl() implementation when server_key is not being used * Add support for incrementByKey() and decrementByKey() * Make increment/decrement initialize value when it is not available (when using binary protocol) Version 2.0.0b1 --------------- * Change the return value for non-existing keys to be NULL rather than 'false', affects simple get only * Add fastlz library that provides better/faster payload compression * Add configure switch to enable/disable JSON serialization support * Add getAllKeys() method * Add deleteMulti() and deleteMultiByKey() methods * Add isPristine() and isPersistent() methods * Add setOptions() method to set multiple options at once * Add SERIALIZER_JSON_ARRAY type that decodes JSON payloads as arrays instead of objects * Add support for Unix domain socket connections * Add memcached.compression_threshold INI setting * Add memcached.compression_factor INI setting * Add memcached.compression_type INI setting * Implement a few speed optimizations * Many bug fixes and memory leaks plugged * Add several more tests * Add constants for libmemcached 0.37+: - Memcached::OPT_NUMBER_OF_REPLICAS - Memcached::OPT_RANDOMIZE_REPLICA_READ * Add 'on_new' callback to constructor * Add SASL support Version 1.0.1 ------------- * 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 ------------- * 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 ------------- * 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. * Add 'flags' parameter to getMulti() and getMultiByKey(). * Add GET_PRESERVE_ORDER class constant that can be used with abovementioned flags parameter to make the order of the keys in the response match the request. * Fix an issue with retrieving 0-length payloads (FALSE boolean value). * Refactor the way payload types are stored in memcached flags to optimize the structure and allow for future expansion. WARNING! You have to flush the cache when upgrading to this version. * Add several tests. Version 0.1.5 ------------- * 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 ------------- * Fix compilation against PHP 5.3. * Add support for 'igbinary' serializer (Oleg Grenrus) Version 0.1.3 ------------- * Bludgeon bug #15896 (Memcached setMulti error) into submission. Version 0.1.2 ------------- * Fix bug #15896 (Memcached setMulti error). * Check for empty key in getServerByKey(). * Allow passing 'null' for callbacks. * get() with cas token fetching wasn't erroring out properly. * Rename certain parameters in the API to be more clear. * Allow only strings as the append/prepend value. * Remove expiration parameter from append/prepend. Version 0.1.1 ------------- * Add OPT_LIBKETAMA_COMPATIBLE option. * Implement addServers() method. * Swap internal compressed and serialized flags to be compatible with other clients. Version 0.1.0 ------------- * Initial release memcached-2.1.0/memcached-api.php0000644000076700000240000001167512010120566015740 0ustar andreistaff comp_len * factor ; ; the default value is 1.3 (23% space saving) 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 ; Set the default serializer for new memcached objects. ; valid values are: php, igbinary, json, json_array ; ; json - standard php JSON encoding. This serializer ; is fast and compact but only works on UTF-8 ; encoded data and does not fully implement ; serializing. See the JSON extension. ; json_array - as json, but decodes into arrays ; php - the standard php serializer ; igbinary - a binary serializer ; ; The default is igbinary if available and php otherwise. memcached.serializer = "igbinary" memcached-2.1.0/fastlz/LICENSE0000644000076700000240000000231212010120566015046 0ustar andreistaffFastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. memcached-2.1.0/config.m40000644000076700000240000002221412010120566014250 0ustar andreistaffdnl dnl $ Id: $ dnl vim:se ts=2 sw=2 et: PHP_ARG_ENABLE(memcached, whether to 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) PHP_ARG_ENABLE(memcached-session, whether to enable memcached session handler support, [ --disable-memcached-session Disable 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) PHP_ARG_ENABLE(memcached-json, whether to enable memcached json serializer support, [ --enable-memcached-json Enable memcached json serializer support], no, no) PHP_ARG_ENABLE(memcached-sasl, whether to disable memcached sasl support, [ --disable-memcached-sasl Disable memcached sasl support], 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) fi if test -z "$PHP_DEBUG"; then AC_ARG_ENABLE(debug, [ --enable-debug compile with debugging symbols],[ PHP_DEBUG=$enableval ],[ PHP_DEBUG=no ]) fi if test "$PHP_MEMCACHED" != "no"; then dnl # zlib if test "$PHP_ZLIB_DIR" != "no" && test "$PHP_ZLIB_DIR" != "yes"; then if test -f "$PHP_ZLIB_DIR/include/zlib/zlib.h"; then PHP_ZLIB_DIR="$PHP_ZLIB_DIR" PHP_ZLIB_INCDIR="$PHP_ZLIB_DIR/include/zlib" elif test -f "$PHP_ZLIB_DIR/include/zlib.h"; then PHP_ZLIB_DIR="$PHP_ZLIB_DIR" PHP_ZLIB_INCDIR="$PHP_ZLIB_DIR/include" else AC_MSG_ERROR([Can't find ZLIB headers under "$PHP_ZLIB_DIR"]) fi else for i in /usr/local /usr; do if test -f "$i/include/zlib/zlib.h"; then PHP_ZLIB_DIR="$i" PHP_ZLIB_INCDIR="$i/include/zlib" elif test -f "$i/include/zlib.h"; then PHP_ZLIB_DIR="$i" PHP_ZLIB_INCDIR="$i/include" fi done fi AC_MSG_CHECKING([for zlib location]) if test "$PHP_ZLIB_DIR" != "no" && test "$PHP_ZLIB_DIR" != "yes"; then AC_MSG_RESULT([$PHP_ZLIB_DIR]) PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR/$PHP_LIBDIR, MEMCACHED_SHARED_LIBADD) PHP_ADD_INCLUDE($PHP_ZLIB_INCDIR) else AC_MSG_ERROR([memcached support requires ZLIB. Use --with-zlib-dir= to specify the prefix where ZLIB headers and library are located]) fi if test "$PHP_MEMCACHED_SESSION" != "no"; then AC_MSG_CHECKING([for session includes]) session_inc_path="" if test -f "$abs_srcdir/include/php/ext/session/php_session.h"; then session_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/session/php_session.h"; then session_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/session/php_session.h"; then session_inc_path="$phpincludedir" else for i in php php4 php5 php6; do if test -f "$prefix/include/$i/ext/session/php_session.h"; then session_inc_path="$prefix/include/$i" fi done fi if test "$session_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_session.h]) else 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]) 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]) fi fi if test "$PHP_MEMCACHED_IGBINARY" != "no"; then AC_MSG_CHECKING([for igbinary includes]) igbinary_inc_path="" if test -f "$abs_srcdir/include/php/ext/igbinary/igbinary.h"; then igbinary_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/session/igbinary.h"; then igbinary_inc_path="$phpincludedir" elif test -f "$phpincludedir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$phpincludedir" else for i in php php4 php5 php6; do if test -f "$prefix/include/$i/ext/igbinary/igbinary.h"; then igbinary_inc_path="$prefix/include/$i" fi done fi if test "$igbinary_inc_path" = ""; then AC_MSG_ERROR([Cannot find igbinary.h]) else AC_MSG_RESULT([$igbinary_inc_path]) fi fi AC_MSG_CHECKING([for memcached session support]) if test "$PHP_MEMCACHED_SESSION" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_MEMCACHED_SESSION,1,[Whether memcache session handler is enabled]) SESSION_INCLUDES="-I$session_inc_path" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcached, session) ]) else SESSION_INCLUDES="" AC_MSG_RESULT([disabled]) fi AC_MSG_CHECKING([for memcached igbinary support]) if test "$PHP_MEMCACHED_IGBINARY" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_MEMCACHED_IGBINARY,1,[Whether memcache igbinary serializer is enabled]) IGBINARY_INCLUDES="-I$igbinary_inc_path" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcached, igbinary) ]) else IGBINARY_INCLUDES="" AC_MSG_RESULT([disabled]) fi if test "$PHP_MEMCACHED_SASL" != "no"; then AC_CHECK_HEADERS([sasl/sasl.h], [memcached_enable_sasl="yes"], [memcached_enable_sasl="no"]) AC_MSG_CHECKING([whether to enable sasl support]) AC_MSG_RESULT([$memcached_enable_sasl]) fi AC_MSG_CHECKING([for libmemcached location]) if test "$PHP_LIBMEMCACHED_DIR" != "no" && test "$PHP_LIBMEMCACHED_DIR" != "yes"; then if ! test -r "$PHP_LIBMEMCACHED_DIR/include/libmemcached-1.0/memcached.h"; then AC_MSG_ERROR([Can't find libmemcached 1.0.x headers under "$PHP_LIBMEMCACHED_DIR"]) fi else PHP_LIBMEMCACHED_DIR="no" for i in /usr /usr/local; do if test -r "$i/include/libmemcached/memcached.h"; then PHP_LIBMEMCACHED_DIR=$i break fi done fi if test "$PHP_LIBMEMCACHED_DIR" = "no"; then AC_MSG_ERROR([memcached support requires libmemcached 1.0.x. Use --with-libmemcached-dir= to specify the prefix where libmemcached headers and library are located]) else AC_MSG_RESULT([$PHP_LIBMEMCACHED_DIR]) PHP_LIBMEMCACHED_INCDIR="$PHP_LIBMEMCACHED_DIR/include" PHP_ADD_INCLUDE($PHP_LIBMEMCACHED_INCDIR) PHP_ADD_LIBRARY_WITH_PATH(memcached, $PHP_LIBMEMCACHED_DIR/$PHP_LIBDIR, MEMCACHED_SHARED_LIBADD) PHP_SUBST(MEMCACHED_SHARED_LIBADD) PHP_MEMCACHED_FILES="php_memcached.c fastlz/fastlz.c g_fmt.c" if test "$PHP_MEMCACHED_SESSION" != "no"; then PHP_MEMCACHED_FILES="${PHP_MEMCACHED_FILES} php_memcached_session.c" fi PHP_NEW_EXTENSION(memcached, $PHP_MEMCACHED_FILES, $ext_shared,,$SESSION_INCLUDES $IGBINARY_INCLUDES) PHP_ADD_BUILD_DIR($ext_builddir/fastlz, 1) ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcached, spl, true) ]) fi fi memcached-2.1.0/config.w320000644000076700000240000000076012010120566014345 0ustar andreistaff// $ Id: $ // vim:ft=javascript ARG_WITH('memcached', 'libmemcached extension', 'no'); if (PHP_MEMCACHED == "yes") { if (!CHECK_LIB("memcached.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"); AC_DEFINE("HAVE_MEMCACHED", 1, "memcached support"); } memcached-2.1.0/php_memcached.c0000644000076700000240000033716012010120566015473 0ustar andreistaff/* +----------------------------------------------------------------------+ | Copyright (c) 2009-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski | +----------------------------------------------------------------------+ */ /* $ Id: $ */ /* TODO * - set LIBKETAMA_COMPATIBLE as the default? * - fix unserialize(serialize($memc)) * - ability to set binary protocol for sessions */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef ZTS #include "TSRM.h" #endif #include #include #include #include #include #include #include #include #include "php_memcached.h" #include "g_fmt.h" #ifdef HAVE_MEMCACHED_SESSION # include "php_memcached_session.h" #endif #include "fastlz/fastlz.h" #include /* Used to store the size of the block */ #if defined(HAVE_INTTYPES_H) #include #elif defined(HAVE_STDINT_H) #include #endif #ifdef PHP_WIN32 # include "win32/php_stdint.h" #else # ifndef HAVE_INT32_T # if SIZEOF_INT == 4 typedef int int32_t; # elif SIZEOF_LONG == 4 typedef long int int32_t; # endif # endif # ifndef HAVE_UINT32_T # if SIZEOF_INT == 4 typedef unsigned int uint32_t; # elif SIZEOF_LONG == 4 typedef unsigned long int uint32_t; # endif # endif #endif #ifdef HAVE_JSON_API # include "ext/json/php_json.h" #endif #ifdef HAVE_MEMCACHED_IGBINARY # include "ext/igbinary/igbinary.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 /**************************************** Custom options ****************************************/ #define MEMC_OPT_COMPRESSION -1001 #define MEMC_OPT_PREFIX_KEY -1002 #define MEMC_OPT_SERIALIZER -1003 #define MEMC_OPT_COMPRESSION_TYPE -1004 /**************************************** Custom result codes ****************************************/ #define MEMC_RES_PAYLOAD_FAILURE -1001 /**************************************** Payload value flags ****************************************/ #define MEMC_VAL_TYPE_MASK 0xf #define MEMC_VAL_GET_TYPE(flags) ((flags) & MEMC_VAL_TYPE_MASK) #define MEMC_VAL_SET_TYPE(flags, type) ((flags) |= ((type) & MEMC_VAL_TYPE_MASK)) #define MEMC_VAL_IS_STRING 0 #define MEMC_VAL_IS_LONG 1 #define MEMC_VAL_IS_DOUBLE 2 #define MEMC_VAL_IS_BOOL 3 #define MEMC_VAL_IS_SERIALIZED 4 #define MEMC_VAL_IS_IGBINARY 5 #define MEMC_VAL_IS_JSON 6 #define MEMC_VAL_COMPRESSED (1<<4) #define MEMC_VAL_COMPRESSION_ZLIB (1<<5) #define MEMC_VAL_COMPRESSION_FASTLZ (1<<6) /**************************************** "get" operation flags ****************************************/ #define MEMC_GET_PRESERVE_ORDER (1<<0) /**************************************** 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 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; #endif } *obj; zend_bool is_persistent; zend_bool is_pristine; int rescode; int memc_errno; } php_memc_t; enum { MEMC_OP_SET, MEMC_OP_TOUCH, MEMC_OP_ADD, MEMC_OP_REPLACE, MEMC_OP_APPEND, MEMC_OP_PREPEND }; static zend_class_entry *memcached_ce = NULL; static zend_class_entry *memcached_exception_ce = NULL; static zend_object_handlers memcached_object_handlers; struct callbackContext { zval *array; zval *entry; memcached_stat_st *stats; /* for use with functions that need stats */ void *return_value; unsigned int i; /* for use with structures mapped against servers */ }; #if HAVE_SPL static zend_class_entry *spl_ce_RuntimeException = NULL; #endif #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3) const zend_fcall_info empty_fcall_info = { 0, NULL, NULL, NULL, NULL, 0, NULL, NULL, 0 }; #undef ZEND_BEGIN_ARG_INFO_EX #define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ static zend_arg_info name[] = { \ { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, #endif ZEND_DECLARE_MODULE_GLOBALS(php_memcached) #ifdef COMPILE_DL_MEMCACHED ZEND_GET_MODULE(memcached) #endif 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; } else { return FAILURE; } return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } static PHP_INI_MH(OnUpdateSerializer) { if (!new_value) { MEMC_G(serializer) = SERIALIZER_DEFAULT; } else if (!strcmp(new_value, "php")) { MEMC_G(serializer) = SERIALIZER_PHP; #ifdef HAVE_MEMCACHE_IGBINARY } else if (!strcmp(new_value, "igbinary")) { MEMC_G(serializer) = 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; #endif // JSON } else { return FAILURE; } return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* {{{ 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_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_prefix", "memc.sess.key.", PHP_INI_ALL, OnUpdateString, sess_prefix, 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 PHP_INI_END() /* }}} */ /**************************************** 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, 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); static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); 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, memcached_server_instance_st instance, void *in_context); static memcached_return php_memc_do_stats_callback(const memcached_st *ptr, memcached_server_instance_st instance, void *in_context); static memcached_return php_memc_do_version_callback(const memcached_st *ptr, memcached_server_instance_st instance, void *in_context); static void php_memc_destroy(struct memc_obj *m_obj, zend_bool persistent TSRMLS_DC); /**************************************** Method implementations ****************************************/ 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) { 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); } /* Call the cb */ params[0] = &object; params[1] = &pid_z_ptr; fci->params = params; fci->param_count = 2; fci->retval_ptr_ptr = &retval_ptr; fci->no_separation = 1; if (zend_call_function(fci, fci_cache TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to invoke 'on_new' callback %s()", Z_STRVAL_P(fci->function_name)); return 0; } zval_dtor(pid_z_ptr); if (retval_ptr) { zval_ptr_dtor(&retval_ptr); } return 1; } static int le_memc, le_memc_sess; static int php_memc_list_entry(void) { return le_memc; } /* {{{ 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) { 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; char *plist_key = NULL; int plist_key_len = 0; zend_fcall_info fci = {0}; zend_fcall_info_cache fci_cache; 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; } i_obj = (php_memc_t *) zend_object_store_get_object(object TSRMLS_CC); i_obj->is_pristine = 0; if (persistent_id && *persistent_id) { zend_rsrc_list_entry *le = NULL; is_persistent = 1; plist_key_len = spprintf(&plist_key, 0, "memcached:id=%s", persistent_id); plist_key_len += 1; 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; } i_obj->is_persistent = is_persistent; if (!m_obj) { m_obj = pecalloc(1, sizeof(*m_obj), is_persistent); if (m_obj == NULL) { if (plist_key) { efree(plist_key); } php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory: cannot allocate handle"); /* not reached */ } if (conn_str) { m_obj->memc = memcached(conn_str, conn_str_len); if (!m_obj->memc) { char error_buffer[1024]; if (plist_key) { efree(plist_key); } 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"); } /* 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; i_obj->obj = m_obj; i_obj->is_pristine = 1; 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); } i_obj->obj = NULL; if (is_persistent) { php_memc_destroy(m_obj, is_persistent TSRMLS_CC); } return; } } if (is_persistent) { zend_rsrc_list_entry le; 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); } php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not register persistent entry"); /* not reached */ } } } if (plist_key) { efree(plist_key); } } /* }}} */ /* {{{ Memcached::get(string key [, mixed callback [, double &cas_token ] ]) 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 [, double &cas_token ] ]) 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); } /* }}} */ /* {{{ -- php_memc_get_impl */ static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { char *key = NULL; int key_len = 0; char *server_key = NULL; int server_key_len = 0; 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; zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|f!z", &server_key, &server_key_len, &key, &key_len, &fci, &fcc, &cas_token) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f!z", &key, &key_len, &fci, &fcc, &cas_token) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FROM_GET; } keys[0] = key; key_lens[0] = key_len; if (cas_token) { uint64_t orig_cas_flag; /* * Enable CAS support, but only if it is currently disabled. */ 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); } status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, keys, key_lens, 1); if (orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FROM_GET; } status = MEMCACHED_SUCCESS; memcached_result_create(m_obj->memc, &result); if (memcached_fetch_result(m_obj->memc, &result, &status) == NULL) { /* This is for historical reasons */ if (status == MEMCACHED_END) status = MEMCACHED_NOTFOUND; /* * 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 (status == MEMCACHED_NOTFOUND && fci.size != 0) { status = php_memc_do_cache_callback(getThis(), &fci, &fcc, key, key_len, return_value TSRMLS_CC); ZVAL_DOUBLE(cas_token, 0); } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { memcached_result_free(&result); RETURN_FROM_GET; } /* if we have a callback, all processing is done */ if (fci.size != 0) { memcached_result_free(&result); return; } } payload = memcached_result_value(&result); payload_len = memcached_result_length(&result); flags = memcached_result_flags(&result); cas = memcached_result_cas(&result); 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; } zval_dtor(cas_token); ZVAL_DOUBLE(cas_token, (double)cas); memcached_result_free(&result); } else { int rc; zend_bool return_value_set = 0; status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, keys, key_lens, 1); payload = memcached_fetch(m_obj->memc, NULL, NULL, &payload_len, &flags, &status); /* This is for historical reasons */ if (status == MEMCACHED_END) status = MEMCACHED_NOTFOUND; /* * If payload wasn't found and we have a read-through callback, invoke it to get * the value. The callback will take care of storing the value back into memcache. * The callback will set the return value. */ if (payload == NULL && status == MEMCACHED_NOTFOUND && fci.size != 0) { size_t dummy_length; uint32_t dummy_flags; memcached_return dummy_status; status = php_memc_do_cache_callback(getThis(), &fci, &fcc, key, key_len, return_value TSRMLS_CC); return_value_set = 1; (void)memcached_fetch(m_obj->memc, NULL, NULL, &dummy_length, &dummy_flags, &dummy_status); } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { if (payload) { free(payload); } RETURN_FROM_GET; } /* if memcached gave a value and there was no callback, payload may be NULL */ if (!return_value_set) { rc = php_memc_zval_from_payload(return_value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC); free(payload); if (rc < 0) { i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FROM_GET; } } } } /* }}} */ /* {{{ Memcached::getMulti(array keys [, array &cas_tokens ]) 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 [, array &cas_tokens ]) 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); } /* }}} */ /* {{{ -- php_memc_getMulti_impl */ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { zval *keys = NULL; char *server_key = NULL; int server_key_len = 0; size_t num_keys = 0; zval **entry = NULL; char *payload = NULL; size_t payload_len = 0; const char **mkeys = NULL; size_t *mkeys_len = NULL; char *res_key = NULL; size_t res_key_len = 0; uint32_t flags; uint64_t cas = 0; zval *cas_tokens = NULL; uint64_t orig_cas_flag; zval *value; long get_flags = 0; int i = 0; zend_bool preserve_order; memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|zl", &server_key, &server_key_len, &keys, &cas_tokens, &get_flags) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|zl", &keys, &cas_tokens, &get_flags) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; preserve_order = (get_flags & MEMC_GET_PRESERVE_ORDER); num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); mkeys = safe_emalloc(num_keys, sizeof(*mkeys), 0); mkeys_len = safe_emalloc(num_keys, sizeof(*mkeys_len), 0); array_init(return_value); /* * Create the array of keys for libmemcached. If none of the keys were valid * (strings), set bad key result code and return. */ for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(keys)); zend_hash_get_current_data(Z_ARRVAL_P(keys), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(keys))) { if (Z_TYPE_PP(entry) != IS_STRING) { convert_to_string_ex(entry); } if (Z_TYPE_PP(entry) == IS_STRING && Z_STRLEN_PP(entry) > 0) { mkeys[i] = Z_STRVAL_PP(entry); mkeys_len[i] = Z_STRLEN_PP(entry); if (preserve_order) { add_assoc_null_ex(return_value, mkeys[i], mkeys_len[i] + 1); } i++; } } if (i == 0) { i_obj->rescode = MEMCACHED_NOTFOUND; efree(mkeys); efree(mkeys_len); return; } /* * Enable CAS support, but only if it is currently disabled. */ if (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); } } 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); /* * Restore the CAS support flag, but only if we had to turn it on. */ if (cas_tokens && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } efree(mkeys); efree(mkeys_len); /* * 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) { zval_dtor(cas_tokens); array_init(cas_tokens); } memcached_result_create(m_obj->memc, &result); while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { if (status != MEMCACHED_SUCCESS) { status = MEMCACHED_SOME_ERRORS; php_memc_handle_error(i_obj, status TSRMLS_CC); continue; } payload = memcached_result_value(&result); payload_len = memcached_result_length(&result); flags = memcached_result_flags(&result); res_key = memcached_result_key_value(&result); res_key_len = memcached_result_key_length(&result); /* * This may be a bug in libmemcached, the key is not null terminated * whe using the binary protocol. */ res_key[res_key_len] = 0; MAKE_STD_ZVAL(value); if (php_memc_zval_from_payload(value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { zval_ptr_dtor(&value); if (EG(exception)) { status = MEMC_RES_PAYLOAD_FAILURE; php_memc_handle_error(i_obj, status TSRMLS_CC); memcached_quit(m_obj->memc); break; } status = MEMCACHED_SOME_ERRORS; i_obj->rescode = MEMCACHED_SOME_ERRORS; continue; } add_assoc_zval_ex(return_value, res_key, res_key_len+1, value); if (cas_tokens) { cas = memcached_result_cas(&result); add_assoc_double_ex(cas_tokens, res_key, res_key_len+1, (double)cas); } } memcached_result_free(&result); 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); } zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ Memcached::getDelayed(array keys [, bool with_cas [, mixed callback ] ]) Sends a request for the given keys and returns immediately */ PHP_METHOD(Memcached, getDelayed) { php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::getDelayedByKey(string server_key, array keys [, bool with_cas [, mixed callback ] ]) Sends a request for the given keys from the server identified by the server key and returns immediately */ PHP_METHOD(Memcached, getDelayedByKey) { php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ -- php_memc_getDelayed_impl */ static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { zval *keys = NULL; char *server_key = NULL; int server_key_len = 0; 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; 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; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|bf!", &keys, &with_cas, &fci, &fcc) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; /* * Create the array of keys for libmemcached. If none of the keys were valid * (strings), set bad key result code and return. */ num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); mkeys = safe_emalloc(num_keys, sizeof(*mkeys), 0); mkeys_len = safe_emalloc(num_keys, sizeof(*mkeys_len), 0); 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); } } /* * Issue the request, but collect results only if the result callback is provided. */ status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, mkeys, mkeys_len, i); /* * Restore the CAS support flag, but only if we had to turn it on. */ if (with_cas && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } efree(mkeys); efree(mkeys_len); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { zval_dtor(return_value); RETURN_FALSE; } 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); /* 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; } } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::fetch() Returns the next result from a previous delayed request */ PHP_METHOD(Memcached, fetch) { char *res_key = NULL; size_t res_key_len = 0; 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; if (zend_parse_parameters_none() == FAILURE) { return; } 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); MAKE_STD_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); i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } 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); } memcached_result_free(&result); } /* }}} */ /* {{{ Memcached::fetchAll() Returns all the results from a previous delayed request */ PHP_METHOD(Memcached, fetchAll) { char *res_key = NULL; size_t res_key_len = 0; 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; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; 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); MAKE_STD_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); } add_next_index_zval(return_value, entry); } memcached_result_free(&result); if (status != MEMCACHED_END && php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ Memcached::set(string key, mixed value [, int expiration ]) Sets the value for the given key */ PHP_METHOD(Memcached, set) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 0); } /* }}} */ /* {{{ Memcached::setByKey(string server_key, string key, mixed value [, int expiration ]) Sets the value for the given key on the server identified by the server key */ PHP_METHOD(Memcached, setByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 1); } /* }}} */ #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000002 /* {{{ Memcached::touch(string key, [, int expiration ]) Sets a new expiration for the given key */ PHP_METHOD(Memcached, touch) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 0); } /* }}} */ /* {{{ Memcached::touchbyKey(string key, [, int expiration ]) Sets a new expiration for the given key */ PHP_METHOD(Memcached, touchByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 1); } /* }}} */ #endif /* {{{ Memcached::setMulti(array items [, int expiration ]) Sets the keys/values specified in the items array */ PHP_METHOD(Memcached, setMulti) { php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::setMultiByKey(string server_key, array items [, int expiration ]) Sets the keys/values specified in the items array on the server identified by the given server key */ PHP_METHOD(Memcached, setMultiByKey) { php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ #define PHP_MEMC_FAILOVER_RETRY \ if (!by_key && retry < 2) { \ 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: \ if (memcached_server_count(m_obj->memc) > 0) { \ retry++; \ 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; 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]; 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; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &entries, &expiration) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(entries)); zend_hash_get_current_data(Z_ARRVAL_P(entries), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(entries))) { int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(entries), &str_key, &str_key_len, &num_key, 0, NULL); if (key_type == HASH_KEY_IS_LONG) { /* Array keys are unsigned, but php integers are signed. * Keys must be converted to signed strings that match * php integers. */ assert(sizeof(tmp_key) >= sizeof(ZEND_TOSTR(LONG_MIN))); 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) { flags |= MEMC_VAL_COMPRESSED; } 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; } 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 (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { PHP_MEMC_FAILOVER_RETRY efree(payload); RETURN_FALSE; } efree(payload); } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::add(string key, mixed value [, int expiration ]) Sets the value for the given key, failing if the key already exists */ PHP_METHOD(Memcached, add) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 0); } /* }}} */ /* {{{ Memcached::addByKey(string server_key, string key, mixed value [, int expiration ]) Sets the value for the given key on the server identified by the sever key, failing if the key already exists */ PHP_METHOD(Memcached, addByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 1); } /* }}} */ /* {{{ Memcached::append(string key, mixed value) Appends the value to existing one for the key */ PHP_METHOD(Memcached, append) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 0); } /* }}} */ /* {{{ Memcached::appendByKey(string server_key, string key, mixed value) Appends the value to existing one for the key on the server identified by the server key */ PHP_METHOD(Memcached, appendByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 1); } /* }}} */ /* {{{ Memcached::prepend(string key, mixed value) Prepends the value to existing one for the key */ PHP_METHOD(Memcached, prepend) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 0); } /* }}} */ /* {{{ Memcached::prependByKey(string server_key, string key, mixed value) Prepends the value to existing one for the key on the server identified by the server key */ PHP_METHOD(Memcached, prependByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 1); } /* }}} */ /* {{{ Memcached::replace(string key, mixed value [, int expiration ]) Replaces the value for the given key, failing if the key doesn't exist */ PHP_METHOD(Memcached, replace) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 0); } /* }}} */ /* {{{ Memcached::replaceByKey(string server_key, string key, mixed value [, int expiration ]) Replaces the value for the given key on the server identified by the server key, failing if the key doesn't exist */ PHP_METHOD(Memcached, replaceByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 1); } /* }}} */ /* {{{ -- php_memc_store_impl */ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key) { 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; time_t expiration = 0; char *payload; size_t payload_len; uint32_t flags = 0; uint32_t retry = 0; memcached_return status; 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); value = &s_zvalue; ZVAL_STRINGL(value, s_value, s_value_len, 0); } 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; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|l", &server_key, &server_key_len, &key, &key_len, &value, &expiration) == FAILURE) { return; } } } else { if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &s_value, &s_value_len) == FAILURE) { return; } INIT_ZVAL(s_zvalue); value = &s_zvalue; ZVAL_STRINGL(value, s_value, s_value_len, 0); } else if (op == MEMC_OP_TOUCH) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, &expiration) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &key, &key_len, &value, &expiration) == FAILURE) { return; } } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } if (m_obj->compression) { /* * 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; } flags |= MEMC_VAL_COMPRESSED; } if (op == MEMC_OP_TOUCH) { if (!memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "touch is only supported with binary protocol"); RETURN_FALSE; } } 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; } } 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; 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; 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 */ assert(0); break; } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { PHP_MEMC_FAILOVER_RETRY RETVAL_FALSE; } else { RETVAL_TRUE; } if (op != MEMC_OP_TOUCH) { efree(payload); } } /* }}} */ /* {{{ -- php_memc_cas_impl */ static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { double cas_d; uint64_t cas; char *key = NULL; int key_len = 0; char *server_key = NULL; int server_key_len = 0; zval *value; time_t expiration = 0; char *payload; size_t payload_len; uint32_t flags = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dssz|l", &cas_d, &server_key, &server_key_len, &key, &key_len, &value, &expiration) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dsz|l", &cas_d, &key, &key_len, &value, &expiration) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } DVAL_TO_LVAL(cas_d, cas); if (m_obj->compression) { flags |= MEMC_VAL_COMPRESSED; } payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); if (payload == NULL) { i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } if (by_key) { status = memcached_cas_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags, cas); } else { status = memcached_cas(m_obj->memc, key, key_len, payload, payload_len, expiration, flags, cas); } efree(payload); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ]) Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */ PHP_METHOD(Memcached, cas) { php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ]) Sets the value for the given key on the server identified by the server_key, failing if the cas_token doesn't match the one in memcache */ PHP_METHOD(Memcached, casByKey) { php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ Memcached::delete(string key [, int time ]) Deletes the given key */ PHP_METHOD(Memcached, delete) { php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::deleteMulti(array keys [, int time ]) Deletes the given keys */ PHP_METHOD(Memcached, deleteMulti) { php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::deleteByKey(string server_key, string key [, int time ]) Deletes the given key from the server identified by the server key */ PHP_METHOD(Memcached, deleteByKey) { php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ Memcached::deleteMultiByKey(array keys [, int time ]) Deletes the given key from the server identified by the server key */ PHP_METHOD(Memcached, deleteMultiByKey) { php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ -- php_memc_delete_impl */ static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { char *key = NULL; int key_len = 0; char *server_key = NULL; int server_key_len = 0; time_t expiration = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &server_key, &server_key_len, &key, &key_len, &expiration) == FAILURE) { return; } } 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; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } 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) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ -- 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; 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; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|l", &entries, &expiration) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; 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); } if (Z_STRLEN_PP(entry) == 0) { continue; } if (!by_key) { server_key = Z_STRVAL_PP(entry); server_key_len = Z_STRLEN_PP(entry); } 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); } else { add_assoc_bool(return_value, Z_STRVAL_PP(entry), 1); } } return; } /* }}} */ /* {{{ -- 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; 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; } } else { if (zend_parse_parameters(n_args TSRMLS_CC, "ss|lll", &server_key, &server_key_len, &key, &key_len, &offset, &initial, &expiry) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } if (offset < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset has to be > 0"); RETURN_FALSE; } retry: if ((!by_key && n_args < 3) || (by_key && n_args < 4)) { if (by_key) { if (incr) { status = memcached_increment_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, &value); } else { status = memcached_decrement_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, &value); } } else { if (incr) { status = memcached_increment(m_obj->memc, key, key_len, (unsigned int)offset, &value); } else { status = memcached_decrement(m_obj->memc, key, key_len, (unsigned int)offset, &value); } } } else { 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); } 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); } } else { if (incr) { status = memcached_increment_with_initial(m_obj->memc, key, key_len, (unsigned int)offset, initial, expiry, &value); } else { status = memcached_decrement_with_initial(m_obj->memc, key, key_len, (unsigned int)offset, initial, expiry, &value); } } } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { PHP_MEMC_FAILOVER_RETRY RETURN_FALSE; } RETURN_LONG((long)value); } /* }}} */ /* {{{ Memcached::increment(string key [, int delta [, initial_value [, expiry time ] ] ]) Increments the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, increment) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1); } /* }}} */ /* {{{ Memcached::decrement(string key [, int delta [, initial_value [, expiry time ] ] ]) Decrements the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, decrement) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); } /* }}} */ /* {{{ Memcached::decrementByKey(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ]) Decrements by server the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, decrementByKey) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0); } /* }}} */ /* {{{ Memcached::increment(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ]) Increments by server the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, incrementByKey) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1); } /* }}} */ /* {{{ Memcached::addServer(string hostname, int port [, int weight ]) Adds the given memcache server to the list */ PHP_METHOD(Memcached, addServer) { char *host; int host_len; 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; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; 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); } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::addServers(array servers) Adds the given memcache servers to the server list */ PHP_METHOD(Memcached, addServers) { zval *servers; zval **entry; zval **z_host, **z_port, **z_weight = NULL; uint32_t weight = 0; 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; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(servers)), i = 0; zend_hash_get_current_data(Z_ARRVAL_P(servers), (void **)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(servers)), i++) { 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); continue; } entry_size = zend_hash_num_elements(Z_ARRVAL_PP(entry)); if (entry_size > 1) { zend_hash_internal_pointer_reset(Z_ARRVAL_PP(entry)); /* 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); 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); continue; } convert_to_string_ex(z_host); convert_to_long_ex(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); } convert_to_long_ex(z_weight); weight = Z_LVAL_PP(z_weight); } list = memcached_server_list_append_with_weight(list, Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port), weight, &status); if (php_memc_handle_error(i_obj, status TSRMLS_CC) == 0) { continue; } } /* catch-all for all errors */ php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not add entry #%d to the server list", i+1); } status = memcached_server_push(m_obj->memc, list); memcached_server_list_free(list); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ 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; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; callbacks[0] = php_memc_do_serverlist_callback; array_init(return_value); context.return_value = return_value; memcached_server_cursor(m_obj->memc, callbacks, &context, 1); } /* }}} */ /* {{{ Memcached::getServerByKey(string server_key) Returns the server identified by the given server key */ PHP_METHOD(Memcached, getServerByKey) { char *server_key; int server_key_len; memcached_server_st *server; memcached_return error; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &server_key, &server_key_len) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (server_key_len == 0) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } server = memcached_server_by_key(m_obj->memc, server_key, server_key_len, &error); if (server == NULL) { php_memc_handle_error(i_obj, error TSRMLS_CC); RETURN_FALSE; } array_init(return_value); add_assoc_string(return_value, "host", server->hostname, 1); add_assoc_long(return_value, "port", server->port); add_assoc_long(return_value, "weight", server->weight); } /* }}} */ /* {{{ Memcached::resetServerList() Reset the server list in use */ PHP_METHOD(Memcached, resetServerList) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; memcached_servers_reset(m_obj->memc); RETURN_TRUE; } /* }}} */ /* {{{ Memcached::quit() Close any open connections */ PHP_METHOD(Memcached, quit) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; memcached_quit(m_obj->memc); RETURN_TRUE; } /* }}} */ /* {{{ Memcached::getStats() Returns statistics for the memcache servers */ PHP_METHOD(Memcached, getStats) { memcached_stat_st *stats; memcached_return status; struct callbackContext context = {0}; memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; if (memcached_server_count(m_obj->memc) == 0) { array_init(return_value); return; } stats = memcached_stat(m_obj->memc, NULL, &status); php_memc_handle_error(i_obj, status TSRMLS_CC); if (stats == NULL) { RETURN_FALSE; } else if (status != MEMCACHED_SUCCESS && status != MEMCACHED_SOME_ERRORS) { memcached_stat_free(m_obj->memc, stats); RETURN_FALSE; } array_init(return_value); callbacks[0] = php_memc_do_stats_callback; context.i = 0; context.stats = stats; context.return_value = return_value; memcached_server_cursor(m_obj->memc, callbacks, &context, 1); memcached_stat_free(m_obj->memc, stats); } /* }}} */ /* {{{ Memcached::getVersion() Returns the version of each memcached server in the pool */ PHP_METHOD(Memcached, getVersion) { memcached_return status = MEMCACHED_SUCCESS; struct callbackContext context = {0}; memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } 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); RETURN_FALSE; } callbacks[0] = php_memc_do_version_callback; context.return_value = return_value; memcached_server_cursor(m_obj->memc, callbacks, &context, 1); } /* }}} */ /* {{{ 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) { zval *ctx = (zval*) context; add_next_index_string(ctx, (char*) key, 1); return MEMCACHED_SUCCESS; } PHP_METHOD(Memcached, getAllKeys) { memcached_return rc; memcached_dump_func callback[1]; MEMC_METHOD_INIT_VARS; callback[0] = php_memc_dump_func_callback; 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) { zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ Memcached::flush([ int delay ]) Flushes the data on all the servers */ static PHP_METHOD(Memcached, flush) { time_t delay = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &delay) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; status = memcached_flush(m_obj->memc, delay); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::getOption(int option) Returns the value for the given option constant */ static PHP_METHOD(Memcached, getOption) { 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; } MEMC_METHOD_FETCH_OBJECT; switch (option) { case MEMC_OPT_COMPRESSION_TYPE: RETURN_LONG(m_obj->compression_type); case MEMC_OPT_COMPRESSION: RETURN_BOOL(m_obj->compression); case MEMC_OPT_PREFIX_KEY: { memcached_return retval; char *result; result = memcached_callback_get(m_obj->memc, MEMCACHED_CALLBACK_PREFIX_KEY, &retval); if (retval == MEMCACHED_SUCCESS && result) { #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x00050000 RETURN_STRINGL(result, strlen(result) - 1, 1); #else RETURN_STRING(result, 1); #endif } else { RETURN_EMPTY_STRING(); } } case MEMC_OPT_SERIALIZER: RETURN_LONG((long)m_obj->serializer); 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"); return; } default: /* * Assume that it's a libmemcached behavior option. */ flag = (memcached_behavior) option; result = memcached_behavior_get(m_obj->memc, flag); RETURN_LONG((long)result); } } /* }}} */ static int php_memc_set_option(php_memc_t *i_obj, long option, zval *value TSRMLS_DC) { memcached_behavior flag; struct memc_obj *m_obj = i_obj->obj; switch (option) { case MEMC_OPT_COMPRESSION: convert_to_long(value); m_obj->compression = Z_LVAL_P(value) ? 1 : 0; break; case MEMC_OPT_COMPRESSION_TYPE: convert_to_long(value); if (Z_LVAL_P(value) == COMPRESSION_TYPE_FASTLZ || Z_LVAL_P(value) == COMPRESSION_TYPE_ZLIB) { m_obj->compression_type = Z_LVAL_P(value); } else { /* invalid compression type */ return 0; } break; case MEMC_OPT_PREFIX_KEY: { char *key; #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x00050000 char tmp[MEMCACHED_PREFIX_KEY_MAX_SIZE - 1]; #endif convert_to_string(value); if (Z_STRLEN_P(value) == 0) { key = NULL; } else { /* work-around a bug in libmemcached prior to version 0.50 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 < 0x00050000 snprintf(tmp, sizeof(tmp), "%s0", Z_STRVAL_P(value)); key = tmp; #else key = Z_STRVAL_P(value); #endif } if (memcached_callback_set(m_obj->memc, MEMCACHED_CALLBACK_PREFIX_KEY, key) == MEMCACHED_BAD_KEY_PROVIDED) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "bad key provided"); return 0; } break; } case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: flag = (memcached_behavior) option; convert_to_long(value); if (memcached_behavior_set(m_obj->memc, flag, (uint64_t)Z_LVAL_P(value)) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "error setting memcached option"); return 0; } /* * This is necessary because libmemcached does not reset hash/distribution * options on false case, like it does for MEMCACHED_BEHAVIOR_KETAMA * (non-weighted) case. We have to clean up ourselves. */ if (!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 } break; case MEMC_OPT_SERIALIZER: { convert_to_long(value); /* igbinary serializer */ #ifdef HAVE_MEMCACHED_IGBINARY if (Z_LVAL_P(value) == SERIALIZER_IGBINARY) { m_obj->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; } else #endif /* php serializer */ if (Z_LVAL_P(value) == SERIALIZER_PHP) { m_obj->serializer = SERIALIZER_PHP; } else { m_obj->serializer = SERIALIZER_PHP; php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid serializer provided"); return 0; } break; } default: /* * Assume that it's a libmemcached behavior option. */ flag = (memcached_behavior) option; convert_to_long(value); if (flag < 0 || /* MEMCACHED_BEHAVIOR_MAX was added in somewhere around 0.36 or 0.37 */ #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00037000 flag >= MEMCACHED_BEHAVIOR_MAX || #endif memcached_behavior_set(m_obj->memc, flag, (uint64_t)Z_LVAL_P(value)) != MEMCACHED_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "error setting memcached option"); return 0; } break; } return 1; } /* {{{ Memcached::setOptions(array options) Sets the value for the given option constant */ static PHP_METHOD(Memcached, setOptions) { zval *options; zend_bool ok = 1; uint key_len; char *key; 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; for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(options)); zend_hash_get_current_data(Z_ARRVAL_P(options), (void *) &value) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(options))) { if (zend_hash_get_current_key_ex(Z_ARRVAL_P(options), &key, &key_len, &key_index, 0, NULL) == HASH_KEY_IS_LONG) { zval copy = **value; zval_copy_ctor(©); INIT_PZVAL(©); if (!php_memc_set_option(i_obj, (long) key_index, © TSRMLS_CC)) { ok = 0; } zval_dtor(©); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid configuration option"); ok = 0; } } RETURN_BOOL(ok); } /* }}} */ /* {{{ Memcached::setOption(int option, mixed value) Sets the value for the given option constant */ static PHP_METHOD(Memcached, setOption) { long option; zval *value; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz/", &option, &value) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_BOOL(php_memc_set_option(i_obj, option, value TSRMLS_CC)); } /* }}} */ #ifdef HAVE_MEMCACHED_SASL /* {{{ Memcached::setSaslAuthData(string user, string pass) Sets sasl credentials */ static PHP_METHOD(Memcached, setSaslAuthData) { MEMC_METHOD_INIT_VARS; char *user, *pass; int user_len, pass_len, rc; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &user, &user_len, &pass, &pass_len) == FAILURE) { return; } 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"); RETURN_FALSE; } RETURN_BOOL(memcached_set_sasl_auth_data(m_obj->memc, user, pass)); } /* }}} */ #endif /* HAVE_MEMCACHED_SASL */ /* {{{ Memcached::getResultCode() Returns the result code from the last operation */ static PHP_METHOD(Memcached, getResultCode) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_LONG(i_obj->rescode); } /* }}} */ /* {{{ Memcached::getResultMessage() Returns the result message from the last operation */ static PHP_METHOD(Memcached, getResultMessage) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; switch (i_obj->rescode) { case MEMC_RES_PAYLOAD_FAILURE: RETURN_STRING("PAYLOAD FAILURE", 1); 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); } /* Fall through */ default: RETURN_STRING(memcached_strerror(m_obj->memc, (memcached_return)i_obj->rescode), 1); break; } } /* }}} */ /* {{{ Memcached::isPersistent() Returns the true if instance uses a persistent connection */ static PHP_METHOD(Memcached, isPersistent) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_BOOL(i_obj->is_persistent); } /* }}} */ /* {{{ Memcached::isPristine() Returns the true if instance is recently created */ static PHP_METHOD(Memcached, isPristine) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_BOOL(i_obj->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) { zend_object_std_dtor(&i_obj->zo TSRMLS_CC); if (i_obj->obj && !i_obj->is_persistent) { php_memc_destroy(i_obj->obj, 0 TSRMLS_CC); } i_obj->obj = NULL; efree(i_obj); } zend_object_value php_memc_new(zend_class_entry *ce TSRMLS_DC) { zend_object_value retval; php_memc_t *i_obj; zval *tmp; 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 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; } 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) { if (rsrc->ptr) { memcached_sess *memc_sess = (memcached_sess *)rsrc->ptr; memcached_free(memc_sess->memc_sess); pefree(rsrc->ptr, 1); rsrc->ptr = NULL; } } /* }}} */ /* {{{ internal API functions */ static memcached_return php_memc_do_serverlist_callback(const memcached_st *ptr, memcached_server_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", 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, memcached_server_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, memcached_server_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)); version_len = snprintf(version, sizeof(version), "%d.%d.%d", memcached_server_major_version(instance), memcached_server_minor_version(instance), memcached_server_micro_version(instance)); 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; } 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) { char *payload; char *p; int l; zend_bool buf_used = 0; smart_str buf = {0}; char tmp[40] = {0}; switch (Z_TYPE_P(value)) { case IS_STRING: p = Z_STRVAL_P(value); l = Z_STRLEN_P(value); MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_STRING); break; case IS_LONG: l = sprintf(tmp, "%ld", Z_LVAL_P(value)); p = tmp; MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_LONG); break; case IS_DOUBLE: php_memcached_g_fmt(tmp, Z_DVAL_P(value)); p = tmp; l = strlen(tmp); MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_DOUBLE); break; case IS_BOOL: if (Z_BVAL_P(value)) { l = 1; tmp[0] = '1'; tmp[1] = '\0'; } else { l = 0; tmp[0] = '\0'; } p = tmp; MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL); break; default: switch (serializer) { #ifdef HAVE_MEMCACHED_IGBINARY case SERIALIZER_IGBINARY: if (igbinary_serialize((uint8_t **) &buf.c, &buf.len, value TSRMLS_CC) != 0) { smart_str_free(&buf); return NULL; } p = buf.c; l = buf.len; buf_used = 1; MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_IGBINARY); break; #endif #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 buf.c[buf.len] = 0; p = buf.c; l = buf.len; buf_used = 1; MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_JSON); break; } #endif 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"); smart_str_free(&buf); return NULL; } p = buf.c; l = buf.len; buf_used = 1; MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_SERIALIZED); break; } } break; } /* Check for exceptions caused by serializers */ if (EG(exception) && buf_used) { smart_str_free(&buf); return NULL; } /* turn off compression for values below the threshold */ if ((*flags & MEMC_VAL_COMPRESSED) && l < MEMC_G(compression_threshold)) { *flags &= ~MEMC_VAL_COMPRESSED; } if (*flags & MEMC_VAL_COMPRESSED) { /* status */ zend_bool compress_status = 0; /* Additional 5% for the data */ unsigned long payload_comp_len = (unsigned long)((l * 1.05) + 1); char *payload_comp = emalloc(payload_comp_len + sizeof(uint32_t)); payload = payload_comp; memcpy(payload_comp, &l, sizeof(uint32_t)); payload_comp += sizeof(uint32_t); if (compression_type == COMPRESSION_TYPE_FASTLZ) { compress_status = ((payload_comp_len = fastlz_compress(p, l, payload_comp)) > 0); *flags |= MEMC_VAL_COMPRESSION_FASTLZ; } else if (compression_type == COMPRESSION_TYPE_ZLIB) { compress_status = (compress((Bytef *)payload_comp, &payload_comp_len, (Bytef *)p, l) == Z_OK); *flags |= MEMC_VAL_COMPRESSION_ZLIB; } if (!compress_status) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not compress value"); efree(payload); if (buf_used) { smart_str_free(&buf); } return NULL; } /* Check that we are above ratio */ if (l > (payload_comp_len * MEMC_G(compression_factor))) { *payload_len = payload_comp_len + sizeof(uint32_t); payload[*payload_len] = 0; } else { /* Store plain value */ *flags &= ~MEMC_VAL_COMPRESSED; *payload_len = l; memcpy(payload, p, l); payload[l] = 0; } } else { *payload_len = l; payload = estrndup(p, l); } if (buf_used) { smart_str_free(&buf); } return payload; } /* The caller MUST free the payload */ static int php_memc_zval_from_payload(zval *value, char *payload, size_t payload_len, uint32_t flags, enum memcached_serializer serializer TSRMLS_DC) { /* A NULL payload is completely valid if length is 0, it is simply empty. */ zend_bool payload_emalloc = 0; char *buffer = NULL; if (payload == NULL && payload_len > 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not handle non-existing value of length %zu", payload_len); return -1; } else if (payload == NULL) { if (MEMC_VAL_GET_TYPE(flags) == MEMC_VAL_IS_BOOL) { ZVAL_FALSE(value); } else { ZVAL_EMPTY_STRING(value); } return 0; } if (flags & MEMC_VAL_COMPRESSED) { uint32_t len; unsigned long length; zend_bool decompress_status = 0; /* Stored with newer memcached extension? */ if (flags & MEMC_VAL_COMPRESSION_FASTLZ || 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 (flags & MEMC_VAL_COMPRESSION_FASTLZ) { decompress_status = ((length = fastlz_decompress(payload, payload_len, buffer, len)) > 0); } else if (flags & MEMC_VAL_COMPRESSION_ZLIB) { decompress_status = (uncompress((Bytef *)buffer, &length, (Bytef *)payload, payload_len) == Z_OK); } } /* 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; } } if (!decompress_status) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not decompress value"); efree(buffer); return -1; } payload = buffer; payload_len = length; payload_emalloc = 1; } payload[payload_len] = 0; switch (MEMC_VAL_GET_TYPE(flags)) { case MEMC_VAL_IS_STRING: if (payload_emalloc) { ZVAL_STRINGL(value, payload, payload_len, 0); payload_emalloc = 0; } else { ZVAL_STRINGL(value, payload, payload_len, 1); } break; case MEMC_VAL_IS_LONG: { long lval = strtol(payload, NULL, 10); ZVAL_LONG(value, lval); break; } case MEMC_VAL_IS_DOUBLE: if (payload_len == 8 && memcmp(payload, "Infinity", 8) == 0) { ZVAL_DOUBLE(value, php_get_inf()); } else if (payload_len == 9 && memcmp(payload, "-Infinity", 9) == 0) { ZVAL_DOUBLE(value, -php_get_inf()); } else if (payload_len == 3 && memcmp(payload, "NaN", 3) == 0) { ZVAL_DOUBLE(value, php_get_nan()); } else { ZVAL_DOUBLE(value, zend_strtod(payload, NULL)); } break; case MEMC_VAL_IS_BOOL: ZVAL_BOOL(value, payload_len > 0 && payload[0] == '1'); break; case MEMC_VAL_IS_SERIALIZED: { const char *payload_tmp = payload; php_unserialize_data_t var_hash; 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); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value"); goto my_error; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); break; } 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"); goto my_error; } #else php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no igbinary support"); goto my_error; #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 #else php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no json support"); goto my_error; #endif break; default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "unknown payload type"); goto my_error; } if (payload_emalloc) { efree(payload); } return 0; my_error: if (payload_emalloc) { efree(payload); } return -1; } static void php_memc_init_globals(zend_php_memcached_globals *php_memcached_globals_p TSRMLS_DC) { #ifdef HAVE_MEMCACHED_SESSION MEMC_G(sess_locking_enabled) = 1; MEMC_G(sess_binary_enabled) = 1; MEMC_G(sess_prefix) = NULL; MEMC_G(sess_lock_wait) = 0; MEMC_G(sess_locked) = 0; MEMC_G(sess_lock_key) = NULL; MEMC_G(sess_lock_key_len) = 0; #endif MEMC_G(serializer_name) = NULL; MEMC_G(serializer) = SERIALIZER_DEFAULT; MEMC_G(compression_type) = NULL; MEMC_G(compression_type_real) = COMPRESSION_TYPE_FASTLZ; MEMC_G(compression_factor) = 1.30; #if HAVE_MEMCACHED_SASL MEMC_G(use_sasl) = 0; #endif } static void php_memc_destroy_globals(zend_php_memcached_globals *php_memcached_globals_p TSRMLS_DC) { } PHP_MEMCACHED_API zend_class_entry *php_memc_get_ce(void) { return memcached_ce; } PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception(void) { return memcached_exception_ce; } PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception_base(int root TSRMLS_DC) { #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; } } else { return spl_ce_RuntimeException; } } #endif #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) return zend_exception_get_default(); #else return zend_exception_get_default(TSRMLS_C); #endif } static memcached_return php_memc_do_cache_callback(zval *zmemc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, char *key, size_t key_len, zval *value TSRMLS_DC) { char *payload = NULL; size_t payload_len = 0; zval **params[4]; zval *retval; zval *z_key; zval *z_expiration; uint32_t flags = 0; memcached_return rc; php_memc_t* i_obj; memcached_return status = MEMCACHED_SUCCESS; int result; MAKE_STD_ZVAL(z_key); MAKE_STD_ZVAL(z_expiration); ZVAL_STRINGL(z_key, key, key_len, 1); ZVAL_NULL(value); ZVAL_LONG(z_expiration, 0); params[0] = &zmemc_obj; params[1] = &z_key; params[2] = &value; params[3] = &z_expiration; fci->retval_ptr_ptr = &retval; fci->params = params; fci->param_count = sizeof(params) / sizeof(params[0]); result = zend_call_function(fci, fcc TSRMLS_CC); if (result == SUCCESS && retval) { i_obj = (php_memc_t *) zend_object_store_get_object(zmemc_obj TSRMLS_CC); struct memc_obj *m_obj = i_obj->obj; if (zend_is_true(retval)) { time_t expiration; if (Z_TYPE_P(z_expiration) != IS_LONG) { convert_to_long(z_expiration); } expiration = Z_LVAL_P(z_expiration); payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); if (payload == NULL) { status = (memcached_return)MEMC_RES_PAYLOAD_FAILURE; } else { rc = memcached_set(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) { status = rc; } efree(payload); } } else { status = MEMCACHED_NOTFOUND; zval_dtor(value); ZVAL_NULL(value); } } else { if (result == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not invoke cache callback"); } status = MEMCACHED_FAILURE; zval_dtor(value); ZVAL_NULL(value); } if (retval) { zval_ptr_dtor(&retval); } zval_ptr_dtor(&z_key); zval_ptr_dtor(&z_expiration); return status; } static int php_memc_do_result_callback(zval *zmemc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, memcached_result_st *result TSRMLS_DC) { char *res_key = NULL; size_t res_key_len = 0; 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); MAKE_STD_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 (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; } /* }}} */ /* {{{ 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(1, cas_token) 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(1, cas_token) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getMulti, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(1, cas_tokens) ZEND_ARG_INFO(0, 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(1, cas_tokens) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayed, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(0, with_cas) ZEND_ARG_INFO(0, value_cb) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayedByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(0, with_cas) ZEND_ARG_INFO(0, value_cb) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_fetch, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_fetchAll, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_touch, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_touchByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setMulti, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, items, 0) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setMultiByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_ARRAY_INFO(0, items, 0) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_add, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_addByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_replace, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_replaceByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_append, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_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_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4) ZEND_ARG_INFO(0, cas_token) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_delete, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMulti, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMultiByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_increment, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, initial_value) ZEND_ARG_INFO(0, expiry) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_decrement, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, initial_value) ZEND_ARG_INFO(0, expiry) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_incrementByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, initial_value) ZEND_ARG_INFO(0, expiry) ZEND_END_ARG_INFO() 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_getServerByKey, 0) ZEND_ARG_INFO(0, server_key) 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_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) #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000002 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(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) #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 /* }}} */ /* {{{ memcached_module_entry */ #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep memcached_deps[] = { #ifdef HAVE_MEMCACHED_SESSION ZEND_MOD_REQUIRED("session") #endif #ifdef HAVE_MEMCACHED_IGBINARY ZEND_MOD_REQUIRED("igbinary") #endif #ifdef HAVE_SPL ZEND_MOD_REQUIRED("spl") #endif {NULL, NULL, NULL} }; #endif zend_module_entry memcached_module_entry = { #if ZEND_MODULE_API_NO >= 20050922 STANDARD_MODULE_HEADER_EX, NULL, (zend_module_dep*)memcached_deps, #else STANDARD_MODULE_HEADER, #endif "memcached", NULL, PHP_MINIT(memcached), PHP_MSHUTDOWN(memcached), #if HAVE_MEMCACHED_SASL PHP_RINIT(memcached), PHP_RSHUTDOWN(memcached), #else NULL, NULL, #endif PHP_MINFO(memcached), PHP_MEMCACHED_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ /* {{{ 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) /* * Class options */ 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_PREFIX_KEY, MEMC_OPT_PREFIX_KEY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERIALIZER, MEMC_OPT_SERIALIZER); /* * Indicate whether igbinary serializer is available */ #ifdef HAVE_MEMCACHED_IGBINARY REGISTER_MEMC_CLASS_CONST_LONG(HAVE_IGBINARY, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_IGBINARY, 0); #endif /* * Indicate whether json serializer is available */ #ifdef HAVE_JSON_API REGISTER_MEMC_CLASS_CONST_LONG(HAVE_JSON, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_JSON, 0); #endif #ifdef HAVE_MEMCACHED_SESSION REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SESSION, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SESSION, 0); #endif #ifdef HAVE_MEMCACHED_SASL REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SASL, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SASL, 0); #endif /* * libmemcached behavior options */ REGISTER_MEMC_CLASS_CONST_LONG(OPT_HASH, MEMCACHED_BEHAVIOR_HASH); REGISTER_MEMC_CLASS_CONST_LONG(HASH_DEFAULT, MEMCACHED_HASH_DEFAULT); REGISTER_MEMC_CLASS_CONST_LONG(HASH_MD5, MEMCACHED_HASH_MD5); REGISTER_MEMC_CLASS_CONST_LONG(HASH_CRC, MEMCACHED_HASH_CRC); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1_64, MEMCACHED_HASH_FNV1_64); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1A_64, MEMCACHED_HASH_FNV1A_64); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1_32, MEMCACHED_HASH_FNV1_32); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1A_32, MEMCACHED_HASH_FNV1A_32); REGISTER_MEMC_CLASS_CONST_LONG(HASH_HSIEH, MEMCACHED_HASH_HSIEH); REGISTER_MEMC_CLASS_CONST_LONG(HASH_MURMUR, MEMCACHED_HASH_MURMUR); 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); REGISTER_MEMC_CLASS_CONST_LONG(OPT_BUFFER_WRITES, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_BINARY_PROTOCOL, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL); REGISTER_MEMC_CLASS_CONST_LONG(OPT_NO_BLOCK, MEMCACHED_BEHAVIOR_NO_BLOCK); REGISTER_MEMC_CLASS_CONST_LONG(OPT_TCP_NODELAY, MEMCACHED_BEHAVIOR_TCP_NODELAY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SOCKET_SEND_SIZE, MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SOCKET_RECV_SIZE, MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE); REGISTER_MEMC_CLASS_CONST_LONG(OPT_CONNECT_TIMEOUT, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_RETRY_TIMEOUT, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SEND_TIMEOUT, MEMCACHED_BEHAVIOR_SND_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_RECV_TIMEOUT, MEMCACHED_BEHAVIOR_RCV_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_POLL_TIMEOUT, MEMCACHED_BEHAVIOR_POLL_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_CACHE_LOOKUPS, MEMCACHED_BEHAVIOR_CACHE_LOOKUPS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERVER_FAILURE_LIMIT, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_AUTO_EJECT_HOSTS, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_HASH_WITH_PREFIX_KEY, MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_NOREPLY, MEMCACHED_BEHAVIOR_NOREPLY); 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 #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 REGISTER_MEMC_CLASS_CONST_LONG(OPT_REMOVE_FAILED_SERVERS, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS); #endif /* * 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_MEMORY_ALLOCATION_FAILURE, MEMCACHED_MEMORY_ALLOCATION_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_SOCKET_CREATE_FAILURE, MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE); #if 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); #endif /* * Our result codes. */ REGISTER_MEMC_CLASS_CONST_LONG(RES_PAYLOAD_FAILURE, MEMC_RES_PAYLOAD_FAILURE); /* * 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_JSON_ARRAY, SERIALIZER_JSON_ARRAY); /* * Compression types */ REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_FASTLZ, COMPRESSION_TYPE_FASTLZ); REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_ZLIB, COMPRESSION_TYPE_ZLIB); /* * Flags. */ REGISTER_MEMC_CLASS_CONST_LONG(GET_PRESERVE_ORDER, MEMC_GET_PRESERVE_ORDER); #undef REGISTER_MEMC_CLASS_CONST_LONG /* * Return value from simple get errors */ REGISTER_MEMC_CLASS_CONST_BOOL(GET_ERROR_RETURN_VALUE, 0); } /* }}} */ #if HAVE_MEMCACHED_SASL PHP_RINIT_FUNCTION(memcached) { 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 SUCCESS; } PHP_RSHUTDOWN_FUNCTION(memcached) { if (MEMC_G(use_sasl)) { sasl_done(); } return SUCCESS; } #endif int php_memc_sess_list_entry(void) { return le_memc_sess; } /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(memcached) { zend_class_entry ce; memcpy(&memcached_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); memcached_object_handlers.clone_obj = NULL; 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; 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); /* TODO * possibly declare custom exception property here */ php_memc_register_constants(INIT_FUNC_ARGS_PASSTHRU); #ifdef ZTS ts_allocate_id(&php_memcached_globals_id, sizeof(zend_php_memcached_globals), (ts_allocate_ctor) php_memc_init_globals, (ts_allocate_dtor) php_memc_destroy_globals); #else php_memc_init_globals(&php_memcached_globals TSRMLS_CC); #endif #ifdef HAVE_MEMCACHED_SESSION php_session_register_module(ps_memcached_ptr); #endif REGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(memcached) { #ifdef ZTS ts_free_id(php_memcached_globals_id); #else php_memc_destroy_globals(&php_memcached_globals TSRMLS_CC); #endif UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ 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()); #ifdef HAVE_MEMCACHED_SESSION php_info_print_table_row(2, "Session support", "yes"); #else php_info_print_table_row(2, "Session support ", "no"); #endif #ifdef HAVE_MEMCACHED_IGBINARY php_info_print_table_row(2, "igbinary support", "yes"); #else php_info_print_table_row(2, "igbinary support", "no"); #endif #ifdef HAVE_JSON_API php_info_print_table_row(2, "json support", "yes"); #else php_info_print_table_row(2, "json support", "no"); #endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim: noet sw=4 ts=4 fdm=marker: */ memcached-2.1.0/php_memcached.h0000644000076700000240000000642212010120566015472 0ustar andreistaff/* +----------------------------------------------------------------------+ | Copyright (c) 2009 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski | +----------------------------------------------------------------------+ */ /* $ Id: $ */ #ifndef PHP_MEMCACHED_H #define PHP_MEMCACHED_H #include "php_libmemcached_compat.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif extern zend_module_entry memcached_module_entry; #define phpext_memcached_ptr &memcached_module_entry #ifdef PHP_WIN32 #define PHP_MEMCACHED_API __declspec(dllexport) #else #define PHP_MEMCACHED_API #endif /**************************************** Structures and definitions ****************************************/ enum memcached_serializer { SERIALIZER_PHP = 1, SERIALIZER_IGBINARY = 2, SERIALIZER_JSON = 3, SERIALIZER_JSON_ARRAY = 4, }; #ifdef HAVE_MEMCACHED_IGBINARY #define SERIALIZER_DEFAULT SERIALIZER_IGBINARY #define SERIALIZER_DEFAULT_NAME "igbinary" #else #define SERIALIZER_DEFAULT SERIALIZER_PHP #define SERIALIZER_DEFAULT_NAME "php" #endif /* HAVE_MEMCACHED_IGBINARY */ #if LIBMEMCACHED_WITH_SASL_SUPPORT # if defined(HAVE_SASL_SASL_H) # include # define HAVE_MEMCACHED_SASL 1 # endif #endif ZEND_BEGIN_MODULE_GLOBALS(php_memcached) #ifdef HAVE_MEMCACHED_SESSION zend_bool sess_locking_enabled; long sess_lock_wait; char* sess_prefix; zend_bool sess_locked; char* sess_lock_key; int sess_lock_key_len; #endif char *serializer_name; enum memcached_serializer serializer; char *compression_type; int compression_type_real; int compression_threshold; double compression_factor; #if HAVE_MEMCACHED_SASL bool use_sasl; #endif zend_bool sess_binary_enabled; ZEND_END_MODULE_GLOBALS(php_memcached) 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_RINIT_FUNCTION(memcached); PHP_RSHUTDOWN_FUNCTION(memcached); PHP_MINIT_FUNCTION(memcached); PHP_MSHUTDOWN_FUNCTION(memcached); PHP_MINFO_FUNCTION(memcached); #define PHP_MEMCACHED_VERSION "2.1.0" #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_persisent; } memcached_sess; int php_memc_sess_list_entry(void); #endif /* PHP_MEMCACHED_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ memcached-2.1.0/php_memcached_session.c0000644000076700000240000002101312010120566017221 0ustar andreistaff/* +----------------------------------------------------------------------+ | Copyright (c) 2009-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski | +----------------------------------------------------------------------+ */ #include #include #include #include #ifdef ZTS #include "TSRM.h" #endif #include #include #include #include #include #include #include #include "php_memcached.h" #include "php_memcached_session.h" extern ZEND_DECLARE_MODULE_GLOBALS(php_memcached) #define MEMC_SESS_DEFAULT_LOCK_WAIT 150000 #define MEMC_SESS_LOCK_EXPIRATION 30 ps_module ps_mod_memcached = { PS_MOD(memcached) }; static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC) { char *lock_key = NULL; int lock_key_len = 0; unsigned long attempts; long lock_maxwait; long lock_wait = MEMC_G(sess_lock_wait); time_t expiration; memcached_return status; /* set max timeout for session_start = max_execution_time. (c) Andrei Darashenka, Richter & Poweleit GmbH */ lock_maxwait = zend_ini_long(ZEND_STRS("max_execution_time"), 0); if (lock_maxwait <= 0) { lock_maxwait = MEMC_SESS_LOCK_EXPIRATION; } if (lock_wait == 0) { lock_wait = MEMC_SESS_DEFAULT_LOCK_WAIT; } expiration = time(NULL) + lock_maxwait + 1; attempts = (unsigned long)((1000000.0 / lock_wait) * lock_maxwait); 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) { break; } if (lock_wait > 0) { usleep(lock_wait); } } while(--attempts > 0); efree(lock_key); return -1; } static void php_memc_sess_unlock(memcached_st *memc TSRMLS_DC) { 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; } } PS_OPEN_FUNC(memcached) { memcached_sess *memc_sess = PS_GET_MOD_DATA(); memcached_return status; char *p, *plist_key = NULL; int plist_key_len; if (!strncmp((char *)save_path, "PERSISTENT=", sizeof("PERSISTENT=") - 1)) { zend_rsrc_list_entry *le = NULL; char *e; 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; } } p = e + 1; memc_sess = pecalloc(sizeof(*memc_sess), 1, 1); memc_sess->is_persisent = 1; } else { p = (char *)save_path; memc_sess = ecalloc(sizeof(*memc_sess), 1); memc_sess->is_persisent = 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) { status = memcached_server_push(memc_sess->memc_sess, servers); memcached_server_list_free(servers); if (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; } 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"); } } else { memc_sess->memc_sess = memcached(p, strlen(p)); if (!memc_sess->memc_sess) { 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 { success: PS_SET_MOD_DATA(memc_sess); if (plist_key) { zend_rsrc_list_entry le; le.type = php_memc_sess_list_entry(); le.ptr = memc_sess; 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); } 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; } } return SUCCESS; } } if (plist_key) { efree(plist_key); } PS_SET_MOD_DATA(NULL); return FAILURE; } PS_CLOSE_FUNC(memcached) { memcached_sess *memc_sess = PS_GET_MOD_DATA(); if (MEMC_G(sess_locking_enabled)) { php_memc_sess_unlock(memc_sess->memc_sess TSRMLS_CC); } if (memc_sess->memc_sess) { if (!memc_sess->is_persisent) { memcached_free(memc_sess->memc_sess); efree(memc_sess); } PS_SET_MOD_DATA(NULL); } return SUCCESS; } 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; key_length = strlen(MEMC_G(sess_prefix)) + key_len + 5; // prefix + "lock." if (!key_length || key_length >= MEMCACHED_MAX_KEY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id is too long or contains illegal characters"); PS(invalid_session_id) = 1; return FAILURE; } if (MEMC_G(sess_locking_enabled)) { if (php_memc_sess_lock(memc_sess->memc_sess, key TSRMLS_CC) < 0) { return FAILURE; } } payload = memcached_get(memc_sess->memc_sess, key, key_len, &payload_len, &flags, &status); if (status == MEMCACHED_SUCCESS) { *val = estrndup(payload, payload_len); *vallen = payload_len; free(payload); return SUCCESS; } else { return FAILURE; } } PS_WRITE_FUNC(memcached) { int key_len = strlen(key); time_t expiration = 0; memcached_return status; memcached_sess *memc_sess = PS_GET_MOD_DATA(); size_t key_length; key_length = strlen(MEMC_G(sess_prefix)) + key_len + 5; // prefix + "lock." if (!key_length || key_length >= MEMCACHED_MAX_KEY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id is too long or contains illegal characters"); PS(invalid_session_id) = 1; return FAILURE; } if (PS(gc_maxlifetime) > 0) { expiration = PS(gc_maxlifetime); } status = memcached_set(memc_sess->memc_sess, key, key_len, val, vallen, expiration, 0); if (status == MEMCACHED_SUCCESS) { return SUCCESS; } else { return FAILURE; } } PS_DESTROY_FUNC(memcached) { memcached_sess *memc_sess = 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); } return SUCCESS; } PS_GC_FUNC(memcached) { return SUCCESS; } /* }}} */ memcached-2.1.0/php_memcached_session.h0000644000076700000240000000257712010120566017244 0ustar andreistaff/* +----------------------------------------------------------------------+ | Copyright (c) 2009-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski | +----------------------------------------------------------------------+ */ #ifndef PHP_MEMCACHED_SESSION_H #define PHP_MEMCACHED_SESSION_H /* session handler struct */ #include "ext/session/php_session.h" extern ps_module ps_mod_memcached; #define ps_memcached_ptr &ps_mod_memcached PS_FUNCS(memcached); PS_OPEN_FUNC(memcached); PS_CLOSE_FUNC(memcached); PS_READ_FUNC(memcached); PS_WRITE_FUNC(memcached); PS_DESTROY_FUNC(memcached); PS_GC_FUNC(memcached); #endif /* PHP_MEMCACHED_SESSION_H */ memcached-2.1.0/php_libmemcached_compat.h0000644000076700000240000000023112010120566017514 0ustar andreistaff#ifndef PHP_LIBMEMCACHED_COMPAT #define PHP_LIBMEMCACHED_COMPAT /* this is the version(s) we support */ #include #endif memcached-2.1.0/g_fmt.c0000644000076700000240000000432212010120566014001 0ustar andreistaff/**************************************************************** * * The author of this software is David M. Gay. * * Copyright (c) 1991, 1996 by Lucent Technologies. * * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. * ***************************************************************/ /* g_fmt(buf,x) stores the closest decimal approximation to x in buf; * it suffices to declare buf * char buf[32]; */ /* Modified for use with php in the memcached client extension. * * // Teddy Grenman , 2010-05-18. */ #include char *php_memcached_g_fmt(register char *b, double x) { register int i, k; register char *s; int decpt, j, sign; char *b0, *s0, *se; b0 = b; #ifdef IGNORE_ZERO_SIGN if (!x) { *b++ = '0'; *b = 0; goto done; } #endif s = s0 = zend_dtoa(x, 0, 0, &decpt, &sign, &se); if (sign) *b++ = '-'; if (decpt == 9999) /* Infinity or Nan */ { while(*b++ = *s++); goto done0; } if (decpt <= -4 || decpt > se - s + 5) { *b++ = *s++; if (*s) { *b++ = '.'; while(*b = *s++) b++; } *b++ = 'e'; /* sprintf(b, "%+.2d", decpt - 1); */ if (--decpt < 0) { *b++ = '-'; decpt = -decpt; } else *b++ = '+'; for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); for(;;) { i = decpt / k; *b++ = i + '0'; if (--j <= 0) break; decpt -= i*k; decpt *= 10; } *b = 0; } else if (decpt <= 0) { *b++ = '.'; for(; decpt < 0; decpt++) *b++ = '0'; while(*b++ = *s++); } else { while(*b = *s++) { b++; if (--decpt == 0 && *s) *b++ = '.'; } for(; decpt > 0; decpt--) *b++ = '0'; *b = 0; } done0: zend_freedtoa(s0); done: return b0; } memcached-2.1.0/g_fmt.h0000644000076700000240000000223512010120566014007 0ustar andreistaff/**************************************************************** * * The author of this software is David M. Gay. * * Copyright (c) 1991, 1996 by Lucent Technologies. * * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. * ***************************************************************/ /* g_fmt(buf,x) stores the closest decimal approximation to x in buf; * it suffices to declare buf * char buf[32]; */ /* Modified for use with php in the memcached client * extension by Teddy Grenman, 2010. */ #ifndef MEMC_G_FMT_H #define MEMC_G_FMT_H char *php_memcached_g_fmt(register char *b, double x); #endif memcached-2.1.0/fastlz/fastlz.c0000644000076700000240000003252012010120566015514 0ustar andreistaff/* FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) /* * Always check for bound when decompressing. * Generally it is best to leave it defined. */ #define FASTLZ_SAFE /* * Give hints to the compiler for branch prediction optimization. */ #if defined(__GNUC__) && (__GNUC__ > 2) #define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) #define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) #else #define FASTLZ_EXPECT_CONDITIONAL(c) (c) #define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) #endif /* * Use inlined functions for supported systems. */ #if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) #define FASTLZ_INLINE inline #elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) #define FASTLZ_INLINE __inline #else #define FASTLZ_INLINE #endif /* * Prevent accessing more than 8-bit at once, except on x86 architectures. */ #if !defined(FASTLZ_STRICT_ALIGN) #define FASTLZ_STRICT_ALIGN #if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ #undef FASTLZ_STRICT_ALIGN #elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ #undef FASTLZ_STRICT_ALIGN #elif defined(_M_IX86) /* Intel, MSVC */ #undef FASTLZ_STRICT_ALIGN #elif defined(__386) #undef FASTLZ_STRICT_ALIGN #elif defined(_X86_) /* MinGW */ #undef FASTLZ_STRICT_ALIGN #elif defined(__I86__) /* Digital Mars */ #undef FASTLZ_STRICT_ALIGN #endif #endif /* * FIXME: use preprocessor magic to set this on different platforms! */ typedef unsigned char flzuint8; typedef unsigned short flzuint16; typedef unsigned int flzuint32; /* prototypes */ int fastlz_compress(const void* input, int length, void* output); int fastlz_compress_level(int level, const void* input, int length, void* output); int fastlz_decompress(const void* input, int length, void* output, int maxout); #define MAX_COPY 32 #define MAX_LEN 264 /* 256 + 8 */ #define MAX_DISTANCE 8192 #if !defined(FASTLZ_STRICT_ALIGN) #define FASTLZ_READU16(p) *((const flzuint16*)(p)) #else #define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8) #endif #define HASH_LOG 13 #define HASH_SIZE (1<< HASH_LOG) #define HASH_MASK (HASH_SIZE-1) #define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; } #undef FASTLZ_LEVEL #define FASTLZ_LEVEL 1 #undef FASTLZ_COMPRESSOR #undef FASTLZ_DECOMPRESSOR #define FASTLZ_COMPRESSOR fastlz1_compress #define FASTLZ_DECOMPRESSOR fastlz1_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c" #undef FASTLZ_LEVEL #define FASTLZ_LEVEL 2 #undef MAX_DISTANCE #define MAX_DISTANCE 8191 #define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) #undef FASTLZ_COMPRESSOR #undef FASTLZ_DECOMPRESSOR #define FASTLZ_COMPRESSOR fastlz2_compress #define FASTLZ_DECOMPRESSOR fastlz2_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c" int fastlz_compress(const void* input, int length, void* output) { /* for short block, choose fastlz1 */ if(length < 65536) return fastlz1_compress(input, length, output); /* else... */ return fastlz2_compress(input, length, output); } int fastlz_decompress(const void* input, int length, void* output, int maxout) { /* magic identifier for compression level */ int level = ((*(const flzuint8*)input) >> 5) + 1; if(level == 1) return fastlz1_decompress(input, length, output, maxout); if(level == 2) return fastlz2_decompress(input, length, output, maxout); /* unknown level, trigger error */ return 0; } int fastlz_compress_level(int level, const void* input, int length, void* output) { if(level == 1) return fastlz1_compress(input, length, output); if(level == 2) return fastlz2_compress(input, length, output); return 0; } #else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output) { const flzuint8* ip = (const flzuint8*) input; const flzuint8* ip_bound = ip + length - 2; const flzuint8* ip_limit = ip + length - 12; flzuint8* op = (flzuint8*) output; const flzuint8* htab[HASH_SIZE]; const flzuint8** hslot; flzuint32 hval; flzuint32 copy; /* sanity check */ if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) { if(length) { /* create literal copy only */ *op++ = length-1; ip_bound++; while(ip <= ip_bound) *op++ = *ip++; return length+1; } else return 0; } /* initializes hash table */ for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) *hslot = ip; /* we start with literal copy */ copy = 2; *op++ = MAX_COPY-1; *op++ = *ip++; *op++ = *ip++; /* main loop */ while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) { const flzuint8* ref; flzuint32 distance; /* minimum match length */ flzuint32 len = 3; /* comparison starting-point */ const flzuint8* anchor = ip; /* check for a run */ #if FASTLZ_LEVEL==2 if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) { distance = 1; ip += 3; ref = anchor - 1 + 3; goto match; } #endif /* find potential match */ HASH_FUNCTION(hval,ip); hslot = htab + hval; ref = htab[hval]; /* calculate distance to the match */ distance = anchor - ref; /* update hash table */ *hslot = anchor; /* is this a match? check the first 3 bytes */ if(distance==0 || #if FASTLZ_LEVEL==1 (distance >= MAX_DISTANCE) || #else (distance >= MAX_FARDISTANCE) || #endif *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) goto literal; #if FASTLZ_LEVEL==2 /* far, needs at least 5-byte match */ if(distance >= MAX_DISTANCE) { if(*ip++ != *ref++ || *ip++!= *ref++) goto literal; len += 2; } match: #endif /* last matched byte */ ip = anchor + len; /* distance is biased */ distance--; if(!distance) { /* zero distance means a run */ flzuint8 x = ip[-1]; while(ip < ip_bound) if(*ref++ != x) break; else ip++; } else for(;;) { /* safe because the outer check against ip limit */ if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; while(ip < ip_bound) if(*ref++ != *ip++) break; break; } /* if we have copied something, adjust the copy count */ if(copy) /* copy is biased, '0' means 1 byte copy */ *(op-copy-1) = copy-1; else /* back, to overwrite the copy count */ op--; /* reset literal counter */ copy = 0; /* length is biased, '1' means a match of 3 bytes */ ip -= 3; len = ip - anchor; /* encode the match */ #if FASTLZ_LEVEL==2 if(distance < MAX_DISTANCE) { if(len < 7) { *op++ = (len << 5) + (distance >> 8); *op++ = (distance & 255); } else { *op++ = (7 << 5) + (distance >> 8); for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = (distance & 255); } } else { /* far away, but not yet in the another galaxy... */ if(len < 7) { distance -= MAX_DISTANCE; *op++ = (len << 5) + 31; *op++ = 255; *op++ = distance >> 8; *op++ = distance & 255; } else { distance -= MAX_DISTANCE; *op++ = (7 << 5) + 31; for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = 255; *op++ = distance >> 8; *op++ = distance & 255; } } #else if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2)) while(len > MAX_LEN-2) { *op++ = (7 << 5) + (distance >> 8); *op++ = MAX_LEN - 2 - 7 -2; *op++ = (distance & 255); len -= MAX_LEN-2; } if(len < 7) { *op++ = (len << 5) + (distance >> 8); *op++ = (distance & 255); } else { *op++ = (7 << 5) + (distance >> 8); *op++ = len - 7; *op++ = (distance & 255); } #endif /* update the hash at match boundary */ HASH_FUNCTION(hval,ip); htab[hval] = ip++; HASH_FUNCTION(hval,ip); htab[hval] = ip++; /* assuming literal copy */ *op++ = MAX_COPY-1; continue; literal: *op++ = *anchor++; ip = anchor; copy++; if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) { copy = 0; *op++ = MAX_COPY-1; } } /* left-over as literal copy */ ip_bound++; while(ip <= ip_bound) { *op++ = *ip++; copy++; if(copy == MAX_COPY) { copy = 0; *op++ = MAX_COPY-1; } } /* if we have copied something, adjust the copy length */ if(copy) *(op-copy-1) = copy-1; else op--; #if FASTLZ_LEVEL==2 /* marker for fastlz2 */ *(flzuint8*)output |= (1 << 5); #endif return op - (flzuint8*)output; } static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout) { const flzuint8* ip = (const flzuint8*) input; const flzuint8* ip_limit = ip + length; flzuint8* op = (flzuint8*) output; flzuint8* op_limit = op + maxout; flzuint32 ctrl = (*ip++) & 31; int loop = 1; do { const flzuint8* ref = op; flzuint32 len = ctrl >> 5; flzuint32 ofs = (ctrl & 31) << 8; if(ctrl >= 32) { #if FASTLZ_LEVEL==2 flzuint8 code; #endif len--; ref -= ofs; if (len == 7-1) #if FASTLZ_LEVEL==1 len += *ip++; ref -= *ip++; #else do { code = *ip++; len += code; } while (code==255); code = *ip++; ref -= code; /* match from 16-bit distance */ if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) { ofs = (*ip++) << 8; ofs += *ip++; ref = op - ofs - MAX_DISTANCE; } #endif #ifdef FASTLZ_SAFE if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) return 0; if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) return 0; #endif if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) ctrl = *ip++; else loop = 0; if(ref == op) { /* optimize copy for a run */ flzuint8 b = ref[-1]; *op++ = b; *op++ = b; *op++ = b; for(; len; --len) *op++ = b; } else { #if !defined(FASTLZ_STRICT_ALIGN) const flzuint16* p; flzuint16* q; #endif /* copy from reference */ ref--; *op++ = *ref++; *op++ = *ref++; *op++ = *ref++; #if !defined(FASTLZ_STRICT_ALIGN) /* copy a byte, so that now it's word aligned */ if(len & 1) { *op++ = *ref++; len--; } /* copy 16-bit at once */ q = (flzuint16*) op; op += len; p = (const flzuint16*) ref; for(len>>=1; len > 4; len-=4) { *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = *p++; } for(; len; --len) *q++ = *p++; #else for(; len; --len) *op++ = *ref++; #endif } } else { ctrl++; #ifdef FASTLZ_SAFE if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) return 0; if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) return 0; #endif *op++ = *ip++; for(--ctrl; ctrl; ctrl--) *op++ = *ip++; loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); if(loop) ctrl = *ip++; } } while(FASTLZ_EXPECT_CONDITIONAL(loop)); return op - (flzuint8*)output; } #endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ memcached-2.1.0/fastlz/fastlz.h0000644000076700000240000000675612010120566015535 0ustar andreistaff/* FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FASTLZ_H #define FASTLZ_H #define FASTLZ_VERSION 0x000100 #define FASTLZ_VERSION_MAJOR 0 #define FASTLZ_VERSION_MINOR 0 #define FASTLZ_VERSION_REVISION 0 #define FASTLZ_VERSION_STRING "0.1.0" #if defined (__cplusplus) extern "C" { #endif /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, the return value might be larger than length (input buffer size). The input buffer and the output buffer can not overlap. */ int fastlz_compress(const void* input, int length, void* output); /** Decompress a block of compressed data and returns the size of the decompressed block. If error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) will be returned instead. The input buffer and the output buffer can not overlap. Decompression is memory safe and guaranteed not to write the output buffer more than what is specified in maxout. */ int fastlz_decompress(const void* input, int length, void* output, int maxout); /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, the return value might be larger than length (input buffer size). The input buffer and the output buffer can not overlap. Compression level can be specified in parameter level. At the moment, only level 1 and level 2 are supported. Level 1 is the fastest compression and generally useful for short data. Level 2 is slightly slower but it gives better compression ratio. Note that the compressed data, regardless of the level, can always be decompressed using the function fastlz_decompress above. */ int fastlz_compress_level(int level, const void* input, int length, void* output); #if defined (__cplusplus) } #endif #endif /* FASTLZ_H */