package.xml0000664000076400007640000007011112130423646013074 0ustar vagrantvagrant memcache pecl.php.net memcached extension Memcached is a caching daemon designed especially for dynamic web applications to decrease database load by storing objects in memory. This extension allows you to work with memcached through handy OO and procedural interfaces. Antony Dovgal tony2001 tony@daylessday.org no Mikael Johansson mikl mikael@synd.info yes Pierre-Alain Joye pajoye pierre.php@gmail.com yes Herman J. Radtke III hradtke hradtke@php.net yes 2013-04-08 3.0.8 3.0.8 beta beta PHP License - Fix Bug #63142 - memcache client Segmentation fault - Fix Bug #63272 - Explicitly reserve range of flags in php_memcache.h so application code can use - Fix Bug #59602 - getExtendedStats fails on the host intermittently after restarting memcache - Fix Bug #63141 - Added LICENSE file and include tests folder in package 4.3.11 1.4.0b1 memcache 2013-04-07 3.0.8 3.0.8 beta beta PHP License - Fix Bug #63142 - memcache client Segmentation fault - Fix Bug #63272 - Explicitly reserve range of flags in php_memcache.h so application code can use - Fix Bug #59602 - getExtendedStats fails on the host intermittently after restarting memcache - Fix Bug #63141 - Added LICENSE file and include tests folder in package 2012-09-22 3.0.7 3.0.7 beta beta PHP License - Fixed bug #62589 - missing "$" in memcache.php - Fix php_stream_cast() usage. - fixes for windows build and php 5.4 compat 2011-04-10 3.0.6 3.0.6 beta beta PHP License - Fixed PECL Bug #16672 (memcache.php doesn't support unix socket) - Fixed PECL Bug #19374 (memcache.php throws Notice: Undefined index: VALUE when viewing expired items) - Fixed PECL Bug #17518 (Strange behavior in increment on non integer and after) - Fixed potential segfault in memcache queue. - Fixed various tests 2010-10-03 3.0.5 3.0.5 beta beta PHP License - Fixed PECL bug #16059 (Build error: 'MSG_NOSIGNAL' undeclared) - Added method MemcachePool::findServer(key) which returns the server a key hashes to - Changed MemcachePool::findServer() to return only "hostname:port" since tcp port is significant - Fixed PECL bug #16536 (Weight of 0 causes SegFault on memcache_add_server) - Fixed PECL bug #17566 (3.0.4 cache delete bug) - Fixed PECL Bug #16442 (memcache_set fail with integer value) 2009-02-22 3.0.4 3.0.4 beta beta PHP License - Improved performance of consistent hash strategy - Accept timeout parameter as float to enable microsecond timeouts, param is still given in seconds but with a microsecond fraction. - Added session locking to avoid concurrency problems with AJAX apps - Fixed PECL bug #14642 (3.0.x breaks BC with 2.2.x) - Fixed PECL request #13758 (Failed to extract 'connection' variable from object) - Fixed PECL request #14658 (Allow connection timeouts in ms, not seconds) - New INI directive memcache.lock_timeout = 15 2009-01-13 3.0.3 3.0.3 beta beta PHP License - Enabled compression by default for values larger than 20kb - Scalar data types (int, bool, double) are preserved by get/set - Reserved the lowest 2 bytes of the flags for internal use - Fixed PECL bug #14157 (Segmentation fault with errorcallback handler) - Fixed PECL bug #15342 (memcache keys whitespaces replace issue) - Fixed PECL bug #15447 (Persistent Connections Cause Segfaults if One Server Drops) - Fixed PECL bug #15529 (memcache object loses port information) - Fixed PECL bug #14730 (Types lost during get/set) - Fixed PECL request #14801 (Session handler and large sessions) - Added REPORT_ERRORS to php stream open - New INI directive memcache.compress_threshold = 20000 2008-09-11 3.0.2 3.0.2 beta beta PHP License - Updated binary protocol to be compatible with memcached 1.3.0 - Added memcached stats script from Harun Yayli (http://livebookmark.net/memcachephp) - Fixed server weight not being used in the session handler - Fixed PECL bug #13343 (compilation breaks with CodeWarrior compiler) - Fixed PECL bug #13546 (Session handler through unix socket) - Fixed PECL bug #13592 (error in opcode) - Fixed PECL req #13588 (minor inefficiency in binary protocol implementation) - Fixed PECL bug #14044 (send of 32768 bytes failed with errno=11 Resource temporarily unavailable) - Fixed PECL bug #14239 (Memcache::set() modifies value parameter) - Fixed PECL bug #14156 (No check for parameter count in php_mmc_store) - Fixed PECL bug #14458 (off-by-one causes corrupted arrays in 3.0.x) - Fixed PECL bug #14104 (Malformed VALUE header (0)) - Fixed PECL request #13725 (unnecessary allocations in mmc_consistent_add_server) - Changed behaviour when unserialize/uncompress fail to return false instead of failing the server 2008-02-05 3.0.1 3.0.1 beta beta PHP License - Fixed PECL bug #12866 (Ignore memcache.default_port in memcache_connect) - Fixed PECL bug #12883 (Breaks UTF-8 in keys) - Fixed PECL bug #13060 (make problems, due to role="doc" instead of role="src") - Changed behaviour of close() to actually remove all servers from pool (PECL bug #12555) - Added configure option for PEAR installer to disable session handler support - Added package version to phpinfo() 2007-11-26 3.0.0 3.0.0 alpha alpha PHP License See README for details on new API and INI directives. - UDP support - Binary protocol support - Non-blocking IO using select() - Pipelined multi-set/delete/increment/decrement - CAS (Compare-And-Swap) support - Append/prepend support - Key and session redundancy (values are written to N mirrors) - Improved error reporting and failover handling - Added class "MemcachePool" implementing the new API - New INI directives memcache.protocol = {ascii, binary} memcache.redundancy = 1 memcache.session_redundancy = 2 - Changed INI defaults memcache.hash_strategy = consistent memcache.chunk_size = 32768 2008-02-05 2.2.3 2.2.3 stable stable PHP License - Fixed PECL bug #12883 (Breaks UTF-8 in keys) - Fixed PECL bug #13060 (make problems, due to role="doc" instead of role="src") - Added package version to phpinfo() 2008-01-10 2.2.2 2.2.2 stable stable PHP License - Fixed PECL bug 12494 (Segmentation fault when Exception thrown in the callback function) - Fixed PECL bug #12834 (segfault when getting duplicate array values on 64-bit system) - Changed behaviour of close() to actually remove all servers from pool (PECL bug #12555) - Added configure option for PEAR installer to disable session handler support 2007-11-01 2.2.1 2.2.1 stable stable PHP License - Added argument to Memcache::get() that receives the flags for retrieved value(s) - Fixed PECL bug 11369 ("nested" get calls in __wakeup() functions breaks) - Fixed problem with keys being modified in-place and empty keys (PECL Bug #11236) - Fixed incompatibility with standard hash strategy in previous versions 2007-09-21 2.2.0 2.2.0 stable stable PHP License - Added support for consistent hash strategy - New INI directives "memcache.hash_strategy" = {standard, consistent} "memcache.hash_function" = {crc32, fnv} - PHP6 support - Fixed PECL bug #11221 (Double free when returning cached object with __sleep) - Fixed PECL bug #10607 (Segfault on array key retrieval when array key is not IS_STRING) - Don't failover on "object to large" errors - Use $phpincludedir for finding php_session.h 2007-03-27 2.1.2 2.1.2 stable stable PHP License - Added missing files to the package. 2007-03-27 2.1.1 2.1.1 stable stable PHP License - Added experimental session storage support. You can use memcached as session storage. - Fixed PECL bug #9486 (empty keys should not be allowed in memcache_set/add()) - Fixed PECL bug #9854 (get() changes the datatype of variable of given as key) 2006-10-09 2.1.0 2.1.0 stable stable PHP License - Servers may be flagged up/down and the user may specify a error callback - Individual server params and up/down status may be changed at runtime - New methods: setServerParams(), getServerStatus() - Statistics methods support fetching other kinds of stats (slabs, items, cachedump, ...) - Fixed PECL bugs #7631, #8117 (erroneous line feeds in config.m4) - Added memcache.max_failover_attempts ini directive, which can be used to configure max number of servers to try when setting and getting data. 2.0.4 2.0.4 stable stable 2006-05-16 PHP License - Redefined retry_interval as long to fix no-automatic-reconnect behaviour - Fixed PECL bugs #7635, #7637 (OnUpdateInt: referenced symbol not found) 2.0.3 2.0.3 stable stable 2006-05-15 PHP License - Display ini entries in phpinfo() output - Fixed Zend2 compat 2.0.2 2.0.2 stable stable 2006-05-14 PHP License - Added "memcache.default_port" ini directive (default 11211) - Added "memcache.allow_failover" ini directive (default On) - Added "memcache.chunk_size" ini directive (default 8192 bytes) - Setting retry_interval parameter to -1 disables automatic reconnect - Fixed PECL bug #7331 (NULL pointer freeing causes memcache to segfault) 2.0.1 2.0.1 stable stable 2006-02-01 PHP License - Fixed PECL bug #6595 (segfault in addServer()) - Fixed PECL bug #6512 (buffer overrun in getVersion()) - Fixed PECL bug #6460 (lacking include of php_smart_str_public.h) - Fixed PECL bug #6370 (incorrect failover on out-of-memory server errors) - Improved zlib detection 2.0.0 2.0.0 stable stable 2005-12-21 PHP License - Multiple servers with loadbalancing and failover - Automatic compress threshold setting - New methods: addServer(), getExtendedStats(), setCompressThreshold() 1.6 1.6 stable stable 2005-10-12 PHP License - Fixed minor leak appeared when connect fails. - Fixed PECL bug #5626 (segfault when trying to use previously closed connection). 1.5 1.5 stable stable 2005-08-05 PHP License 1) Fixed minor leak. 2) Class Memcache made inheritable. 1.4 1.4 stable stable 2004-12-02 PHP License 1) Added multi get support (i.e. you're now able to get multiple items with one get() call, just pass array of keys to get() and it will return associative array of found items). 1.3 1.3 stable stable 2004-08-29 PHP License 1) Fixed potential problem with empty datasets (yet another patch from Andrey Nigmatulin [anight at monamour dot ru]). 2) Fixed bug #2167 (thanks to [pbnadeau at gmail dot com] for noticing, testing and patching). 1.2 1.2 stable stable 2004-07-20 PHP License 1) Fixed typo, preventing to use set() with flags and expire in the same time (thanks to Troy Hakala [troy at recipezaar dot com] for noticing). 2) Added possibility to store objects and arrays compressed. 3) Hopefully fixed all possible problems with persistent connections (thanks to [anight at monamour dot ru]). 4) Some uncritical impovements. 1.1 1.1 stable stable 2004-06-08 PHP License 1) Fixed couple of possible segfaults and one error (many thanks to Anight). 2) Minor improvements. 1.0 1.0 beta beta 2004-05-21 PHP License 1) Ifdef'ed debug routines, so they are available only in debug mode. This should add more speed to extension. 2) Minor code decorations. 0.4 0.4 beta beta 2004-xx-xx PHP License 1) Added on-the-fly data (de)compression support. 2) Added procedural API. 3) Extension was fairly refactored and accelerated. 0.2 0.2 beta beta 2004-02-26 PHP License New methods: getStats(), increment(), decrement(). Extension should be rather stable. 0.1 0.1 alpha alpha 2004-02-08 PHP License Initial release in PECL memcache-3.0.8/config.m40000644000076400007640000000016412130423646014673 0ustar vagrantvagrantdnl $Id: config.m4 223414 2006-11-15 21:05:03Z tony2001 $ dnl this file is required by phpize sinclude(config9.m4) memcache-3.0.8/config9.m40000644000076400007640000000745612130423646014777 0ustar vagrantvagrantdnl dnl $Id: config9.m4 245492 2007-11-03 09:19:07Z mikl $ dnl PHP_ARG_ENABLE(memcache, whether to enable memcache support, [ --enable-memcache Enable memcache support]) PHP_ARG_ENABLE(memcache-session, whether to enable memcache session handler support, [ --disable-memcache-session Disable memcache session handler support], yes, no) if test -z "$PHP_ZLIB_DIR"; then PHP_ARG_WITH(zlib-dir, for the location of ZLIB, [ --with-zlib-dir[=DIR] memcache: Set the path to ZLIB install prefix.], no, 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_MEMCACHE" != "no"; then 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 dnl # zlib AC_MSG_CHECKING([for the location of zlib]) if test "$PHP_ZLIB_DIR" = "no"; then AC_MSG_ERROR([memcache support requires ZLIB. Use --with-zlib-dir= to specify prefix where ZLIB include and library are located]) else AC_MSG_RESULT([$PHP_ZLIB_DIR]) if test "z$PHP_LIBDIR" != "z"; then dnl PHP5+ PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR/$PHP_LIBDIR, MEMCACHE_SHARED_LIBADD) else dnl PHP4 PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR/lib, MEMCACHE_SHARED_LIBADD) fi PHP_ADD_INCLUDE($PHP_ZLIB_INCDIR) fi if test "$PHP_MEMCACHE_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 AC_MSG_CHECKING([for memcache session support]) if test "$PHP_MEMCACHE_SESSION" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_MEMCACHE_SESSION,1,[Whether memcache session handler is enabled]) AC_DEFINE(HAVE_MEMCACHE,1,[Whether you want memcache support]) PHP_NEW_EXTENSION(memcache, memcache.c memcache_pool.c memcache_queue.c memcache_ascii_protocol.c memcache_binary_protocol.c memcache_standard_hash.c memcache_consistent_hash.c memcache_session.c, $ext_shared,,-I$session_inc_path) ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcache, session) ]) else AC_MSG_RESULT([disabled]) AC_DEFINE(HAVE_MEMCACHE,1,[Whether you want memcache support]) PHP_NEW_EXTENSION(memcache, memcache.c memcache_pool.c memcache_queue.c memcache_ascii_protocol.c memcache_binary_protocol.c memcache_standard_hash.c memcache_consistent_hash.c, $ext_shared) fi dnl this is needed to build the extension with phpize and -Wall if test "$PHP_DEBUG" = "yes"; then CFLAGS="$CFLAGS -Wall" fi fi memcache-3.0.8/config.w320000644000076400007640000000123412130423646014765 0ustar vagrantvagrant// $Id: config.w32 326387 2012-06-30 17:41:36Z ab $ // vim:ft=javascript ARG_ENABLE("memcache", "memcache support", "no"); if (PHP_MEMCACHE != "no") { if (!PHP_ZLIB_SHARED || CHECK_LIB("zlib.lib", "memcache", PHP_MEMCACHE)) { EXTENSION("memcache", " \ memcache.c \ memcache_ascii_protocol.c \ memcache_binary_protocol.c \ memcache_consistent_hash.c \ memcache_pool.c \ memcache_queue.c \ memcache_session.c \ memcache_standard_hash.c"); AC_DEFINE('HAVE_MEMCACHE', 1, 'Have memcache support'); ADD_FLAG("CFLAGS_MEMCACHE", "/D HAVE_MEMCACHE_SESSION=1"); } else { WARNING("memcache not enabled; libraries and headers not found"); } } memcache-3.0.8/CREDITS0000644000076400007640000000004012130423646014175 0ustar vagrantvagrantAntony Dovgal, Mikael Johansson memcache-3.0.8/LICENSE0000644000076400007640000000621012130423646014167 0ustar vagrantvagrant-------------------------------------------------------------------- The PHP License, Version 3.0 Copyright (c) 1999 - 2005 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, 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 . This product includes the Zend Engine, freely available at . memcache-3.0.8/example.php0000644000076400007640000000077512130423646015340 0ustar vagrantvagrantset("str_key", "String to store in memcached"); $memcache->set("num_key", 123); $object = new StdClass; $object->attribute = 'test'; $memcache->set("obj_key", $object); $array = Array('assoc'=>123, 345, 567); $memcache->set("arr_key", $array); var_dump($memcache->get('str_key')); var_dump($memcache->get('num_key')); var_dump($memcache->get('obj_key')); } else { echo "Connection to memcached failed"; } ?> memcache-3.0.8/memcache.c0000644000076400007640000016701012130423646015076 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache.c 329835 2013-03-19 22:39:10Z hradtke $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "ext/standard/php_string.h" #include "php_memcache.h" #ifndef ZEND_ENGINE_2 #define OnUpdateLong OnUpdateInt #endif /* True global resources - no need for thread safety here */ static int le_memcache_pool, le_memcache_server; static zend_class_entry *memcache_pool_ce; static zend_class_entry *memcache_ce; ZEND_EXTERN_MODULE_GLOBALS(memcache) /* {{{ memcache_functions[] */ zend_function_entry memcache_functions[] = { PHP_FE(memcache_connect, NULL) PHP_FE(memcache_pconnect, NULL) PHP_FE(memcache_add_server, NULL) PHP_FE(memcache_set_server_params, NULL) PHP_FE(memcache_set_failure_callback, NULL) PHP_FE(memcache_get_server_status, NULL) PHP_FE(memcache_get_version, NULL) PHP_FE(memcache_add, NULL) PHP_FE(memcache_set, NULL) PHP_FE(memcache_replace, NULL) PHP_FE(memcache_cas, NULL) PHP_FE(memcache_append, NULL) PHP_FE(memcache_prepend, NULL) PHP_FE(memcache_get, NULL) PHP_FE(memcache_delete, NULL) PHP_FE(memcache_debug, NULL) PHP_FE(memcache_get_stats, NULL) PHP_FE(memcache_get_extended_stats, NULL) PHP_FE(memcache_set_compress_threshold, NULL) PHP_FE(memcache_increment, NULL) PHP_FE(memcache_decrement, NULL) PHP_FE(memcache_close, NULL) PHP_FE(memcache_flush, NULL) {NULL, NULL, NULL} }; static zend_function_entry php_memcache_pool_class_functions[] = { PHP_NAMED_FE(connect, zif_memcache_pool_connect, NULL) PHP_NAMED_FE(addserver, zif_memcache_pool_addserver, NULL) PHP_FALIAS(setserverparams, memcache_set_server_params, NULL) PHP_FALIAS(setfailurecallback, memcache_set_failure_callback, NULL) PHP_FALIAS(getserverstatus, memcache_get_server_status, NULL) PHP_NAMED_FE(findserver, zif_memcache_pool_findserver, NULL) PHP_FALIAS(getversion, memcache_get_version, NULL) PHP_FALIAS(add, memcache_add, NULL) PHP_FALIAS(set, memcache_set, NULL) PHP_FALIAS(replace, memcache_replace, NULL) PHP_FALIAS(cas, memcache_cas, NULL) PHP_FALIAS(append, memcache_append, NULL) PHP_FALIAS(prepend, memcache_prepend, NULL) PHP_FALIAS(get, memcache_get, NULL) PHP_FALIAS(delete, memcache_delete, NULL) PHP_FALIAS(getstats, memcache_get_stats, NULL) PHP_FALIAS(getextendedstats, memcache_get_extended_stats, NULL) PHP_FALIAS(setcompressthreshold, memcache_set_compress_threshold, NULL) PHP_FALIAS(increment, memcache_increment, NULL) PHP_FALIAS(decrement, memcache_decrement, NULL) PHP_FALIAS(close, memcache_close, NULL) PHP_FALIAS(flush, memcache_flush, NULL) {NULL, NULL, NULL} }; static zend_function_entry php_memcache_class_functions[] = { PHP_FALIAS(connect, memcache_connect, NULL) PHP_FALIAS(pconnect, memcache_pconnect, NULL) PHP_FALIAS(addserver, memcache_add_server, NULL) {NULL, NULL, NULL} }; /* }}} */ /* {{{ memcache_module_entry */ zend_module_entry memcache_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "memcache", memcache_functions, PHP_MINIT(memcache), PHP_MSHUTDOWN(memcache), NULL, NULL, PHP_MINFO(memcache), #if ZEND_MODULE_API_NO >= 20010901 PHP_MEMCACHE_VERSION, #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_MEMCACHE ZEND_GET_MODULE(memcache) #endif static PHP_INI_MH(OnUpdateChunkSize) /* {{{ */ { long int lval; lval = strtol(new_value, NULL, 10); if (lval <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.chunk_size must be a positive integer ('%s' given)", new_value); return FAILURE; } return OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* }}} */ static PHP_INI_MH(OnUpdateFailoverAttempts) /* {{{ */ { long int lval; lval = strtol(new_value, NULL, 10); if (lval <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.max_failover_attempts must be a positive integer ('%s' given)", new_value); return FAILURE; } return OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* }}} */ static PHP_INI_MH(OnUpdateProtocol) /* {{{ */ { if (!strcasecmp(new_value, "ascii")) { MEMCACHE_G(protocol) = MMC_ASCII_PROTOCOL; } else if (!strcasecmp(new_value, "binary")) { MEMCACHE_G(protocol) = MMC_BINARY_PROTOCOL; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.protocol must be in set {ascii, binary} ('%s' given)", new_value); return FAILURE; } return SUCCESS; } /* }}} */ static PHP_INI_MH(OnUpdateHashStrategy) /* {{{ */ { if (!strcasecmp(new_value, "standard")) { MEMCACHE_G(hash_strategy) = MMC_STANDARD_HASH; } else if (!strcasecmp(new_value, "consistent")) { MEMCACHE_G(hash_strategy) = MMC_CONSISTENT_HASH; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.hash_strategy must be in set {standard, consistent} ('%s' given)", new_value); return FAILURE; } return SUCCESS; } /* }}} */ static PHP_INI_MH(OnUpdateHashFunction) /* {{{ */ { if (!strcasecmp(new_value, "crc32")) { MEMCACHE_G(hash_function) = MMC_HASH_CRC32; } else if (!strcasecmp(new_value, "fnv")) { MEMCACHE_G(hash_function) = MMC_HASH_FNV1A; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.hash_function must be in set {crc32, fnv} ('%s' given)", new_value); return FAILURE; } return SUCCESS; } /* }}} */ static PHP_INI_MH(OnUpdateRedundancy) /* {{{ */ { long int lval; lval = strtol(new_value, NULL, 10); if (lval <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.redundancy must be a positive integer ('%s' given)", new_value); return FAILURE; } return OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* }}} */ static PHP_INI_MH(OnUpdateCompressThreshold) /* {{{ */ { long int lval; lval = strtol(new_value, NULL, 10); if (lval < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.compress_threshold must be a positive integer ('%s' given)", new_value); return FAILURE; } return OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* }}} */ static PHP_INI_MH(OnUpdateLockTimeout) /* {{{ */ { long int lval; lval = strtol(new_value, NULL, 10); if (lval <= 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.lock_timeout must be a positive integer ('%s' given)", new_value); return FAILURE; } return OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* }}} */ /* {{{ PHP_INI */ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("memcache.allow_failover", "1", PHP_INI_ALL, OnUpdateLong, allow_failover, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.max_failover_attempts", "20", PHP_INI_ALL, OnUpdateFailoverAttempts, max_failover_attempts, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.default_port", "11211", PHP_INI_ALL, OnUpdateLong, default_port, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.chunk_size", "32768", PHP_INI_ALL, OnUpdateChunkSize, chunk_size, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.protocol", "ascii", PHP_INI_ALL, OnUpdateProtocol, protocol, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.hash_strategy", "consistent", PHP_INI_ALL, OnUpdateHashStrategy, hash_strategy, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.hash_function", "crc32", PHP_INI_ALL, OnUpdateHashFunction, hash_function, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.redundancy", "1", PHP_INI_ALL, OnUpdateRedundancy, redundancy, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.session_redundancy", "2", PHP_INI_ALL, OnUpdateRedundancy, session_redundancy, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.compress_threshold", "20000", PHP_INI_ALL, OnUpdateCompressThreshold, compress_threshold, zend_memcache_globals, memcache_globals) STD_PHP_INI_ENTRY("memcache.lock_timeout", "15", PHP_INI_ALL, OnUpdateLockTimeout, lock_timeout, zend_memcache_globals, memcache_globals) PHP_INI_END() /* }}} */ /* {{{ internal function protos */ static void _mmc_pool_list_dtor(zend_rsrc_list_entry * TSRMLS_DC); static void _mmc_server_list_dtor(zend_rsrc_list_entry * TSRMLS_DC); static void php_mmc_set_failure_callback(mmc_pool_t *, zval *, zval * TSRMLS_DC); static void php_mmc_failure_callback(mmc_pool_t *, mmc_t *, void * TSRMLS_DC); /* }}} */ /* {{{ php_memcache_init_globals() */ static void php_memcache_init_globals(zend_memcache_globals *memcache_globals_p TSRMLS_DC) { MEMCACHE_G(hash_strategy) = MMC_STANDARD_HASH; MEMCACHE_G(hash_function) = MMC_HASH_CRC32; } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(memcache) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "MemcachePool", php_memcache_pool_class_functions); memcache_pool_ce = zend_register_internal_class(&ce TSRMLS_CC); INIT_CLASS_ENTRY(ce, "Memcache", php_memcache_class_functions); memcache_ce = zend_register_internal_class_ex(&ce, memcache_pool_ce, NULL TSRMLS_CC); le_memcache_pool = zend_register_list_destructors_ex(_mmc_pool_list_dtor, NULL, "memcache connection", module_number); le_memcache_server = zend_register_list_destructors_ex(NULL, _mmc_server_list_dtor, "persistent memcache connection", module_number); #ifdef ZTS ts_allocate_id(&memcache_globals_id, sizeof(zend_memcache_globals), (ts_allocate_ctor) php_memcache_init_globals, NULL); #else php_memcache_init_globals(&memcache_globals TSRMLS_CC); #endif REGISTER_LONG_CONSTANT("MEMCACHE_COMPRESSED", MMC_COMPRESSED, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER1", MMC_RESERVED_APPLICATIONDEFINEDFLAG_12, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER2", MMC_RESERVED_APPLICATIONDEFINEDFLAG_13, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER3", MMC_RESERVED_APPLICATIONDEFINEDFLAG_14, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MEMCACHE_USER4", MMC_RESERVED_APPLICATIONDEFINEDFLAG_15, CONST_CS | CONST_PERSISTENT); REGISTER_INI_ENTRIES(); #if HAVE_MEMCACHE_SESSION REGISTER_LONG_CONSTANT("MEMCACHE_HAVE_SESSION", 1, CONST_CS | CONST_PERSISTENT); php_session_register_module(ps_memcache_ptr); #else REGISTER_LONG_CONSTANT("MEMCACHE_HAVE_SESSION", 0, CONST_CS | CONST_PERSISTENT); #endif return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(memcache) { UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(memcache) { php_info_print_table_start(); php_info_print_table_header(2, "memcache support", "enabled"); php_info_print_table_row(2, "Version", PHP_MEMCACHE_VERSION); php_info_print_table_row(2, "Revision", "$Revision: 329835 $"); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* ------------------ internal functions ------------------ */ static void _mmc_pool_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ { mmc_pool_t *pool = (mmc_pool_t *)rsrc->ptr; if (pool->failure_callback_param) { zval_ptr_dtor((zval **)&pool->failure_callback_param); pool->failure_callback_param = NULL; } mmc_pool_free(pool TSRMLS_CC); } /* }}} */ static void _mmc_server_list_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ { mmc_server_free((mmc_t *)rsrc->ptr TSRMLS_CC); } /* }}} */ static int mmc_get_pool(zval *id, mmc_pool_t **pool TSRMLS_DC) /* {{{ */ { zval **connection; int resource_type; if (Z_TYPE_P(id) != IS_OBJECT || zend_hash_find(Z_OBJPROP_P(id), "connection", sizeof("connection"), (void **)&connection) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "No servers added to memcache connection"); return 0; } *pool = (mmc_pool_t *) zend_list_find(Z_LVAL_PP(connection), &resource_type); if (!*pool || resource_type != le_memcache_pool) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid MemcachePool->connection member variable"); return 0; } return Z_LVAL_PP(connection); } /* }}} */ int mmc_stored_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /* handles SET/ADD/REPLACE response, param is a zval pointer to store result into {{{ */ { zval *result = (zval *)param; if (response == MMC_OK) { if (Z_TYPE_P(result) == IS_NULL) { ZVAL_TRUE(result); } return MMC_REQUEST_DONE; } /* return FALSE or catch memory errors without failover */ if (response == MMC_RESPONSE_EXISTS || response == MMC_RESPONSE_OUT_OF_MEMORY || response == MMC_RESPONSE_TOO_LARGE || response == MMC_RESPONSE_CLIENT_ERROR) { ZVAL_FALSE(result); if (response != MMC_RESPONSE_EXISTS) { /* trigger notice but no need for failover */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, message, response); } return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0 TSRMLS_CC); } /* }}} */ static void php_mmc_store(INTERNAL_FUNCTION_PARAMETERS, int op) /* {{{ */ { mmc_pool_t *pool; mmc_request_t *request; zval *keys, *value = 0, *mmc_object = getThis(); long flags = 0, exptime = 0, cas = 0; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz|zlll", &mmc_object, memcache_pool_ce, &keys, &value, &flags, &exptime, &cas) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|zlll", &keys, &value, &flags, &exptime, &cas) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { RETURN_FALSE; } RETVAL_NULL(); if (Z_TYPE_P(keys) == IS_ARRAY) { zstr key; char keytmp[MAX_LENGTH_OF_LONG + 1]; unsigned int key_len; unsigned long index; int key_type; zval **arrval; HashPosition pos; zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos); while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&arrval, &pos) == SUCCESS) { key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(keys), &key, &key_len, &index, 0, &pos); zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos); switch (key_type) { case HASH_KEY_IS_STRING: key_len--; break; case HASH_KEY_IS_LONG: key_len = sprintf(keytmp, "%lu", index); key = ZSTR(keytmp); break; default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); continue; } /* allocate request */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, return_value, mmc_pool_failover_handler, NULL TSRMLS_CC); if (mmc_prepare_key_ex(ZSTR_VAL(key), key_len, request->key, &(request->key_len)) != MMC_OK) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); mmc_pool_release(pool, request); continue; } /* assemble command */ if (pool->protocol->store(pool, request, op, request->key, request->key_len, flags, exptime, cas, *arrval TSRMLS_CC) != MMC_OK) { mmc_pool_release(pool, request); continue; } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy) TSRMLS_CC) != MMC_OK) { continue; } /* begin sending requests immediatly */ mmc_pool_select(pool TSRMLS_CC); } } else if (value) { /* allocate request */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, return_value, mmc_pool_failover_handler, NULL TSRMLS_CC); if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); mmc_pool_release(pool, request); RETURN_FALSE; } /* assemble command */ if (pool->protocol->store(pool, request, op, request->key, request->key_len, flags, exptime, cas, value TSRMLS_CC) != MMC_OK) { mmc_pool_release(pool, request); RETURN_FALSE; } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy) TSRMLS_CC) != MMC_OK) { RETURN_FALSE; } } else { WRONG_PARAM_COUNT; } /* execute all requests */ mmc_pool_run(pool TSRMLS_CC); if (Z_TYPE_P(return_value) == IS_NULL) { RETVAL_FALSE; } } /* }}} */ int mmc_numeric_response_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /* handles a mutate response line, param is a zval pointer to store result into {{{ */ { zval *result = (zval *)param; if (response == MMC_OK) { if (Z_TYPE_P(result) == IS_ARRAY) { add_assoc_bool_ex(result, request->key, request->key_len + 1, 1); } else if (Z_TYPE_P(result) == IS_NULL) { /* switch only from null to true, not from false to true */ ZVAL_TRUE(result); } return MMC_REQUEST_DONE; } if (response == MMC_RESPONSE_NOT_FOUND || response == MMC_RESPONSE_CLIENT_ERROR) { if (Z_TYPE_P(result) == IS_ARRAY) { add_assoc_bool_ex(result, request->key, request->key_len + 1, 0); } else { ZVAL_FALSE(result); } if (response != MMC_RESPONSE_NOT_FOUND) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, message, response); } return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0 TSRMLS_CC); } /* }}} */ static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int invert) /* sends one or several commands which have a single optional numeric parameter (incr, decr, delete) {{{ */ { mmc_pool_t *pool; zval *mmc_object = getThis(); zval *keys; long value = 1, defval = 0, exptime = 0; mmc_request_t *request; void *value_handler_param[3]; int defval_used = 0; if (mmc_object == NULL) { if (deleted) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz|l", &mmc_object, memcache_pool_ce, &keys, &value) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz|lll", &mmc_object, memcache_pool_ce, &keys, &value, &defval, &exptime) == FAILURE) { return; } defval_used = ZEND_NUM_ARGS() >= 4; } } else { if (deleted) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &keys, &value) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|lll", &keys, &value, &defval, &exptime) == FAILURE) { return; } defval_used = ZEND_NUM_ARGS() >= 3; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { RETURN_FALSE; } value_handler_param[0] = return_value; value_handler_param[1] = NULL; value_handler_param[2] = NULL; if (Z_TYPE_P(keys) == IS_ARRAY) { zval **key; HashPosition pos; if (deleted) { /* changed to true/false by mmc_numeric_response_handler */ RETVAL_NULL(); } else { /* populated with responses by mmc_numeric_response_handler and mmc_value_handler_multi */ array_init(return_value); } zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos); while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&key, &pos) == SUCCESS) { zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos); /* allocate request */ request = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_numeric_response_handler, return_value, mmc_pool_failover_handler, NULL TSRMLS_CC); request->value_handler = mmc_value_handler_multi; request->value_handler_param = value_handler_param; if (mmc_prepare_key(*key, request->key, &(request->key_len)) != MMC_OK) { mmc_pool_release(pool, request); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); continue; } if (deleted) { pool->protocol->delete(request, request->key, request->key_len, exptime); } else { pool->protocol->mutate(request, *key, request->key, request->key_len, invert ? -value : value, defval, defval_used, exptime); } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy) TSRMLS_CC) != MMC_OK) { continue; } /* begin sending requests immediatly */ mmc_pool_select(pool TSRMLS_CC); } } else { /* changed to true/false by mmc_numeric_response_handler or set to a value * by mmc_value_handler_single if incr/decr returns one */ RETVAL_NULL(); /* allocate request */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_numeric_response_handler, return_value, mmc_pool_failover_handler, NULL TSRMLS_CC); request->value_handler = mmc_value_handler_single; request->value_handler_param = value_handler_param; if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) { mmc_pool_release(pool, request); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); RETURN_FALSE; } if (deleted) { pool->protocol->delete(request, request->key, request->key_len, exptime); } else { pool->protocol->mutate(request, keys, request->key, request->key_len, invert ? -value : value, defval, defval_used, exptime); } /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy) TSRMLS_CC) != MMC_OK) { RETURN_FALSE; } } /* execute all requests */ mmc_pool_run(pool TSRMLS_CC); } /* }}} */ mmc_t *mmc_find_persistent(const char *host, int host_len, unsigned short port, unsigned short udp_port, double timeout, int retry_interval TSRMLS_DC) /* {{{ */ { mmc_t *mmc; zend_rsrc_list_entry *le; char *key; int key_len; key_len = spprintf(&key, 0, "memcache:server:%s:%u:%u", host, port, udp_port); if (zend_hash_find(&EG(persistent_list), key, key_len+1, (void **)&le) == FAILURE) { zend_rsrc_list_entry new_le; mmc = mmc_server_new(host, host_len, port, udp_port, 1, timeout, retry_interval TSRMLS_CC); new_le.type = le_memcache_server; new_le.ptr = mmc; /* register new persistent connection */ if (zend_hash_update(&EG(persistent_list), key, key_len+1, (void *)&new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) { mmc_server_free(mmc TSRMLS_CC); mmc = NULL; } else { #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3) zend_list_insert(mmc, le_memcache_server); #else zend_list_insert(mmc, le_memcache_server TSRMLS_CC); #endif } } else if (le->type != le_memcache_server || le->ptr == NULL) { zend_rsrc_list_entry new_le; zend_hash_del(&EG(persistent_list), key, key_len+1); mmc = mmc_server_new(host, host_len, port, udp_port, 1, timeout, retry_interval TSRMLS_CC); new_le.type = le_memcache_server; new_le.ptr = mmc; /* register new persistent connection */ if (zend_hash_update(&EG(persistent_list), key, key_len+1, (void *)&new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) { mmc_server_free(mmc TSRMLS_CC); mmc = NULL; } else { #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3) zend_list_insert(mmc, le_memcache_server); #else zend_list_insert(mmc, le_memcache_server TSRMLS_CC); #endif } } else { mmc = (mmc_t *)le->ptr; mmc->timeout = double_to_timeval(timeout); mmc->tcp.retry_interval = retry_interval; /* attempt to reconnect this node before failover in case connection has gone away */ if (mmc->tcp.status == MMC_STATUS_CONNECTED) { mmc->tcp.status = MMC_STATUS_UNKNOWN; } if (mmc->udp.status == MMC_STATUS_CONNECTED) { mmc->udp.status = MMC_STATUS_UNKNOWN; } } efree(key); return mmc; } /* }}} */ static mmc_t *php_mmc_pool_addserver( zval *mmc_object, const char *host, int host_len, long tcp_port, long udp_port, long weight, zend_bool persistent, double timeout, long retry_interval, zend_bool status, mmc_pool_t **pool_result TSRMLS_DC) /* {{{ */ { zval **connection; mmc_pool_t *pool; mmc_t *mmc; int list_id, resource_type; if (weight < 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "weight must be a positive integer"); return NULL; } /* initialize pool if need be */ if (zend_hash_find(Z_OBJPROP_P(mmc_object), "connection", sizeof("connection"), (void **)&connection) == FAILURE) { pool = mmc_pool_new(TSRMLS_C); pool->failure_callback = &php_mmc_failure_callback; #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3) list_id = zend_list_insert(pool, le_memcache_pool); #else list_id = zend_list_insert(pool, le_memcache_pool TSRMLS_CC); #endif add_property_resource(mmc_object, "connection", list_id); } else { pool = (mmc_pool_t *)zend_list_find(Z_LVAL_PP(connection), &resource_type); if (!pool || resource_type != le_memcache_pool) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown connection identifier"); return NULL; } } /* binary protocol isn't support over UDP yet */ if (udp_port && pool->protocol == &mmc_binary_protocol) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "binary protocol isn't support over UDP, defaulting to TCP"); udp_port = 0; } /* lazy initialization of server struct */ if (persistent && status) { mmc = mmc_find_persistent(host, host_len, tcp_port, udp_port, timeout, retry_interval TSRMLS_CC); } else { mmc = mmc_server_new(host, host_len, tcp_port, udp_port, 0, timeout, retry_interval TSRMLS_CC); } /* add server in failed mode */ if (!status) { mmc->tcp.status = MMC_STATUS_FAILED; mmc->udp.status = MMC_STATUS_FAILED; } mmc_pool_add(pool, mmc, weight); if (pool_result != NULL) { *pool_result = pool; } return mmc; } /* }}} */ static void php_mmc_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool persistent) /* {{{ */ { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc; char *host; int host_len; long tcp_port = MEMCACHE_G(default_port); double timeout = MMC_DEFAULT_TIMEOUT; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ld", &host, &host_len, &tcp_port, &timeout) == FAILURE) { return; } /* initialize pool and object if need be */ if (!mmc_object) { int list_id; mmc_pool_t *pool = mmc_pool_new(TSRMLS_C); pool->failure_callback = &php_mmc_failure_callback; #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3) list_id = zend_list_insert(pool, le_memcache_pool); #else list_id = zend_list_insert(pool, le_memcache_pool TSRMLS_CC); #endif mmc_object = return_value; object_init_ex(mmc_object, memcache_ce); add_property_resource(mmc_object, "connection", list_id); } else { RETVAL_TRUE; } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, 0, 1, persistent, timeout, MMC_DEFAULT_RETRY, 1, NULL TSRMLS_CC); if (mmc == NULL) { RETURN_FALSE; } /* force a reconnect attempt if stream EOF */ if (mmc->tcp.stream != NULL && php_stream_eof(mmc->tcp.stream)) { mmc_server_disconnect(mmc, &(mmc->tcp) TSRMLS_CC); } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } /* force a tcp connect (if not persistently connected) */ if (mmc_pool_open(pool, mmc, &(mmc->tcp), 0 TSRMLS_CC) != MMC_OK) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect to %s:%d, %s (%d)", host, mmc->tcp.port, mmc->error ? mmc->error : "Unknown error", mmc->errnum); RETURN_FALSE; } } /* }}} */ /* * STAT 6:chunk_size 64 */ static int mmc_stats_parse_stat(char *start, char *end, zval *result TSRMLS_DC) /* {{{ */ { char *space, *colon, *key; long index = 0; if (Z_TYPE_P(result) != IS_ARRAY) { array_init(result); } /* find space delimiting key and value */ if ((space = php_memnstr(start, " ", 1, end)) == NULL) { return 0; } /* find colon delimiting subkeys */ if ((colon = php_memnstr(start, ":", 1, space - 1)) != NULL) { zval *element, **elem; key = estrndup(start, colon - start); /* find existing or create subkey array in result */ if ((is_numeric_string(key, colon - start, &index, NULL, 0) && zend_hash_index_find(Z_ARRVAL_P(result), index, (void **)&elem) != FAILURE) || zend_hash_find(Z_ARRVAL_P(result), key, colon - start + 1, (void **)&elem) != FAILURE) { element = *elem; } else { MAKE_STD_ZVAL(element); array_init(element); add_assoc_zval_ex(result, key, colon - start + 1, element); } efree(key); return mmc_stats_parse_stat(colon + 1, end, element TSRMLS_CC); } /* no more subkeys, add value under last subkey */ key = estrndup(start, space - start); add_assoc_stringl_ex(result, key, space - start + 1, space + 1, end - space, 1); efree(key); return 1; } /* }}} */ /* * ITEM test_key [3 b; 1157099416 s] */ static int mmc_stats_parse_item(char *start, char *end, zval *result TSRMLS_DC) /* {{{ */ { char *space, *value, *value_end, *key; zval *element; if (Z_TYPE_P(result) != IS_ARRAY) { array_init(result); } /* find space delimiting key and value */ if ((space = php_memnstr(start, " ", 1, end)) == NULL) { return 0; } MAKE_STD_ZVAL(element); array_init(element); /* parse each contained value */ for (value = php_memnstr(space, "[", 1, end); value != NULL && value <= end; value = php_memnstr(value + 1, ";", 1, end)) { do { value++; } while (*value == ' ' && value <= end); if (value <= end && (value_end = php_memnstr(value, " ", 1, end)) != NULL && value_end <= end) { add_next_index_stringl(element, value, value_end - value, 1); } } /* add parsed values under key */ key = estrndup(start, space - start); add_assoc_zval_ex(result, key, space - start + 1, element); efree(key); return 1; } /* }}} */ static int mmc_stats_parse_generic(char *start, char *end, zval *result TSRMLS_DC) /* {{{ */ { char *space, *key; if (Z_TYPE_P(result) != IS_ARRAY) { array_init(result); } if (start < end) { if ((space = php_memnstr(start, " ", 1, end)) != NULL) { key = estrndup(start, space - start); add_assoc_stringl_ex(result, key, space - start + 1, space + 1, end - space, 1); efree(key); } else { add_next_index_stringl(result, start, end - start, 1); } } else { return 0; } return 1; } /* }}} */ static void php_mmc_failure_callback(mmc_pool_t *pool, mmc_t *mmc, void *param TSRMLS_DC) /* {{{ */ { zval **callback; /* check for userspace callback */ if (param != NULL && zend_hash_find(Z_OBJPROP_P((zval *)param), "_failureCallback", sizeof("_failureCallback"), (void **)&callback) == SUCCESS && Z_TYPE_PP(callback) != IS_NULL) { if (IS_CALLABLE(*callback, 0, NULL)) { zval *retval = NULL; zval *host, *tcp_port, *udp_port, *error, *errnum; zval **params[5]; params[0] = &host; params[1] = &tcp_port; params[2] = &udp_port; params[3] = &error; params[4] = &errnum; MAKE_STD_ZVAL(host); MAKE_STD_ZVAL(tcp_port); MAKE_STD_ZVAL(udp_port); MAKE_STD_ZVAL(error); MAKE_STD_ZVAL(errnum); ZVAL_STRING(host, mmc->host, 1); ZVAL_LONG(tcp_port, mmc->tcp.port); ZVAL_LONG(udp_port, mmc->udp.port); if (mmc->error != NULL) { ZVAL_STRING(error, mmc->error, 1); } else { ZVAL_NULL(error); } ZVAL_LONG(errnum, mmc->errnum); call_user_function_ex(EG(function_table), NULL, *callback, &retval, 5, params, 0, NULL TSRMLS_CC); zval_ptr_dtor(&host); zval_ptr_dtor(&tcp_port); zval_ptr_dtor(&udp_port); zval_ptr_dtor(&error); zval_ptr_dtor(&errnum); if (retval != NULL) { zval_ptr_dtor(&retval); } } else { php_mmc_set_failure_callback(pool, (zval *)param, NULL TSRMLS_CC); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid failure callback"); } } else { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, mmc->error, mmc->errnum); } } /* }}} */ static void php_mmc_set_failure_callback(mmc_pool_t *pool, zval *mmc_object, zval *callback TSRMLS_DC) /* {{{ */ { // Decrease refcount of old mmc_object if (pool->failure_callback_param) { zval_ptr_dtor((zval **)&pool->failure_callback_param); } if (callback != NULL) { zval *callback_tmp; ALLOC_ZVAL(callback_tmp); *callback_tmp = *callback; zval_copy_ctor(callback_tmp); INIT_PZVAL(callback_tmp); add_property_zval(mmc_object, "_failureCallback", callback_tmp); pool->failure_callback_param = mmc_object; zval_add_ref(&mmc_object); INIT_PZVAL(callback_tmp); } else { add_property_null(mmc_object, "_failureCallback"); pool->failure_callback_param = NULL; } } /* }}} */ /* ---------------- module functions ---------------- */ /* {{{ proto bool MemcachePool::connect(string host [, int tcp_port [, int udp_port [, bool persistent [, int weight [, double timeout [, int retry_interval] ] ] ] ] ]) Connects to server and returns a Memcache object */ PHP_NAMED_FUNCTION(zif_memcache_pool_connect) { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc; char *host; int host_len; long tcp_port = MEMCACHE_G(default_port), udp_port = 0, weight = 1, retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool persistent = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|llbldl", &host, &host_len, &tcp_port, &udp_port, &persistent, &weight, &timeout, &retry_interval) == FAILURE) { return; } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, udp_port, weight, persistent, timeout, retry_interval, 1, NULL TSRMLS_CC); if (mmc == NULL) { RETURN_FALSE; } /* force a reconnect attempt if stream EOF */ if (mmc->tcp.stream != NULL && php_stream_eof(mmc->tcp.stream)) { mmc_server_disconnect(mmc, &(mmc->tcp) TSRMLS_CC); } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } /* force a tcp connect (if not persistently connected) */ if (mmc_pool_open(pool, mmc, &(mmc->tcp), 0 TSRMLS_CC) != MMC_OK) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't connect to %s:%d, %s (%d)", host, mmc->tcp.port, mmc->error ? mmc->error : "Unknown error", mmc->errnum); RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto object memcache_connect(string host [, int port [, double timeout ] ]) Connects to server and returns a Memcache object */ PHP_FUNCTION(memcache_connect) { php_mmc_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ proto object memcache_pconnect(string host [, int port [, double timeout ] ]) Connects to server and returns a Memcache object */ PHP_FUNCTION(memcache_pconnect) { php_mmc_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ proto bool MemcachePool::addServer(string host [, int tcp_port [, int udp_port [, bool persistent [, int weight [, double timeout [, int retry_interval [, bool status] ] ] ] ]) Adds a server to the pool */ PHP_NAMED_FUNCTION(zif_memcache_pool_addserver) { zval *mmc_object = getThis(); mmc_t *mmc; char *host; int host_len; long tcp_port = MEMCACHE_G(default_port), udp_port = 0, weight = 1, retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool persistent = 1, status = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|llbldlb", &host, &host_len, &tcp_port, &udp_port, &persistent, &weight, &timeout, &retry_interval, &status) == FAILURE) { return; } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, udp_port, weight, persistent, timeout, retry_interval, status, NULL TSRMLS_CC); if (mmc == NULL) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto string MemcachePool::findServer(string key) Returns the server corresponding to a key */ PHP_NAMED_FUNCTION(zif_memcache_pool_findserver) { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc; zval *zkey; char key[MMC_MAX_KEY_LEN + 1]; unsigned int key_len; char *hostname; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zkey) == FAILURE) { return; } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { RETURN_FALSE; } if (mmc_prepare_key(zkey, key, &key_len) != MMC_OK) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); RETURN_FALSE; } mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC); spprintf(&hostname, 0, "%s:%d", mmc->host, mmc->tcp.port); RETURN_STRING(hostname, 0); } /* }}} */ /* {{{ proto bool memcache_add_server(string host [, int port [, bool persistent [, int weight [, double timeout [, int retry_interval [, bool status [, callback failure_callback ] ] ] ] ] ] ]) Adds a connection to the pool. The order in which this function is called is significant */ PHP_FUNCTION(memcache_add_server) { zval *mmc_object = getThis(), *failure_callback = NULL; mmc_pool_t *pool; mmc_t *mmc; char *host; int host_len; long tcp_port = MEMCACHE_G(default_port), weight = 1, retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool persistent = 1, status = 1; if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lbldlbz", &host, &host_len, &tcp_port, &persistent, &weight, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os|lbldlbz", &mmc_object, memcache_ce, &host, &host_len, &tcp_port, &persistent, &weight, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) { if (!IS_CALLABLE(failure_callback, 0, NULL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid failure callback"); RETURN_FALSE; } } mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, 0, weight, persistent, timeout, retry_interval, status, &pool TSRMLS_CC); if (mmc == NULL) { RETURN_FALSE; } if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) { php_mmc_set_failure_callback(pool, mmc_object, failure_callback TSRMLS_CC); } RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_set_server_params( string host [, int port [, double timeout [, int retry_interval [, bool status [, callback failure_callback ] ] ] ] ]) Changes server parameters at runtime */ PHP_FUNCTION(memcache_set_server_params) { zval *mmc_object = getThis(), *failure_callback = NULL; mmc_pool_t *pool; mmc_t *mmc = NULL; long tcp_port = MEMCACHE_G(default_port), retry_interval = MMC_DEFAULT_RETRY; double timeout = MMC_DEFAULT_TIMEOUT; zend_bool status = 1; int host_len, i; char *host; if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ldlbz", &host, &host_len, &tcp_port, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os|ldlbz", &mmc_object, memcache_pool_ce, &host, &host_len, &tcp_port, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } for (i=0; inum_servers; i++) { if (!strcmp(pool->servers[i]->host, host) && pool->servers[i]->tcp.port == tcp_port) { mmc = pool->servers[i]; break; } } if (!mmc) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Server not found in pool"); RETURN_FALSE; } if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) { if (!IS_CALLABLE(failure_callback, 0, NULL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid failure callback"); RETURN_FALSE; } } mmc->timeout = double_to_timeval(timeout); mmc->tcp.retry_interval = retry_interval; /* store the smallest timeout for any server */ if (timeval_to_double(mmc->timeout) < timeval_to_double(pool->timeout)) { pool->timeout = mmc->timeout; } if (!status) { mmc->tcp.status = MMC_STATUS_FAILED; mmc->udp.status = MMC_STATUS_FAILED; } else { if (mmc->tcp.status == MMC_STATUS_FAILED) { mmc->tcp.status = MMC_STATUS_DISCONNECTED; } if (mmc->udp.status == MMC_STATUS_FAILED) { mmc->udp.status = MMC_STATUS_DISCONNECTED; } } if (failure_callback != NULL) { if (Z_TYPE_P(failure_callback) != IS_NULL) { php_mmc_set_failure_callback(pool, mmc_object, failure_callback TSRMLS_CC); } else { php_mmc_set_failure_callback(pool, mmc_object, NULL TSRMLS_CC); } } RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_set_failure_callback( callback failure_callback ) Changes the failover callback */ PHP_FUNCTION(memcache_set_failure_callback) { zval *mmc_object = getThis(), *failure_callback; mmc_pool_t *pool; if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &failure_callback) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz", &mmc_object, memcache_pool_ce, &failure_callback) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } if (Z_TYPE_P(failure_callback) != IS_NULL) { if (!IS_CALLABLE(failure_callback, 0, NULL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid failure callback"); RETURN_FALSE; } } if (Z_TYPE_P(failure_callback) != IS_NULL) { php_mmc_set_failure_callback(pool, mmc_object, failure_callback TSRMLS_CC); } else { php_mmc_set_failure_callback(pool, mmc_object, NULL TSRMLS_CC); } RETURN_TRUE; } /* }}} */ /* {{{ proto int memcache_get_server_status( string host [, int port ]) Returns server status (0 if server is failed, otherwise non-zero) */ PHP_FUNCTION(memcache_get_server_status) { zval *mmc_object = getThis(); mmc_pool_t *pool; mmc_t *mmc = NULL; long tcp_port = MEMCACHE_G(default_port); int host_len, i; char *host; if (mmc_object) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &host, &host_len, &tcp_port) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os|l", &mmc_object, memcache_pool_ce, &host, &host_len, &tcp_port) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } for (i=0; inum_servers; i++) { if (!strcmp(pool->servers[i]->host, host) && pool->servers[i]->tcp.port == tcp_port) { mmc = pool->servers[i]; break; } } if (!mmc) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Server not found in pool"); RETURN_FALSE; } RETURN_LONG(mmc->tcp.status > MMC_STATUS_FAILED ? 1 : 0); } /* }}} */ static int mmc_version_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /* parses the VERSION response line, param is a zval pointer to store version into {{{ */ { if (response != MMC_RESPONSE_ERROR) { char *version = emalloc(message_len + 1); if (sscanf(message, "VERSION %s", version) == 1) { ZVAL_STRING((zval *)param, version, 0); } else { efree(version); ZVAL_STRINGL((zval *)param, (char *)message, message_len, 1); } return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0 TSRMLS_CC); } /* }}} */ /* {{{ proto string memcache_get_version( object memcache ) Returns server's version */ PHP_FUNCTION(memcache_get_version) { mmc_pool_t *pool; zval *mmc_object = getThis(); int i; mmc_request_t *request; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &mmc_object, memcache_pool_ce) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { RETURN_FALSE; } RETVAL_FALSE; for (i=0; inum_servers; i++) { /* run command and check for valid return value */ request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_version_handler, return_value, NULL, NULL TSRMLS_CC); pool->protocol->version(request); if (mmc_pool_schedule(pool, pool->servers[i], request TSRMLS_CC) == MMC_OK) { mmc_pool_run(pool TSRMLS_CC); if (Z_TYPE_P(return_value) == IS_STRING) { break; } } } } /* }}} */ /* {{{ proto bool memcache_add(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Adds new item. Item with such key should not exist. */ PHP_FUNCTION(memcache_add) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_ADD); } /* }}} */ /* {{{ proto bool memcache_set(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Sets the value of an item. Item may exist or not */ PHP_FUNCTION(memcache_set) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_SET); } /* }}} */ /* {{{ proto bool memcache_replace(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ) Replaces existing item. Returns false if item doesn't exist */ PHP_FUNCTION(memcache_replace) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_REPLACE); } /* }}} */ /* {{{ proto bool memcache_cas(object memcache, mixed key [, mixed var [, int flag [, int exptime [, long cas ] ] ] ]) Sets the value of an item if the CAS value is the same (Compare-And-Swap) */ PHP_FUNCTION(memcache_cas) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_CAS); } /* }}} */ /* {{{ proto bool memcache_prepend(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Appends a value to the stored value, value must exist */ PHP_FUNCTION(memcache_append) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_APPEND); } /* }}} */ /* {{{ proto bool memcache_prepend(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ]) Prepends a value to the stored value, value must exist */ PHP_FUNCTION(memcache_prepend) { php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_PREPEND); } /* }}} */ int mmc_value_handler_multi( const char *key, unsigned int key_len, zval *value, unsigned int flags, unsigned long cas, void *param TSRMLS_DC) /* receives a multiple values, param is a zval** array to store value and flags in {{{ */ { zval **result = (zval **)param; /* add value to result */ if (Z_TYPE_P(result[0]) != IS_ARRAY) { array_init(result[0]); } add_assoc_zval_ex(result[0], (char *)key, key_len + 1, value); /* add flags to result */ if (result[1] != NULL) { if (Z_TYPE_P(result[1]) != IS_ARRAY) { array_init(result[1]); } add_assoc_long_ex(result[1], (char *)key, key_len + 1, flags); } /* add CAS value to result */ if (result[2] != NULL) { if (Z_TYPE_P(result[2]) != IS_ARRAY) { array_init(result[2]); } add_assoc_long_ex(result[2], (char *)key, key_len + 1, cas); } return MMC_REQUEST_DONE; } /* }}} */ int mmc_value_handler_single( const char *key, unsigned int key_len, zval *value, unsigned int flags, unsigned long cas, void *param TSRMLS_DC) /* receives a single value, param is a zval pointer to store value to {{{ */ { zval **result = (zval **)param; ZVAL_ZVAL(result[0], value, 1, 1); if (result[1] != NULL) { ZVAL_LONG(result[1], flags); } if (result[2] != NULL) { ZVAL_LONG(result[2], cas); } return MMC_REQUEST_DONE; } /* }}} */ static int mmc_value_failover_handler(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param TSRMLS_DC) /* uses keys and return value to reschedule requests to other servers, param is a zval ** pointer {{{ */ { zval **key, *keys = ((zval **)param)[0], **value_handler_param = (zval **)((void **)param)[1]; HashPosition pos; if (!MEMCACHE_G(allow_failover) || request->failed_servers.len >= MEMCACHE_G(max_failover_attempts)) { mmc_pool_release(pool, request); return MMC_REQUEST_FAILURE; } zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos); while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&key, &pos) == SUCCESS) { zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos); /* re-schedule key if it does not exist in return value array */ if (Z_TYPE_P(value_handler_param[0]) != IS_ARRAY || !zend_hash_exists(Z_ARRVAL_P(value_handler_param[0]), Z_STRVAL_PP(key), Z_STRLEN_PP(key) + 1)) { mmc_pool_schedule_get(pool, MMC_PROTO_UDP, value_handler_param[2] != NULL ? MMC_OP_GETS : MMC_OP_GET, *key, request->value_handler, request->value_handler_param, request->failover_handler, request->failover_handler_param, request TSRMLS_CC); } } mmc_pool_release(pool, request); return MMC_OK; } /* }}}*/ /* {{{ proto mixed memcache_get( object memcache, mixed key [, mixed &flags [, mixed &cas ] ] ) Returns value of existing item or false */ PHP_FUNCTION(memcache_get) { mmc_pool_t *pool; zval *keys, *flags = NULL, *cas = NULL, *mmc_object = getThis(); void *value_handler_param[3], *failover_handler_param[2]; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz|zz", &mmc_object, memcache_pool_ce, &keys, &flags, &cas) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|zz", &keys, &flags, &cas) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { RETURN_FALSE; } value_handler_param[0] = return_value; value_handler_param[1] = flags; value_handler_param[2] = cas; if (Z_TYPE_P(keys) == IS_ARRAY) { zval **key; HashPosition pos; /* return empty array if no keys found */ array_init(return_value); failover_handler_param[0] = keys; failover_handler_param[1] = value_handler_param; zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos); while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&key, &pos) == SUCCESS) { zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos); /* schedule request */ mmc_pool_schedule_get(pool, MMC_PROTO_UDP, cas != NULL ? MMC_OP_GETS : MMC_OP_GET, *key, mmc_value_handler_multi, value_handler_param, mmc_value_failover_handler, failover_handler_param, NULL TSRMLS_CC); } } else { mmc_request_t *request; /* return false if key isn't found */ ZVAL_FALSE(return_value); /* allocate request */ request = mmc_pool_request_get( pool, MMC_PROTO_UDP, mmc_value_handler_single, value_handler_param, mmc_pool_failover_handler, NULL TSRMLS_CC); if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) { mmc_pool_release(pool, request); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); return; } pool->protocol->get(request, cas != NULL ? MMC_OP_GETS : MMC_OP_GET, keys, request->key, request->key_len); /* schedule request */ if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, 1 TSRMLS_CC) != MMC_OK) { return; } } /* execute all requests */ mmc_pool_run(pool TSRMLS_CC); } /* }}} */ static int mmc_stats_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /* parses the stats response line, param is a zval pointer to store stats into {{{ */ { if (response != MMC_RESPONSE_ERROR) { char *line = (char *)message; if(!message_len) { return MMC_REQUEST_DONE; } if (mmc_str_left(line, "RESET", message_len, sizeof("RESET")-1)) { ZVAL_TRUE((zval *)param); return MMC_REQUEST_DONE; } else if (mmc_str_left(line, "STAT ", message_len, sizeof("STAT ")-1)) { if (mmc_stats_parse_stat(line + sizeof("STAT ")-1, line + message_len - 1, (zval *)param TSRMLS_CC)) { return MMC_REQUEST_AGAIN; } } else if (mmc_str_left(line, "ITEM ", message_len, sizeof("ITEM ")-1)) { if (mmc_stats_parse_item(line + sizeof("ITEM ")-1, line + message_len - 1, (zval *)param TSRMLS_CC)) { return MMC_REQUEST_AGAIN; } } else if (mmc_str_left(line, "END", message_len, sizeof("END")-1)) { return MMC_REQUEST_DONE; } else if (mmc_stats_parse_generic(line, line + message_len, (zval *)param TSRMLS_CC)) { return MMC_REQUEST_AGAIN; } zval_dtor((zval *)param); ZVAL_FALSE((zval *)param); return MMC_REQUEST_FAILURE; } return mmc_request_failure(mmc, request->io, message, message_len, 0 TSRMLS_CC); } /* }}} */ static int mmc_stats_checktype(const char *type) { /* {{{ */ return type == NULL || !strcmp(type, "reset") || !strcmp(type, "malloc") || !strcmp(type, "slabs") || !strcmp(type, "cachedump") || !strcmp(type, "items") || !strcmp(type, "sizes"); } /* }}} */ /* {{{ proto array memcache_get_stats( object memcache [, string type [, int slabid [, int limit ] ] ]) Returns server's statistics */ PHP_FUNCTION(memcache_get_stats) { mmc_pool_t *pool; zval *mmc_object = getThis(); char *type = NULL; int i, type_len = 0; long slabid = 0, limit = MMC_DEFAULT_CACHEDUMP_LIMIT; mmc_request_t *request; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|sll", &mmc_object, memcache_pool_ce, &type, &type_len, &slabid, &limit) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sll", &type, &type_len, &slabid, &limit) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { RETURN_FALSE; } if (!mmc_stats_checktype(type)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stats type"); RETURN_FALSE; } ZVAL_FALSE(return_value); for (i=0; inum_servers; i++) { request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stats_handler, return_value, NULL, NULL TSRMLS_CC); pool->protocol->stats(request, type, slabid, limit); /* run command and check for valid return value */ if (mmc_pool_schedule(pool, pool->servers[i], request TSRMLS_CC) == MMC_OK) { mmc_pool_run(pool TSRMLS_CC); if (Z_TYPE_P(return_value) != IS_BOOL || Z_BVAL_P(return_value)) { break; } } } /* execute all requests */ mmc_pool_run(pool TSRMLS_CC); } /* }}} */ /* {{{ proto array memcache_get_extended_stats( object memcache [, string type [, int slabid [, int limit ] ] ]) Returns statistics for each server in the pool */ PHP_FUNCTION(memcache_get_extended_stats) { mmc_pool_t *pool; zval *mmc_object = getThis(), *stats; char *host, *type = NULL; int i, host_len, type_len = 0; long slabid = 0, limit = MMC_DEFAULT_CACHEDUMP_LIMIT; mmc_request_t *request; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|sll", &mmc_object, memcache_pool_ce, &type, &type_len, &slabid, &limit) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sll", &type, &type_len, &slabid, &limit) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) { RETURN_FALSE; } if (!mmc_stats_checktype(type)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stats type"); RETURN_FALSE; } array_init(return_value); for (i=0; inum_servers; i++) { MAKE_STD_ZVAL(stats); ZVAL_FALSE(stats); host_len = spprintf(&host, 0, "%s:%u", pool->servers[i]->host, pool->servers[i]->tcp.port); add_assoc_zval_ex(return_value, host, host_len + 1, stats); efree(host); request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stats_handler, stats, NULL, NULL TSRMLS_CC); pool->protocol->stats(request, type, slabid, limit); if (mmc_pool_schedule(pool, pool->servers[i], request TSRMLS_CC) == MMC_OK) { mmc_pool_run(pool TSRMLS_CC); } } /* execute all requests */ mmc_pool_run(pool TSRMLS_CC); } /* }}} */ /* {{{ proto array memcache_set_compress_threshold( object memcache, int threshold [, float min_savings ] ) Set automatic compress threshold */ PHP_FUNCTION(memcache_set_compress_threshold) { mmc_pool_t *pool; zval *mmc_object = getThis(); long threshold; double min_savings = MMC_DEFAULT_SAVINGS; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ol|d", &mmc_object, memcache_pool_ce, &threshold, &min_savings) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &threshold, &min_savings) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } if (threshold < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "threshold must be a positive integer"); RETURN_FALSE; } pool->compress_threshold = threshold; if (min_savings != MMC_DEFAULT_SAVINGS) { if (min_savings < 0 || min_savings > 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "min_savings must be a float in the 0..1 range"); RETURN_FALSE; } pool->min_compress_savings = min_savings; } else { pool->min_compress_savings = MMC_DEFAULT_SAVINGS; } RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_delete(object memcache, mixed key [, int exptime ]) Deletes existing item */ PHP_FUNCTION(memcache_delete) { php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0); } /* }}} */ /* {{{ proto mixed memcache_increment(object memcache, mixed key [, int value [, int defval [, int exptime ] ] ]) Increments existing variable */ PHP_FUNCTION(memcache_increment) { php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); } /* }}} */ /* {{{ proto mixed memcache_decrement(object memcache, mixed key [, int value [, int defval [, int exptime ] ] ]) Decrements existing variable */ PHP_FUNCTION(memcache_decrement) { php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1); } /* }}} */ /* {{{ proto bool memcache_close( object memcache ) Closes connection to memcached */ PHP_FUNCTION(memcache_close) { mmc_pool_t *pool; zval *mmc_object = getThis(); if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &mmc_object, memcache_pool_ce) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } mmc_pool_close(pool TSRMLS_CC); RETURN_TRUE; } /* }}} */ static int mmc_flush_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /* parses the OK response line, param is an int pointer to increment on success {{{ */ { if (response == MMC_OK) { (*((int *)param))++; return MMC_REQUEST_DONE; } if (response == MMC_RESPONSE_CLIENT_ERROR) { ZVAL_FALSE((zval *)param); php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, message, response); return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0 TSRMLS_CC); } /* }}} */ /* {{{ proto bool memcache_flush( object memcache [, int delay ] ) Flushes cache, optionally at after the specified delay */ PHP_FUNCTION(memcache_flush) { mmc_pool_t *pool; zval *mmc_object = getThis(); mmc_request_t *request; unsigned int i, responses = 0; long delay = 0; if (mmc_object == NULL) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|l", &mmc_object, memcache_pool_ce, &delay) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &delay) == FAILURE) { return; } } if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC)) { RETURN_FALSE; } for (i=0; inum_servers; i++) { request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_flush_handler, &responses, NULL, NULL TSRMLS_CC); pool->protocol->flush(request, delay); if (mmc_pool_schedule(pool, pool->servers[i], request TSRMLS_CC) == MMC_OK) { /* begin sending requests immediatly */ mmc_pool_select(pool TSRMLS_CC); } } /* execute all requests */ mmc_pool_run(pool TSRMLS_CC); if (responses < pool->num_servers) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto bool memcache_debug( bool onoff ) */ PHP_FUNCTION(memcache_debug) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache_debug() is deprecated, please use a debugger (like Eclipse + CDT)"); } /* }}} */ /* * 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 */ memcache-3.0.8/memcache_pool.c0000644000076400007640000013726512130423646016140 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_pool.c 329835 2013-03-19 22:39:10Z hradtke $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef PHP_WIN32 #include #else #include #endif #include "php.h" #include "php_network.h" #include "ext/standard/crc32.h" #include "ext/standard/php_var.h" #include "ext/standard/php_string.h" #include "ext/standard/php_smart_str.h" #include "memcache_pool.h" ZEND_DECLARE_MODULE_GLOBALS(memcache) inline void mmc_buffer_alloc(mmc_buffer_t *buffer, unsigned int size) /* ensures space for an additional size bytes {{{ */ { register size_t newlen; smart_str_alloc((&(buffer->value)), size, 0); } /* }}} */ inline void mmc_buffer_free(mmc_buffer_t *buffer) /* {{{ */ { if (buffer->value.c != NULL) { smart_str_free(&(buffer->value)); } memset(buffer, 0, sizeof(*buffer)); } /* }}} */ static unsigned int mmc_hash_crc32_init() { return ~0; } static unsigned int mmc_hash_crc32_finish(unsigned int seed) { return ~seed; } static unsigned int mmc_hash_crc32_combine(unsigned int seed, const void *key, unsigned int key_len) /* CRC32 hash {{{ */ { const char *p = (const char *)key, *end = p + key_len; while (p < end) { CRC32(seed, *(p++)); } return seed; } /* }}} */ mmc_hash_function_t mmc_hash_crc32 = { mmc_hash_crc32_init, mmc_hash_crc32_combine, mmc_hash_crc32_finish }; static unsigned int mmc_hash_fnv1a_combine(unsigned int seed, const void *key, unsigned int key_len) /* FNV-1a hash {{{ */ { const char *p = (const char *)key, *end = p + key_len; while (p < end) { seed ^= (unsigned int)*(p++); seed *= FNV_32_PRIME; } return seed; } /* }}} */ static unsigned int mmc_hash_fnv1a_init() { return FNV_32_INIT; } static unsigned int mmc_hash_fnv1a_finish(unsigned int seed) { return seed; } mmc_hash_function_t mmc_hash_fnv1a = { mmc_hash_fnv1a_init, mmc_hash_fnv1a_combine, mmc_hash_fnv1a_finish }; double timeval_to_double(struct timeval tv) { return (double)tv.tv_sec + ((double)tv.tv_usec) / 1000000; } struct timeval double_to_timeval(double sec) { struct timeval tv; tv.tv_sec = (long)sec; tv.tv_usec = (sec - tv.tv_sec) * 1000000; return tv; } static size_t mmc_stream_read_buffered(mmc_stream_t *io, char *buf, size_t count TSRMLS_DC) /* attempts to reads count bytes from the stream buffer {{{ */ { size_t toread = io->buffer.value.len - io->buffer.idx < count ? io->buffer.value.len - io->buffer.idx : count; memcpy(buf, io->buffer.value.c + io->buffer.idx, toread); io->buffer.idx += toread; return toread; } /* }}} */ static char *mmc_stream_readline_buffered(mmc_stream_t *io, char *buf, size_t maxlen, size_t *retlen TSRMLS_DC) /* reads count bytes from the stream buffer, this implementation only detects \r\n (like memcached sends) {{{ */ { char *eol; eol = memchr(io->buffer.value.c + io->buffer.idx, '\n', io->buffer.value.len - io->buffer.idx); if (eol != NULL) { *retlen = eol - io->buffer.value.c - io->buffer.idx + 1; } else { *retlen = io->buffer.value.len - io->buffer.idx; } /* ensure space for data + \0 */ if (*retlen >= maxlen) { *retlen = maxlen - 1; } memcpy(buf, io->buffer.value.c + io->buffer.idx, *retlen); io->buffer.idx += *retlen; buf[*retlen] = '\0'; return buf; } /* }}} */ static size_t mmc_stream_read_wrapper(mmc_stream_t *io, char *buf, size_t count TSRMLS_DC) /* {{{ */ { return php_stream_read(io->stream, buf, count); } /* }}} */ static char *mmc_stream_readline_wrapper(mmc_stream_t *io, char *buf, size_t maxlen, size_t *retlen TSRMLS_DC) /* {{{ */ { return php_stream_get_line(io->stream, ZSTR(buf), maxlen, retlen); } /* }}} */ void mmc_request_reset(mmc_request_t *request) /* {{{ */ { request->key_len = 0; mmc_buffer_reset(&(request->sendbuf)); mmc_queue_reset(&(request->failed_servers)); request->failed_index = 0; } /* }}} */ void mmc_request_free(mmc_request_t *request) /* {{{ */ { mmc_buffer_free(&(request->sendbuf)); mmc_buffer_free(&(request->readbuf)); mmc_queue_free(&(request->failed_servers)); efree(request); } /* }}} */ static inline int mmc_request_send(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* {{{ */ { int count, bytes; /* send next chunk of buffer */ count = request->sendbuf.value.len - request->sendbuf.idx; if (count > request->io->stream->chunk_size) { count = request->io->stream->chunk_size; } bytes = send(request->io->fd, request->sendbuf.value.c + request->sendbuf.idx, count, MSG_NOSIGNAL); if (bytes >= 0) { request->sendbuf.idx += bytes; /* done sending? */ if (request->sendbuf.idx >= request->sendbuf.value.len) { return MMC_REQUEST_DONE; } return MMC_REQUEST_MORE; } else { char *message, buf[1024]; long err = php_socket_errno(); if (err == EAGAIN) { return MMC_REQUEST_MORE; } message = php_socket_strerror(err, buf, 1024); return mmc_server_failure(mmc, request->io, message, err TSRMLS_CC); } } /* }}} */ static int mmc_request_read_udp(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* reads an entire datagram into buffer and validates the udp header {{{ */ { size_t bytes; mmc_udp_header_t *header; uint16_t reqid, seqid; /* reset buffer if completely consumed */ if (request->io->buffer.idx >= request->io->buffer.value.len) { mmc_buffer_reset(&(request->io->buffer)); } /* attempt to read datagram + sentinel-byte */ mmc_buffer_alloc(&(request->io->buffer), MMC_MAX_UDP_LEN + 1); bytes = php_stream_read(request->io->stream, request->io->buffer.value.c + request->io->buffer.value.len, MMC_MAX_UDP_LEN + 1); if (bytes < sizeof(mmc_udp_header_t)) { return mmc_server_failure(mmc, request->io, "Failed te read complete UDP header from stream", 0 TSRMLS_CC); } if (bytes > MMC_MAX_UDP_LEN) { return mmc_server_failure(mmc, request->io, "Server sent packet larger than MMC_MAX_UDP_LEN bytes", 0 TSRMLS_CC); } header = (mmc_udp_header_t *)(request->io->buffer.value.c + request->io->buffer.value.len); reqid = ntohs(header->reqid); seqid = ntohs(header->seqid); /* initialize udp header fields */ if (!request->udp.total && request->udp.reqid == reqid) { request->udp.seqid = seqid; request->udp.total = ntohs(header->total); } /* detect dropped packets and reschedule for tcp delivery */ if (request->udp.reqid != reqid || request->udp.seqid != seqid) { /* ensure that no more udp requests are scheduled for a while */ request->io->status = MMC_STATUS_FAILED; request->io->failed = (long)time(NULL); /* discard packets for previous requests */ if (reqid < request->udp.reqid) { return MMC_REQUEST_MORE; } php_error_docref(NULL TSRMLS_CC, E_NOTICE, "UDP packet loss, expected reqid/seqid %d/%d got %d/%d", (int)request->udp.reqid, (int)request->udp.seqid, (int)reqid, (int)seqid); return MMC_REQUEST_RETRY; } request->udp.seqid++; /* skip udp header */ if (request->io->buffer.idx > 0) { memmove( request->io->buffer.value.c + request->io->buffer.value.len, request->io->buffer.value.c + request->io->buffer.value.len + sizeof(mmc_udp_header_t), bytes - sizeof(mmc_udp_header_t)); } else { request->io->buffer.idx += sizeof(mmc_udp_header_t); } request->io->buffer.value.len += bytes; return MMC_OK; } /* }}} */ static void mmc_compress(mmc_pool_t *pool, mmc_buffer_t *buffer, const char *value, int value_len, unsigned int *flags, int copy TSRMLS_DC) /* {{{ */ { /* autocompress large values */ if (pool->compress_threshold && value_len >= pool->compress_threshold) { *flags |= MMC_COMPRESSED; } if (*flags & MMC_COMPRESSED) { int status; mmc_buffer_t prev; unsigned long result_len = value_len * (1 - pool->min_compress_savings); if (copy) { /* value is already in output buffer */ prev = *buffer; /* allocate space for prev header + result */ memset(buffer, 0, sizeof(*buffer)); mmc_buffer_alloc(buffer, prev.value.len + result_len); /* append prev header */ smart_str_appendl(&(buffer->value), prev.value.c, prev.value.len - value_len); buffer->idx = prev.idx; } else { /* allocate space directly in buffer */ mmc_buffer_alloc(buffer, result_len); } if (MMC_COMPRESSION_LEVEL >= 0) { status = compress2((unsigned char *)buffer->value.c + buffer->value.len, &result_len, (unsigned const char *)value, value_len, MMC_COMPRESSION_LEVEL); } else { status = compress((unsigned char *)buffer->value.c + buffer->value.len, &result_len, (unsigned const char *)value, value_len); } if (status == Z_OK) { buffer->value.len += result_len; } else { smart_str_appendl(&(buffer->value), value, value_len); *flags &= ~MMC_COMPRESSED; } if (copy) { mmc_buffer_free(&prev); } } else if (!copy) { smart_str_appendl(&(buffer->value), value, value_len); } } /* }}}*/ static int mmc_uncompress(const char *data, unsigned long data_len, char **result, unsigned long *result_len) /* {{{ */ { int status, factor = 1; do { *result_len = data_len * (1 << factor++); *result = (char *)erealloc(*result, *result_len + 1); status = uncompress((unsigned char *)*result, result_len, (unsigned const char *)data, data_len); } while (status == Z_BUF_ERROR && factor < 16); if (status == Z_OK) { return MMC_OK; } efree(*result); return MMC_REQUEST_FAILURE; } /* }}}*/ int mmc_pack_value(mmc_pool_t *pool, mmc_buffer_t *buffer, zval *value, unsigned int *flags TSRMLS_DC) /* does serialization and compression to pack a zval into the buffer {{{ */ { if (*flags & 0xffff & ~MMC_COMPRESSED) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The lowest two bytes of the flags array is reserved for pecl/memcache internal use"); return MMC_REQUEST_FAILURE; } *flags &= ~MMC_SERIALIZED; switch (Z_TYPE_P(value)) { case IS_STRING: *flags |= MMC_TYPE_STRING; mmc_compress(pool, buffer, Z_STRVAL_P(value), Z_STRLEN_P(value), flags, 0 TSRMLS_CC); break; case IS_LONG: *flags |= MMC_TYPE_LONG; *flags &= ~MMC_COMPRESSED; smart_str_append_long(&(buffer->value), Z_LVAL_P(value)); break; case IS_DOUBLE: { char buf[256]; int len = snprintf(buf, 256, "%.14g", Z_DVAL_P(value)); *flags |= MMC_TYPE_DOUBLE; *flags &= ~MMC_COMPRESSED; smart_str_appendl(&(buffer->value), buf, len); break; } case IS_BOOL: *flags |= MMC_TYPE_BOOL; *flags &= ~MMC_COMPRESSED; smart_str_appendc(&(buffer->value), Z_BVAL_P(value) ? '1' : '0'); break; default: { php_serialize_data_t value_hash; zval value_copy, *value_copy_ptr; size_t prev_len = buffer->value.len; /* FIXME: we should be using 'Z' instead of this, but unfortunately it's PHP5-only */ value_copy = *value; zval_copy_ctor(&value_copy); value_copy_ptr = &value_copy; PHP_VAR_SERIALIZE_INIT(value_hash); php_var_serialize(&(buffer->value), &value_copy_ptr, &value_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(value_hash); /* trying to save null or something went really wrong */ if (buffer->value.c == NULL || buffer->value.len == prev_len) { zval_dtor(&value_copy); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to serialize value"); return MMC_REQUEST_FAILURE; } *flags |= MMC_SERIALIZED; zval_dtor(&value_copy); mmc_compress(pool, buffer, buffer->value.c + prev_len, buffer->value.len - prev_len, flags, 1 TSRMLS_CC); } } return MMC_OK; } /* }}} */ int mmc_unpack_value( mmc_t *mmc, mmc_request_t *request, mmc_buffer_t *buffer, const char *key, unsigned int key_len, unsigned int flags, unsigned long cas, unsigned int bytes TSRMLS_DC) /* does uncompression and unserializing to reconstruct a zval {{{ */ { char *data = NULL; unsigned long data_len; int rv; zval *object; MAKE_STD_ZVAL(object); if (flags & MMC_COMPRESSED) { if (mmc_uncompress(buffer->value.c, bytes, &data, &data_len) != MMC_OK) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Failed to uncompress data"); return MMC_REQUEST_DONE; } } else { data = buffer->value.c; data_len = bytes; } if (flags & MMC_SERIALIZED) { php_unserialize_data_t var_hash; const unsigned char *p = (unsigned char *)data; char key_tmp[MMC_MAX_KEY_LEN + 1]; mmc_request_value_handler value_handler; void *value_handler_param; mmc_buffer_t buffer_tmp; /* make copies of data to ensure re-entrancy */ memcpy(key_tmp, key, key_len + 1); value_handler = request->value_handler; value_handler_param = request->value_handler_param; if (!(flags & MMC_COMPRESSED)) { buffer_tmp = *buffer; mmc_buffer_release(buffer); } PHP_VAR_UNSERIALIZE_INIT(var_hash); if (!php_var_unserialize(&object, &p, p + data_len, &var_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (flags & MMC_COMPRESSED) { efree(data); } else if (buffer->value.c == NULL) { *buffer = buffer_tmp; } else { mmc_buffer_free(&buffer_tmp); } php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Failed to unserialize data"); return MMC_REQUEST_DONE; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (flags & MMC_COMPRESSED) { efree(data); } else if (buffer->value.c == NULL) { *buffer = buffer_tmp; } else { mmc_buffer_free(&buffer_tmp); } /* delegate to value handler */ return value_handler(key_tmp, key_len, object, flags, cas, value_handler_param TSRMLS_CC); } else { switch (flags & 0x0f00) { case MMC_TYPE_LONG: { long val; data[data_len] = '\0'; val = strtol(data, NULL, 10); ZVAL_LONG(object, val); break; } case MMC_TYPE_DOUBLE: { double val = 0; data[data_len] = '\0'; sscanf(data, "%lg", &val); ZVAL_DOUBLE(object, val); break; } case MMC_TYPE_BOOL: ZVAL_BOOL(object, data_len == 1 && data[0] == '1'); break; default: data[data_len] = '\0'; ZVAL_STRINGL(object, data, data_len, 0); if (!(flags & MMC_COMPRESSED)) { /* release buffer because it's now owned by the zval */ mmc_buffer_release(buffer); } } /* delegate to value handler */ return request->value_handler(key, key_len, object, flags, cas, request->value_handler_param TSRMLS_CC); } } /* }}}*/ mmc_t *mmc_server_new( const char *host, int host_len, unsigned short tcp_port, unsigned short udp_port, int persistent, double timeout, int retry_interval TSRMLS_DC) /* {{{ */ { mmc_t *mmc = pemalloc(sizeof(mmc_t), persistent); memset(mmc, 0, sizeof(*mmc)); mmc->host = pemalloc(host_len + 1, persistent); memcpy(mmc->host, host, host_len); mmc->host[host_len] = '\0'; mmc->tcp.port = tcp_port; mmc->tcp.status = MMC_STATUS_DISCONNECTED; mmc->udp.port = udp_port; mmc->udp.status = MMC_STATUS_DISCONNECTED; mmc->persistent = persistent; mmc->timeout = double_to_timeval(timeout); mmc->tcp.retry_interval = retry_interval; mmc->tcp.chunk_size = MEMCACHE_G(chunk_size); mmc->udp.retry_interval = retry_interval; mmc->udp.chunk_size = MEMCACHE_G(chunk_size); /* = MMC_MAX_UDP_LEN;*/ return mmc; } /* }}} */ static void _mmc_server_disconnect(mmc_t *mmc, mmc_stream_t *io, int close_persistent_stream TSRMLS_DC) /* {{{ */ { mmc_buffer_free(&(io->buffer)); if (io->stream != NULL) { if (mmc->persistent) { if (close_persistent_stream) { php_stream_pclose(io->stream); } } else { php_stream_close(io->stream); } io->stream = NULL; io->fd = 0; } io->status = MMC_STATUS_DISCONNECTED; } /* }}} */ void mmc_server_disconnect(mmc_t *mmc, mmc_stream_t *io TSRMLS_DC) /* {{{ */ { _mmc_server_disconnect(mmc, io, 1 TSRMLS_CC); } /* }}} */ static void mmc_server_seterror(mmc_t *mmc, const char *error, int errnum) /* {{{ */ { if (error != NULL) { if (mmc->error != NULL) { efree(mmc->error); } mmc->error = estrdup(error); mmc->errnum = errnum; } } /* }}} */ static void mmc_server_deactivate(mmc_pool_t *pool, mmc_t *mmc TSRMLS_DC) /* disconnect and marks the server as down, failovers all queued requests {{{ */ { mmc_queue_t readqueue; mmc_request_t *request; mmc_server_disconnect(mmc, &(mmc->tcp) TSRMLS_CC); mmc_server_disconnect(mmc, &(mmc->udp) TSRMLS_CC); mmc->tcp.status = MMC_STATUS_FAILED; mmc->udp.status = MMC_STATUS_FAILED; mmc->tcp.failed = (long)time(NULL); mmc->udp.failed = mmc->tcp.failed; mmc_queue_remove(pool->sending, mmc); mmc_queue_remove(pool->reading, mmc); /* failover queued requests, sendque can be ignored since * readque + readreq + buildreq will always contain all active requests */ mmc_queue_reset(&(mmc->sendqueue)); mmc->sendreq = NULL; readqueue = mmc->readqueue; mmc_queue_release(&(mmc->readqueue)); if (mmc->readreq != NULL) { mmc_queue_push(&readqueue, mmc->readreq); mmc->readreq = NULL; } if (mmc->buildreq != NULL) { mmc_queue_push(&readqueue, mmc->buildreq); mmc->buildreq = NULL; } /* delegate to failover handlers */ while ((request = mmc_queue_pop(&readqueue)) != NULL) { request->failover_handler(pool, mmc, request, request->failover_handler_param TSRMLS_CC); } mmc_queue_free(&readqueue); /* fire userspace failure event */ if (pool->failure_callback != NULL) { pool->failure_callback(pool, mmc, pool->failure_callback_param TSRMLS_CC); } } /* }}} */ int mmc_server_failure(mmc_t *mmc, mmc_stream_t *io, const char *error, int errnum TSRMLS_DC) /* determines if a request should be retried or is a hard network failure {{{ */ { switch (io->status) { case MMC_STATUS_DISCONNECTED: return MMC_REQUEST_RETRY; /* attempt reconnect of sockets in unknown state */ case MMC_STATUS_UNKNOWN: io->status = MMC_STATUS_DISCONNECTED; return MMC_REQUEST_RETRY; } mmc_server_seterror(mmc, error, errnum); return MMC_REQUEST_FAILURE; } /* }}} */ int mmc_request_failure(mmc_t *mmc, mmc_stream_t *io, const char *message, unsigned int message_len, int errnum TSRMLS_DC) /* checks for a valid server generated error message and calls mmc_server_failure() {{{ */ { if (message_len) { return mmc_server_failure(mmc, io, message, errnum TSRMLS_CC); } return mmc_server_failure(mmc, io, "Malformed server response", errnum TSRMLS_CC); } /* }}} */ static int mmc_server_connect(mmc_pool_t *pool, mmc_t *mmc, mmc_stream_t *io, int udp TSRMLS_DC) /* connects a stream, calls mmc_server_deactivate() on failure {{{ */ { char *host, *hash_key = NULL, *errstr = NULL; int host_len, errnum = 0; struct timeval tv = mmc->timeout; int fd; /* close open stream */ if (io->stream != NULL) { mmc_server_disconnect(mmc, io TSRMLS_CC); } if (mmc->persistent) { spprintf(&hash_key, 0, "memcache:stream:%s:%u:%d", mmc->host, io->port, udp); } #if PHP_API_VERSION > 20020918 if (udp) { host_len = spprintf(&host, 0, "udp://%s:%u", mmc->host, io->port); } else if (io->port) { host_len = spprintf(&host, 0, "%s:%u", mmc->host, io->port); } else { host_len = spprintf(&host, 0, "%s", mmc->host); } io->stream = php_stream_xport_create( host, host_len, ENFORCE_SAFE_MODE | REPORT_ERRORS | (mmc->persistent ? STREAM_OPEN_PERSISTENT : 0), STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hash_key, &tv, NULL, &errstr, &errnum); efree(host); #else if (mmc->persistent) { switch (php_stream_from_persistent_id(hash_key, &(io->stream) TSRMLS_CC)) { case PHP_STREAM_PERSISTENT_SUCCESS: if (php_stream_eof(io->stream)) { php_stream_pclose(io->stream); io->stream = NULL; io->fd = 0; break; } case PHP_STREAM_PERSISTENT_FAILURE: break; } } if (io->stream == NULL) { if (io->port) { io->stream = php_stream_sock_open_host(mmc->host, io->port, udp ? SOCK_DGRAM : SOCK_STREAM, &tv, hash_key); } else if (strncasecmp("unix://", mmc->host, sizeof("unix://")-1) == 0 && strlen(mmc->host) > sizeof("unix://")-1) { io->stream = php_stream_sock_open_unix(mmc->host + sizeof("unix://")-1, strlen(mmc->host + sizeof("unix://")-1), hash_key, &tv); } else { io->stream = php_stream_sock_open_unix(mmc->host, strlen(mmc->host), hash_key, &tv); } } #endif if (hash_key != NULL) { efree(hash_key); } /* check connection and extract socket for select() purposes */ if (!io->stream || php_stream_cast(io->stream, PHP_STREAM_AS_FD_FOR_SELECT, (void **)&fd, 1) != SUCCESS) { mmc_server_seterror(mmc, errstr != NULL ? errstr : "Connection failed", errnum); mmc_server_deactivate(pool, mmc TSRMLS_CC); if (errstr != NULL) { efree(errstr); } return MMC_REQUEST_FAILURE; } io->fd = fd; io->status = MMC_STATUS_CONNECTED; php_stream_auto_cleanup(io->stream); php_stream_set_chunk_size(io->stream, io->chunk_size); php_stream_set_option(io->stream, PHP_STREAM_OPTION_BLOCKING, 0, NULL); php_stream_set_option(io->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &(mmc->timeout)); /* doing our own buffering increases performance */ php_stream_set_option(io->stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); php_stream_set_option(io->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); /* php_stream buffering prevent us from detecting datagram boundaries when using udp */ if (udp) { io->read = mmc_stream_read_buffered; io->readline = mmc_stream_readline_buffered; } else { io->read = mmc_stream_read_wrapper; io->readline = mmc_stream_readline_wrapper; } #ifdef SO_NOSIGPIPE /* Mac OS X doesn't have MSG_NOSIGNAL */ { int optval = 1; setsockopt(io->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, sizeof(optval)); } #endif if (mmc->error != NULL) { efree(mmc->error); mmc->error = NULL; } return MMC_OK; } /* }}} */ int mmc_server_valid(mmc_t *mmc TSRMLS_DC) /* checks if a server should be considered valid to serve requests {{{ */ { if (mmc != NULL) { if (mmc->tcp.status >= MMC_STATUS_DISCONNECTED) { return 1; } if (mmc->tcp.status == MMC_STATUS_FAILED && mmc->tcp.retry_interval >= 0 && (long)time(NULL) >= mmc->tcp.failed + mmc->tcp.retry_interval) { return 1; } } return 0; } /* }}} */ void mmc_server_sleep(mmc_t *mmc TSRMLS_DC) /* prepare server struct for persistent sleep {{{ */ { mmc_buffer_free(&(mmc->tcp.buffer)); mmc_buffer_free(&(mmc->udp.buffer)); mmc->sendreq = NULL; mmc->readreq = NULL; mmc->buildreq = NULL; mmc_queue_free(&(mmc->sendqueue)); mmc_queue_free(&(mmc->readqueue)); if (mmc->error != NULL) { efree(mmc->error); mmc->error = NULL; } } /* }}} */ void mmc_server_free(mmc_t *mmc TSRMLS_DC) /* {{{ */ { mmc_server_sleep(mmc TSRMLS_CC); _mmc_server_disconnect(mmc, &(mmc->tcp), 0 TSRMLS_CC); _mmc_server_disconnect(mmc, &(mmc->udp), 0 TSRMLS_CC); pefree(mmc->host, mmc->persistent); pefree(mmc, mmc->persistent); } /* }}} */ static void mmc_pool_init_hash(mmc_pool_t *pool TSRMLS_DC) /* {{{ */ { mmc_hash_function_t *hash; switch (MEMCACHE_G(hash_strategy)) { case MMC_CONSISTENT_HASH: pool->hash = &mmc_consistent_hash; break; default: pool->hash = &mmc_standard_hash; } switch (MEMCACHE_G(hash_function)) { case MMC_HASH_FNV1A: hash = &mmc_hash_fnv1a; break; default: hash = &mmc_hash_crc32; } pool->hash_state = pool->hash->create_state(hash); } /* }}} */ mmc_pool_t *mmc_pool_new(TSRMLS_D) /* {{{ */ { mmc_pool_t *pool = emalloc(sizeof(mmc_pool_t)); memset(pool, 0, sizeof(*pool)); switch (MEMCACHE_G(protocol)) { case MMC_BINARY_PROTOCOL: pool->protocol = &mmc_binary_protocol; break; default: pool->protocol = &mmc_ascii_protocol; } mmc_pool_init_hash(pool TSRMLS_CC); pool->compress_threshold = MEMCACHE_G(compress_threshold); pool->min_compress_savings = MMC_DEFAULT_SAVINGS; pool->sending = &(pool->_sending1); pool->reading = &(pool->_reading1); return pool; } /* }}} */ void mmc_pool_free(mmc_pool_t *pool TSRMLS_DC) /* {{{ */ { int i; mmc_request_t *request; for (i=0; inum_servers; i++) { if (pool->servers[i] != NULL) { if (pool->servers[i]->persistent) { mmc_server_sleep(pool->servers[i] TSRMLS_CC); } else { mmc_server_free(pool->servers[i] TSRMLS_CC); } pool->servers[i] = NULL; } } if (pool->num_servers) { efree(pool->servers); } pool->hash->free_state(pool->hash_state); mmc_queue_free(&(pool->_sending1)); mmc_queue_free(&(pool->_sending2)); mmc_queue_free(&(pool->_reading1)); mmc_queue_free(&(pool->_reading2)); mmc_queue_free(&(pool->pending)); /* requests are owned by us so free them */ while ((request = mmc_queue_pop(&(pool->free_requests))) != NULL) { pool->protocol->free_request(request); } mmc_queue_free(&(pool->free_requests)); efree(pool); } /* }}} */ void mmc_pool_add(mmc_pool_t *pool, mmc_t *mmc, unsigned int weight) /* adds a server to the pool and hash strategy {{{ */ { pool->hash->add_server(pool->hash_state, mmc, weight); pool->servers = erealloc(pool->servers, sizeof(*pool->servers) * (pool->num_servers + 1)); pool->servers[pool->num_servers] = mmc; /* store the smallest timeout for any server */ if (!pool->num_servers || timeval_to_double(mmc->timeout) < timeval_to_double(pool->timeout)) { pool->timeout = mmc->timeout; } pool->num_servers++; } /* }}} */ void mmc_pool_close(mmc_pool_t *pool TSRMLS_DC) /* disconnects and removes all servers in the pool {{{ */ { if (pool->num_servers) { int i; for (i=0; inum_servers; i++) { if (pool->servers[i]->persistent) { mmc_server_sleep(pool->servers[i] TSRMLS_CC); } else { mmc_server_free(pool->servers[i] TSRMLS_CC); } } efree(pool->servers); pool->servers = NULL; pool->num_servers = 0; /* reallocate the hash strategy state */ pool->hash->free_state(pool->hash_state); mmc_pool_init_hash(pool TSRMLS_CC); } } /* }}} */ int mmc_pool_open(mmc_pool_t *pool, mmc_t *mmc, mmc_stream_t *io, int udp TSRMLS_DC) /* connects if the stream is not already connected {{{ */ { switch (io->status) { case MMC_STATUS_CONNECTED: case MMC_STATUS_UNKNOWN: return MMC_OK; case MMC_STATUS_DISCONNECTED: case MMC_STATUS_FAILED: return mmc_server_connect(pool, mmc, io, udp TSRMLS_CC); } return MMC_REQUEST_FAILURE; } /* }}} */ mmc_t *mmc_pool_find_next(mmc_pool_t *pool, const char *key, unsigned int key_len, mmc_queue_t *skip_servers, unsigned int *last_index TSRMLS_DC) /* finds the next server in the failover sequence {{{ */ { mmc_t *mmc; char keytmp[MMC_MAX_KEY_LEN + MAX_LENGTH_OF_LONG + 1]; unsigned int keytmp_len; /* find the next server not present in the skip-list */ do { keytmp_len = sprintf(keytmp, "%s-%d", key, (*last_index)++); mmc = pool->hash->find_server(pool->hash_state, keytmp, keytmp_len TSRMLS_CC); } while (mmc_queue_contains(skip_servers, mmc) && *last_index < MEMCACHE_G(max_failover_attempts)); return mmc; } mmc_t *mmc_pool_find(mmc_pool_t *pool, const char *key, unsigned int key_len TSRMLS_DC) /* maps a key to a non-failed server {{{ */ { mmc_t *mmc = pool->hash->find_server(pool->hash_state, key, key_len TSRMLS_CC); /* check validity and try to failover otherwise */ if (!mmc_server_valid(mmc TSRMLS_CC) && MEMCACHE_G(allow_failover)) { unsigned int last_index = 0; do { mmc = mmc_pool_find_next(pool, key, key_len, NULL, &last_index TSRMLS_CC); } while (!mmc_server_valid(mmc TSRMLS_CC) && last_index < MEMCACHE_G(max_failover_attempts)); } return mmc; } /* }}} */ int mmc_pool_failover_handler(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param TSRMLS_DC) /* uses request->key to reschedule request to other server {{{ */ { if (MEMCACHE_G(allow_failover) && request->failed_index < MEMCACHE_G(max_failover_attempts) && request->failed_servers.len < pool->num_servers) { do { mmc_queue_push(&(request->failed_servers), mmc); mmc = mmc_pool_find_next(pool, request->key, request->key_len, &(request->failed_servers), &(request->failed_index) TSRMLS_CC); } while (!mmc_server_valid(mmc TSRMLS_CC) && request->failed_index < MEMCACHE_G(max_failover_attempts) && request->failed_servers.len < pool->num_servers); return mmc_pool_schedule(pool, mmc, request TSRMLS_CC); } mmc_pool_release(pool, request); return MMC_REQUEST_FAILURE; } /* }}}*/ int mmc_pool_failover_handler_null(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param TSRMLS_DC) /* always returns failure {{{ */ { mmc_pool_release(pool, request); return MMC_REQUEST_FAILURE; } /* }}}*/ static int mmc_pool_response_handler_null(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /* always returns done {{{ */ { return MMC_REQUEST_DONE; } /* }}}*/ static inline mmc_request_t *mmc_pool_request_alloc(mmc_pool_t *pool, int protocol, mmc_request_failover_handler failover_handler, void *failover_handler_param TSRMLS_DC) /* {{{ */ { mmc_request_t *request = mmc_queue_pop(&(pool->free_requests)); if (request == NULL) { request = pool->protocol->create_request(); } else { pool->protocol->reset_request(request); } request->protocol = protocol; if (protocol == MMC_PROTO_UDP) { mmc_udp_header_t header = {0}; smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); } request->failover_handler = failover_handler != NULL ? failover_handler : mmc_pool_failover_handler_null; request->failover_handler_param = failover_handler_param; return request; } /* }}} */ mmc_request_t *mmc_pool_request(mmc_pool_t *pool, int protocol, mmc_request_response_handler response_handler, void *response_handler_param, mmc_request_failover_handler failover_handler, void *failover_handler_param TSRMLS_DC) /* allocates a request, must be added to pool using mmc_pool_schedule or released with mmc_pool_release {{{ */ { mmc_request_t *request = mmc_pool_request_alloc(pool, protocol, failover_handler, failover_handler_param TSRMLS_CC); request->response_handler = response_handler; request->response_handler_param = response_handler_param; return request; } /* }}} */ mmc_request_t *mmc_pool_request_get(mmc_pool_t *pool, int protocol, mmc_request_value_handler value_handler, void *value_handler_param, mmc_request_failover_handler failover_handler, void *failover_handler_param TSRMLS_DC) /* allocates a request, must be added to pool using mmc_pool_schedule or released with mmc_pool_release {{{ */ { mmc_request_t *request = mmc_pool_request( pool, protocol, mmc_pool_response_handler_null, NULL, failover_handler, failover_handler_param TSRMLS_CC); request->value_handler = value_handler; request->value_handler_param = value_handler_param; return request; } /* }}} */ mmc_request_t *mmc_pool_clone_request(mmc_pool_t *pool, mmc_request_t *request TSRMLS_DC) /* clones a request, must be added to pool using mmc_pool_schedule or released with mmc_pool_release {{{ */ { mmc_request_t *clone = mmc_pool_request_alloc(pool, request->protocol, NULL, NULL TSRMLS_CC); clone->response_handler = request->response_handler; clone->response_handler_param = request->response_handler_param; clone->value_handler = request->value_handler; clone->value_handler_param = request->value_handler_param; /* copy payload parser */ clone->parse = request->parse; /* copy key */ memcpy(clone->key, request->key, request->key_len); clone->key_len = request->key_len; /* copy sendbuf */ mmc_buffer_alloc(&(clone->sendbuf), request->sendbuf.value.len); memcpy(clone->sendbuf.value.c, request->sendbuf.value.c, request->sendbuf.value.len); clone->sendbuf.value.len = request->sendbuf.value.len; /* copy protocol specific values */ pool->protocol->clone_request(clone, request); return clone; } /* }}} */ static int mmc_pool_slot_send(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, int handle_failover TSRMLS_DC) /* {{{ */ { if (request != NULL) { /* select protocol strategy and open connection */ if (request->protocol == MMC_PROTO_UDP && mmc->udp.port && request->sendbuf.value.len <= mmc->udp.chunk_size && mmc_pool_open(pool, mmc, &(mmc->udp), 1 TSRMLS_CC) == MMC_OK) { request->io = &(mmc->udp); request->read = mmc_request_read_udp; /* initialize udp header */ request->udp.reqid = mmc->reqid++; request->udp.seqid = 0; request->udp.total = 0; ((mmc_udp_header_t *)request->sendbuf.value.c)->reqid = htons(request->udp.reqid); ((mmc_udp_header_t *)request->sendbuf.value.c)->total = htons(1); } else if (mmc_pool_open(pool, mmc, &(mmc->tcp), 0 TSRMLS_CC) == MMC_OK) { /* skip udp header */ if (request->protocol == MMC_PROTO_UDP) { request->sendbuf.idx += sizeof(mmc_udp_header_t); } request->io = &(mmc->tcp); request->read = NULL; } else { mmc->sendreq = NULL; if (handle_failover) { return request->failover_handler(pool, mmc, request, request->failover_handler_param TSRMLS_CC); } return MMC_REQUEST_FAILURE; } } mmc->sendreq = request; return MMC_OK; } /* }}} */ int mmc_pool_schedule(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* schedules a request against a server, return MMC_OK on success {{{ */ { if (!mmc_server_valid(mmc TSRMLS_CC)) { /* delegate to failover handler if connect fails */ return request->failover_handler(pool, mmc, request, request->failover_handler_param TSRMLS_CC); } /* reset sendbuf to start position */ request->sendbuf.idx = 0; /* reset readbuf entirely */ mmc_buffer_reset(&(request->readbuf)); /* push request into sendreq slot if available */ if (mmc->sendreq == NULL) { if (mmc_pool_slot_send(pool, mmc, request, 0 TSRMLS_CC) != MMC_OK) { return request->failover_handler(pool, mmc, request, request->failover_handler_param TSRMLS_CC); } mmc_queue_push(pool->sending, mmc); } else { mmc_queue_push(&(mmc->sendqueue), request); } /* push request into readreq slot if available */ if (mmc->readreq == NULL) { mmc->readreq = request; mmc_queue_push(pool->reading, mmc); } else { mmc_queue_push(&(mmc->readqueue), request); } return MMC_OK; } /* }}} */ int mmc_pool_schedule_key(mmc_pool_t *pool, const char *key, unsigned int key_len, mmc_request_t *request, unsigned int redundancy TSRMLS_DC) /* schedules a request against a server selected by the provided key, return MMC_OK on success {{{ */ { if (redundancy > 1) { int i, result; mmc_t *mmc; unsigned int last_index = 0; mmc_queue_t skip_servers = {0}; /* schedule the first request */ mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC); result = mmc_pool_schedule(pool, mmc, request TSRMLS_CC); /* clone and schedule redundancy-1 additional requests */ for (i=0; i < redundancy-1 && i < pool->num_servers-1; i++) { mmc_queue_push(&skip_servers, mmc); mmc = mmc_pool_find_next(pool, key, key_len, &skip_servers, &last_index TSRMLS_CC); if (mmc_server_valid(mmc TSRMLS_CC)) { mmc_pool_schedule(pool, mmc, mmc_pool_clone_request(pool, request TSRMLS_CC) TSRMLS_CC); } } mmc_queue_free(&skip_servers); return result; } return mmc_pool_schedule(pool, mmc_pool_find(pool, key, key_len TSRMLS_CC), request TSRMLS_CC); } /* }}} */ int mmc_pool_schedule_get( mmc_pool_t *pool, int protocol, int op, zval *zkey, mmc_request_value_handler value_handler, void *value_handler_param, mmc_request_failover_handler failover_handler, void *failover_handler_param, mmc_request_t *failed_request TSRMLS_DC) /* schedules a get command against a server {{{ */ { mmc_t *mmc; char key[MMC_MAX_KEY_LEN + 1]; unsigned int key_len; if (mmc_prepare_key(zkey, key, &key_len) != MMC_OK) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid key"); return MMC_REQUEST_FAILURE; } mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC); if (!mmc_server_valid(mmc TSRMLS_CC)) { return MMC_REQUEST_FAILURE; } if (mmc->buildreq == NULL) { mmc_queue_push(&(pool->pending), mmc); mmc->buildreq = mmc_pool_request_get( pool, protocol, value_handler, value_handler_param, failover_handler, failover_handler_param TSRMLS_CC); if (failed_request != NULL) { mmc_queue_copy(&(mmc->buildreq->failed_servers), &(failed_request->failed_servers)); mmc->buildreq->failed_index = failed_request->failed_index; } pool->protocol->begin_get(mmc->buildreq, op); } else if (protocol == MMC_PROTO_UDP && mmc->buildreq->sendbuf.value.len + key_len + 3 > MMC_MAX_UDP_LEN) { /* datagram if full, schedule for delivery */ pool->protocol->end_get(mmc->buildreq); mmc_pool_schedule(pool, mmc, mmc->buildreq TSRMLS_CC); /* begin sending requests immediatly */ mmc_pool_select(pool TSRMLS_CC); mmc->buildreq = mmc_pool_request_get( pool, protocol, value_handler, value_handler_param, failover_handler, failover_handler_param TSRMLS_CC); if (failed_request != NULL) { mmc_queue_copy(&(mmc->buildreq->failed_servers), &(failed_request->failed_servers)); mmc->buildreq->failed_index = failed_request->failed_index; } pool->protocol->begin_get(mmc->buildreq, op); } pool->protocol->append_get(mmc->buildreq, zkey, key, key_len); return MMC_OK; } /* }}} */ static inline void mmc_pool_switch(mmc_pool_t *pool) { /* switch sending and reading queues */ if (pool->sending == &(pool->_sending1)) { pool->sending = &(pool->_sending2); pool->reading = &(pool->_reading2); } else { pool->sending = &(pool->_sending1); pool->reading = &(pool->_reading1); } /* reset queues so they can be re-populated */ mmc_queue_reset(pool->sending); mmc_queue_reset(pool->reading); } static int mmc_select_failure(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, int result TSRMLS_DC) /* {{{ */ { if (result == 0) { /* timeout expired, non-responsive server */ if (mmc_server_failure(mmc, request->io, "Network timeout", 0 TSRMLS_CC) == MMC_REQUEST_RETRY) { return MMC_REQUEST_RETRY; } } else { char buf[1024]; const char *message; long err = php_socket_errno(); if (err) { message = php_socket_strerror(err, buf, 1024); } else { message = "Unknown select() error"; } mmc_server_seterror(mmc, message, errno); } /* hard failure, deactivate connection */ mmc_server_deactivate(pool, mmc TSRMLS_CC); return MMC_REQUEST_FAILURE; } /* }}} */ static void mmc_select_retry(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* removes request from send/read queues and calls failover {{{ */ { /* clear out failed request from queues */ mmc_queue_remove(&(mmc->sendqueue), request); mmc_queue_remove(&(mmc->readqueue), request); /* shift next request into send slot */ if (mmc->sendreq == request) { mmc_pool_slot_send(pool, mmc, mmc_queue_pop(&(mmc->sendqueue)), 1 TSRMLS_CC); /* clear out connection from send queue if no new request was slotted */ if (!mmc->sendreq) { mmc_queue_remove(pool->sending, mmc); } } /* shift next request into read slot */ if (mmc->readreq == request) { mmc->readreq = mmc_queue_pop(&(mmc->readqueue)); /* clear out connection from read queue if no new request was slotted */ if (!mmc->readreq) { mmc_queue_remove(pool->reading, mmc); } } request->failover_handler(pool, mmc, request, request->failover_handler_param TSRMLS_CC); } /* }}} */ void mmc_pool_select(mmc_pool_t *pool TSRMLS_DC) /* runs one select() round on all scheduled requests {{{ */ { int i, fd, result; mmc_t *mmc; mmc_queue_t *sending, *reading; /* help complete previous run */ if (pool->in_select) { if (pool->sending == &(pool->_sending1)) { sending = &(pool->_sending2); reading = &(pool->_reading2); } else { sending = &(pool->_sending1); reading = &(pool->_reading1); } } else { int nfds = 0; struct timeval tv = pool->timeout; sending = pool->sending; reading = pool->reading; mmc_pool_switch(pool); FD_ZERO(&(pool->wfds)); FD_ZERO(&(pool->rfds)); for (i=0; i < sending->len; i++) { mmc = mmc_queue_item(sending, i); if (mmc->sendreq->io->fd > nfds) { nfds = mmc->sendreq->io->fd; } FD_SET(mmc->sendreq->io->fd, &(pool->wfds)); } for (i=0; i < reading->len; i++) { mmc = mmc_queue_item(reading, i); if (mmc->readreq->io->fd > nfds) { nfds = mmc->readreq->io->fd; } FD_SET(mmc->readreq->io->fd, &(pool->rfds)); } result = select(nfds + 1, &(pool->rfds), &(pool->wfds), NULL, &tv); /* if select timed out */ if (result <= 0) { for (i=0; i < sending->len; i++) { mmc = (mmc_t *)mmc_queue_item(sending, i); /* remove sending request */ if (!FD_ISSET(mmc->sendreq->io->fd, &(pool->wfds))) { mmc_queue_remove(sending, mmc); mmc_queue_remove(reading, mmc); i--; if (mmc_select_failure(pool, mmc, mmc->sendreq, result TSRMLS_CC) == MMC_REQUEST_RETRY) { /* allow request to try and send again */ mmc_select_retry(pool, mmc, mmc->sendreq TSRMLS_CC); } } } for (i=0; i < reading->len; i++) { mmc = (mmc_t *)mmc_queue_item(reading, i); /* remove reading request */ if (!FD_ISSET(mmc->readreq->io->fd, &(pool->rfds))) { mmc_queue_remove(sending, mmc); mmc_queue_remove(reading, mmc); i--; if (mmc_select_failure(pool, mmc, mmc->readreq, result TSRMLS_CC) == MMC_REQUEST_RETRY) { /* allow request to try and read again */ mmc_select_retry(pool, mmc, mmc->readreq TSRMLS_CC); } } } } pool->in_select = 1; } for (i=0; i < sending->len; i++) { mmc = mmc_queue_item(sending, i); /* skip servers which have failed */ if (!mmc->sendreq) { continue; } if (FD_ISSET(mmc->sendreq->io->fd, &(pool->wfds))) { fd = mmc->sendreq->io->fd; /* clear bit for reentrancy reasons */ FD_CLR(fd, &(pool->wfds)); /* until stream buffer is empty */ do { /* delegate to request send handler */ result = mmc_request_send(mmc, mmc->sendreq TSRMLS_CC); /* check if someone has helped complete our run */ if (!pool->in_select) { return; } switch (result) { case MMC_REQUEST_FAILURE: /* take server offline and failover requests */ mmc_server_deactivate(pool, mmc TSRMLS_CC); /* server is failed, remove from read queue */ mmc_queue_remove(reading, mmc); break; case MMC_REQUEST_RETRY: /* allow request to reschedule itself */ mmc_select_retry(pool, mmc, mmc->sendreq TSRMLS_CC); break; case MMC_REQUEST_DONE: /* shift next request into send slot */ mmc_pool_slot_send(pool, mmc, mmc_queue_pop(&(mmc->sendqueue)), 1 TSRMLS_CC); break; case MMC_REQUEST_MORE: /* send more data to socket */ break; default: php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid return value, bailing out"); } } while (mmc->sendreq != NULL && (result == MMC_REQUEST_DONE || result == MMC_REQUEST_AGAIN)); if (result == MMC_REQUEST_MORE) { /* add server to read queue once more */ mmc_queue_push(pool->sending, mmc); } } else { /* add server to send queue once more */ mmc_queue_push(pool->sending, mmc); } } for (i=0; i < reading->len; i++) { mmc = mmc_queue_item(reading, i); /* skip servers which have failed */ if (!mmc->readreq) { continue; } if (FD_ISSET(mmc->readreq->io->fd, &(pool->rfds))) { fd = mmc->readreq->io->fd; /* clear bit for reentrancy reasons */ FD_CLR(fd, &(pool->rfds)); /* fill read buffer if needed */ if (mmc->readreq->read != NULL) { result = mmc->readreq->read(mmc, mmc->readreq TSRMLS_CC); if (result != MMC_OK) { switch (result) { case MMC_REQUEST_FAILURE: /* take server offline and failover requests */ mmc_server_deactivate(pool, mmc TSRMLS_CC); break; case MMC_REQUEST_RETRY: /* allow request to reschedule itself */ mmc_select_retry(pool, mmc, mmc->readreq TSRMLS_CC); break; case MMC_REQUEST_MORE: /* add server to read queue once more */ mmc_queue_push(pool->reading, mmc); break; default: php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid return value, bailing out"); } /* skip to next request */ continue; } } /* until stream buffer is empty */ do { /* delegate to request response handler */ result = mmc->readreq->parse(mmc, mmc->readreq TSRMLS_CC); /* check if someone has helped complete our run */ if (!pool->in_select) { return; } switch (result) { case MMC_REQUEST_FAILURE: /* take server offline and failover requests */ mmc_server_deactivate(pool, mmc TSRMLS_CC); break; case MMC_REQUEST_RETRY: /* allow request to reschedule itself */ mmc_select_retry(pool, mmc, mmc->readreq TSRMLS_CC); break; case MMC_REQUEST_DONE: /* might have completed without having sent all data (e.g. object too large errors) */ if (mmc->sendreq == mmc->readreq) { /* disconnect stream since data may have been sent before we received the SERVER_ERROR */ mmc_server_disconnect(mmc, mmc->readreq->io TSRMLS_CC); /* shift next request into send slot */ mmc_pool_slot_send(pool, mmc, mmc_queue_pop(&(mmc->sendqueue)), 1 TSRMLS_CC); /* clear out connection from send queue if no new request was slotted */ if (!mmc->sendreq) { mmc_queue_remove(pool->sending, mmc); } } /* release completed request */ mmc_pool_release(pool, mmc->readreq); /* shift next request into read slot */ mmc->readreq = mmc_queue_pop(&(mmc->readqueue)); break; case MMC_REQUEST_MORE: /* read more data from socket */ if (php_stream_eof(mmc->readreq->io->stream)) { result = mmc_server_failure(mmc, mmc->readreq->io, "Read failed (socket was unexpectedly closed)", 0 TSRMLS_CC); if (result == MMC_REQUEST_FAILURE) { /* take server offline and failover requests */ mmc_server_deactivate(pool, mmc TSRMLS_CC); } } break; case MMC_REQUEST_AGAIN: /* request wants another loop */ break; default: php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid return value, bailing out"); } } while (mmc->readreq != NULL && (result == MMC_REQUEST_DONE || result == MMC_REQUEST_AGAIN)); if (result == MMC_REQUEST_MORE) { /* add server to read queue once more */ mmc_queue_push(pool->reading, mmc); } } else { /* add server to read queue once more */ mmc_queue_push(pool->reading, mmc); } } pool->in_select = 0; } /* }}} */ void mmc_pool_run(mmc_pool_t *pool TSRMLS_DC) /* runs all scheduled requests to completion {{{ */ { mmc_t *mmc; while ((mmc = mmc_queue_pop(&(pool->pending))) != NULL) { pool->protocol->end_get(mmc->buildreq); mmc_pool_schedule(pool, mmc, mmc->buildreq TSRMLS_CC); mmc->buildreq = NULL; } while (pool->reading->len || pool->sending->len) { mmc_pool_select(pool TSRMLS_CC); } } /* }}} */ inline int mmc_prepare_key_ex(const char *key, unsigned int key_len, char *result, unsigned int *result_len) /* {{{ */ { unsigned int i; if (key_len == 0) { return MMC_REQUEST_FAILURE; } *result_len = key_len < MMC_MAX_KEY_LEN ? key_len : MMC_MAX_KEY_LEN; result[*result_len] = '\0'; for (i=0; i<*result_len; i++) { result[i] = ((unsigned char)key[i]) > ' ' ? key[i] : '_'; } return MMC_OK; } /* }}} */ inline int mmc_prepare_key(zval *key, char *result, unsigned int *result_len) /* {{{ */ { if (Z_TYPE_P(key) == IS_STRING) { return mmc_prepare_key_ex(Z_STRVAL_P(key), Z_STRLEN_P(key), result, result_len); } else { int res; zval keytmp = *key; zval_copy_ctor(&keytmp); convert_to_string(&keytmp); res = mmc_prepare_key_ex(Z_STRVAL(keytmp), Z_STRLEN(keytmp), result, result_len); zval_dtor(&keytmp); return res; } } /* }}} */ /* * 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 */ memcache-3.0.8/memcache_queue.c0000644000076400007640000000677412130423646016313 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2004 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_queue.c 326387 2012-06-30 17:41:36Z ab $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "memcache_queue.h" MMC_QUEUE_INLINE void mmc_queue_push(mmc_queue_t *queue, void *ptr) { if (mmc_queue_contains(queue, ptr)) return; if (queue->len >= queue->alloc) { int increase = 1 + MMC_QUEUE_PREALLOC; queue->alloc += increase; queue->items = erealloc(queue->items, sizeof(*queue->items) * queue->alloc); /* move tail segment downwards */ if (queue->head < queue->tail) { memmove(queue->items + queue->tail + increase, queue->items + queue->tail, (queue->alloc - queue->tail - increase) * sizeof(*queue->items)); queue->tail += increase; } } if (queue->len) { queue->head++; if (queue->head >= queue->alloc) { queue->head = 0; } } queue->items[queue->head] = ptr; queue->len++; } MMC_QUEUE_INLINE void *mmc_queue_pop(mmc_queue_t *queue) { if (queue->len) { void *ptr; ptr = queue->items[queue->tail]; queue->len--; if (queue->len) { queue->tail++; if (queue->tail >= queue->alloc) { queue->tail = 0; } } return ptr; } return NULL; } MMC_QUEUE_INLINE int mmc_queue_contains(mmc_queue_t *queue, void *ptr) { if (queue != NULL) { int i; for (i=0; i < queue->len; i++) { if (mmc_queue_item(queue, i) == ptr) { return 1; } } } return 0; } MMC_QUEUE_INLINE void mmc_queue_free(mmc_queue_t *queue) { if (queue->items != NULL) { efree(queue->items); } memset(queue, 0, sizeof(*queue)); } MMC_QUEUE_INLINE void mmc_queue_copy(mmc_queue_t *target, mmc_queue_t *source) { if (target->alloc != source->alloc) { target->alloc = source->alloc; target->items = erealloc(target->items, sizeof(*target->items) * target->alloc); } memcpy(target->items, source->items, sizeof(*source->items) * source->alloc); target->head = source->head; target->tail = source->tail; target->len = source->len; } MMC_QUEUE_INLINE void mmc_queue_remove(mmc_queue_t *queue, void *ptr) { void *item; mmc_queue_t original = *queue; mmc_queue_release(queue); while ((item = mmc_queue_pop(&original)) != NULL) { if (item != ptr) { mmc_queue_push(queue, item); } } mmc_queue_free(&original); } /* * 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 */ memcache-3.0.8/memcache_session.c0000644000076400007640000003717612130423646016652 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2004 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_session.c 326387 2012-06-30 17:41:36Z ab $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "php.h" #include "php_ini.h" #include "php_variables.h" #include "SAPI.h" #include "ext/standard/php_smart_str.h" #include "ext/standard/url.h" #include "php_memcache.h" #ifdef PHP_WIN32 void usleep(int waitTime) { __int64 time1 = 0, time2 = 0, freq = 0; QueryPerformanceCounter((LARGE_INTEGER *) &time1); QueryPerformanceFrequency((LARGE_INTEGER *)&freq); do { QueryPerformanceCounter((LARGE_INTEGER *) &time2); } while((time2-time1) < waitTime); } #endif ZEND_EXTERN_MODULE_GLOBALS(memcache) ps_module ps_mod_memcache = { PS_MOD(memcache) }; /* {{{ PS_OPEN_FUNC */ PS_OPEN_FUNC(memcache) { mmc_pool_t *pool; mmc_t *mmc; php_url *url; zval *params, **param; int i, j, path_len; pool = mmc_pool_new(TSRMLS_C); for (i=0,j=0,path_len=strlen(save_path); iquery != NULL) { MAKE_STD_ZVAL(params); array_init(params); sapi_module.treat_data(PARSE_STRING, estrdup(url->query), params TSRMLS_CC); if (zend_hash_find(Z_ARRVAL_P(params), "persistent", sizeof("persistent"), (void **) ¶m) != FAILURE) { convert_to_boolean_ex(param); persistent = Z_BVAL_PP(param); } if (zend_hash_find(Z_ARRVAL_P(params), "udp_port", sizeof("udp_port"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); udp_port = Z_LVAL_PP(param); } if (zend_hash_find(Z_ARRVAL_P(params), "weight", sizeof("weight"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); weight = Z_LVAL_PP(param); } if (zend_hash_find(Z_ARRVAL_P(params), "timeout", sizeof("timeout"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); timeout = Z_LVAL_PP(param); } if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) ¶m) != FAILURE) { convert_to_long_ex(param); retry_interval = Z_LVAL_PP(param); } zval_ptr_dtor(¶ms); } if (url->scheme && url->path && !strcmp(url->scheme, "file")) { char *host; int host_len = spprintf(&host, 0, "unix://%s", url->path); /* chop off trailing :0 port specifier */ if (!strcmp(host + host_len - 2, ":0")) { host_len -= 2; } if (persistent) { mmc = mmc_find_persistent(host, host_len, 0, 0, timeout, retry_interval TSRMLS_CC); } else { mmc = mmc_server_new(host, host_len, 0, 0, 0, timeout, retry_interval TSRMLS_CC); } efree(host); } else { if (url->host == NULL || weight <= 0 || timeout <= 0) { php_url_free(url); mmc_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); return FAILURE; } if (persistent) { mmc = mmc_find_persistent(url->host, strlen(url->host), url->port, udp_port, timeout, retry_interval TSRMLS_CC); } else { mmc = mmc_server_new(url->host, strlen(url->host), url->port, udp_port, 0, timeout, retry_interval TSRMLS_CC); } } mmc_pool_add(pool, mmc, weight); php_url_free(url); } } if (pool->num_servers) { PS_SET_MOD_DATA(pool); return SUCCESS; } mmc_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); return FAILURE; } /* }}} */ /* {{{ PS_CLOSE_FUNC */ PS_CLOSE_FUNC(memcache) { mmc_pool_t *pool = PS_GET_MOD_DATA(); if (pool) { mmc_pool_free(pool TSRMLS_CC); PS_SET_MOD_DATA(NULL); } return SUCCESS; } /* }}} */ static int php_mmc_session_read_request( mmc_pool_t *pool, zval *zkey, zval **lockparam, zval *addparam, zval **dataparam, mmc_request_t **lockreq, mmc_request_t **addreq, mmc_request_t **datareq TSRMLS_DC) /* {{{ */ { mmc_request_t *lreq, *areq, *dreq; zval lockvalue; /* increment request which stores the response int using value_handler_single */ lreq = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_numeric_response_handler, lockparam[0], mmc_pool_failover_handler_null, NULL TSRMLS_CC); lreq->value_handler = mmc_value_handler_single; lreq->value_handler_param = lockparam; /* add request which should fail if lock has been incremented */ areq = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_stored_handler, addparam, mmc_pool_failover_handler_null, NULL TSRMLS_CC); /* request to fetch the session data */ dreq = mmc_pool_request_get( pool, MMC_PROTO_TCP, mmc_value_handler_single, dataparam, mmc_pool_failover_handler_null, NULL TSRMLS_CC); /* prepare key */ if (mmc_prepare_key_ex(Z_STRVAL_P(zkey), Z_STRLEN_P(zkey), dreq->key, &(dreq->key_len)) != MMC_OK) { mmc_pool_release(pool, lreq); mmc_pool_release(pool, areq); mmc_pool_release(pool, dreq); return MMC_REQUEST_FAILURE; } /* append .lock to key */ memcpy(lreq->key, dreq->key, dreq->key_len); strcpy(lreq->key + dreq->key_len, ".lock"); memcpy(areq->key, dreq->key, dreq->key_len); strcpy(areq->key + dreq->key_len, ".lock"); lreq->key_len = areq->key_len = dreq->key_len + sizeof(".lock")-1; /* value for add request */ ZVAL_LONG(&lockvalue, 1); /* build requests */ pool->protocol->mutate(lreq, zkey, lreq->key, lreq->key_len, 1, 1, 1, MEMCACHE_G(lock_timeout)); pool->protocol->store(pool, areq, MMC_OP_ADD, areq->key, areq->key_len, 0, MEMCACHE_G(lock_timeout), 0, &lockvalue TSRMLS_CC); pool->protocol->get(dreq, MMC_OP_GET, zkey, dreq->key, dreq->key_len); *lockreq = lreq; *addreq = areq; *datareq = dreq; return MMC_OK; } /* }}} */ /* {{{ PS_READ_FUNC */ PS_READ_FUNC(memcache) { mmc_pool_t *pool = PS_GET_MOD_DATA(); if (pool != NULL) { zval lockresult, addresult, dataresult, zkey; zval *lockparam[3]; zval *dataparam[3]; mmc_t *mmc; mmc_request_t *lockrequest, *addrequest, *datarequest; mmc_queue_t skip_servers = {0}; unsigned int last_index = 0, prev_index = 0, timeout = 5000; long remainingtime = MEMCACHE_G(lock_timeout) * 1000000 * 2; lockparam[0] = &lockresult; lockparam[1] = NULL; lockparam[2] = NULL; dataparam[0] = &dataresult; dataparam[1] = NULL; dataparam[2] = NULL; ZVAL_STRING(&zkey, (char *)key, 0); do { /* first request tries to increment lock */ ZVAL_NULL(&lockresult); /* second request tries to add lock, succeeds if lock doesn't exist (not relevant for binary * protocol where increment takes a default value */ ZVAL_NULL(&addresult); /* third request fetches the data, data is only valid if either of the lock requests succeeded */ ZVAL_NULL(&dataresult); /* create requests */ if (php_mmc_session_read_request(pool, &zkey, lockparam, &addresult, dataparam, &lockrequest, &addrequest, &datarequest TSRMLS_CC) != MMC_OK) { break; } /* find next server in line */ prev_index = last_index; mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index TSRMLS_CC); /* schedule the requests */ if (!mmc_server_valid(mmc TSRMLS_CC) || mmc_pool_schedule(pool, mmc, lockrequest TSRMLS_CC) != MMC_OK || /*pool->protocol != &mmc_binary_protocol && */mmc_pool_schedule(pool, mmc, addrequest TSRMLS_CC) != MMC_OK || mmc_pool_schedule(pool, mmc, datarequest TSRMLS_CC) != MMC_OK) { mmc_pool_release(pool, lockrequest); mmc_pool_release(pool, addrequest); mmc_pool_release(pool, datarequest); mmc_queue_push(&skip_servers, mmc); continue; } /* execute requests */ mmc_pool_run(pool TSRMLS_CC); if ((Z_TYPE(lockresult) == IS_LONG && Z_LVAL(lockresult) == 1) || (Z_TYPE(addresult) == IS_BOOL && Z_BVAL(addresult))) { if (Z_TYPE(dataresult) == IS_STRING) { /* break if successfully locked with existing value */ mmc_queue_free(&skip_servers); *val = Z_STRVAL(dataresult); *vallen = Z_STRLEN(dataresult); return SUCCESS; } /* if missing value, skip this server and try next */ zval_dtor(&dataresult); mmc_queue_push(&skip_servers, mmc); } else { /* if missing lock, back off and retry same server */ last_index = prev_index; usleep(timeout); remainingtime -= timeout; timeout *= 2; /* max value to usleep() is 1 second */ if (timeout > 1000000) { timeout = 1000000; } } } while (skip_servers.len < MEMCACHE_G(session_redundancy)-1 && skip_servers.len < pool->num_servers && remainingtime > 0); mmc_queue_free(&skip_servers); zval_dtor(&dataresult); } return FAILURE; } /* }}} */ /* {{{ PS_WRITE_FUNC */ PS_WRITE_FUNC(memcache) { mmc_pool_t *pool = PS_GET_MOD_DATA(); if (pool != NULL) { zval lockresult, dataresult, lockvalue, value; mmc_t *mmc; mmc_request_t *lockrequest, *datarequest; mmc_queue_t skip_servers = {0}; unsigned int last_index = 0; ZVAL_NULL(&lockresult); ZVAL_NULL(&dataresult); do { /* allocate requests */ datarequest = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_stored_handler, &dataresult, mmc_pool_failover_handler_null, NULL TSRMLS_CC); if (mmc_prepare_key_ex(key, strlen(key), datarequest->key, &(datarequest->key_len)) != MMC_OK) { mmc_pool_release(pool, datarequest); break; } /* append .lock to key */ lockrequest = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_stored_handler, &lockresult, mmc_pool_failover_handler_null, NULL TSRMLS_CC); memcpy(lockrequest->key, datarequest->key, datarequest->key_len); strcpy(lockrequest->key + datarequest->key_len, ".lock"); lockrequest->key_len = datarequest->key_len + sizeof(".lock")-1; ZVAL_LONG(&lockvalue, 0); ZVAL_STRINGL(&value, (char *)val, vallen, 0); /* assemble commands to store data and reset lock */ if (pool->protocol->store(pool, datarequest, MMC_OP_SET, datarequest->key, datarequest->key_len, 0, INI_INT("session.gc_maxlifetime"), 0, &value TSRMLS_CC) != MMC_OK || pool->protocol->store(pool, lockrequest, MMC_OP_SET, lockrequest->key, lockrequest->key_len, 0, MEMCACHE_G(lock_timeout), 0, &lockvalue TSRMLS_CC) != MMC_OK) { mmc_pool_release(pool, datarequest); mmc_pool_release(pool, lockrequest); break; } /* find next server in line */ mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index TSRMLS_CC); mmc_queue_push(&skip_servers, mmc); if (!mmc_server_valid(mmc TSRMLS_CC) || mmc_pool_schedule(pool, mmc, datarequest TSRMLS_CC) != MMC_OK || mmc_pool_schedule(pool, mmc, lockrequest TSRMLS_CC) != MMC_OK) { mmc_pool_release(pool, datarequest); mmc_pool_release(pool, lockrequest); continue; } } while (skip_servers.len < MEMCACHE_G(session_redundancy)-1 && skip_servers.len < pool->num_servers); mmc_queue_free(&skip_servers); /* execute requests */ mmc_pool_run(pool TSRMLS_CC); if (Z_BVAL(lockresult) && Z_BVAL(dataresult)) { return SUCCESS; } } return FAILURE; } /* }}} */ static int mmc_deleted_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC) /* parses a DELETED response line, param is a zval pointer to store result into {{{ */ { if (response == MMC_OK || response == MMC_RESPONSE_NOT_FOUND) { ZVAL_TRUE((zval *)param); return MMC_REQUEST_DONE; } if (response == MMC_RESPONSE_CLIENT_ERROR) { ZVAL_FALSE((zval *)param); php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)", mmc->host, mmc->tcp.port, mmc->udp.port, message, response); return MMC_REQUEST_DONE; } return mmc_request_failure(mmc, request->io, message, message_len, 0 TSRMLS_CC); } /* }}} */ /* {{{ PS_DESTROY_FUNC */ PS_DESTROY_FUNC(memcache) { mmc_pool_t *pool = PS_GET_MOD_DATA(); if (pool != NULL) { zval lockresult, dataresult; mmc_t *mmc; mmc_request_t *lockrequest, *datarequest; mmc_queue_t skip_servers = {0}; unsigned int last_index = 0; ZVAL_NULL(&lockresult); ZVAL_NULL(&dataresult); do { /* allocate requests */ datarequest = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_deleted_handler, &dataresult, mmc_pool_failover_handler_null, NULL TSRMLS_CC); if (mmc_prepare_key_ex(key, strlen(key), datarequest->key, &(datarequest->key_len)) != MMC_OK) { mmc_pool_release(pool, datarequest); break; } /* append .lock to key */ lockrequest = mmc_pool_request( pool, MMC_PROTO_TCP, mmc_deleted_handler, &lockresult, mmc_pool_failover_handler_null, NULL TSRMLS_CC); memcpy(lockrequest->key, datarequest->key, datarequest->key_len); strcpy(lockrequest->key + datarequest->key_len, ".lock"); lockrequest->key_len = datarequest->key_len + sizeof(".lock")-1; /* assemble commands to store data and reset lock */ pool->protocol->delete(datarequest, datarequest->key, datarequest->key_len, 0); pool->protocol->delete(lockrequest, lockrequest->key, lockrequest->key_len, 0); /* find next server in line */ mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index TSRMLS_CC); mmc_queue_push(&skip_servers, mmc); if (!mmc_server_valid(mmc TSRMLS_CC) || mmc_pool_schedule(pool, mmc, datarequest TSRMLS_CC) != MMC_OK || mmc_pool_schedule(pool, mmc, lockrequest TSRMLS_CC) != MMC_OK) { mmc_pool_release(pool, datarequest); mmc_pool_release(pool, lockrequest); continue; } } while (skip_servers.len < MEMCACHE_G(session_redundancy)-1 && skip_servers.len < pool->num_servers); mmc_queue_free(&skip_servers); /* execute requests */ mmc_pool_run(pool TSRMLS_CC); if (Z_BVAL(lockresult) && Z_BVAL(dataresult)) { return SUCCESS; } } return FAILURE; } /* }}} */ /* {{{ PS_GC_FUNC */ PS_GC_FUNC(memcache) { return SUCCESS; } /* }}} */ /* * 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 */ memcache-3.0.8/memcache_ascii_protocol.c0000644000076400007640000003245012130423646020166 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_ascii_protocol.c 329835 2013-03-19 22:39:10Z hradtke $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "memcache_pool.h" #include "ext/standard/php_smart_str.h" typedef struct mmc_ascii_request { mmc_request_t base; /* enable cast to mmc_request_t */ struct { /* stores value info while the body is being read */ char key[MMC_MAX_KEY_LEN + 1]; unsigned int flags; unsigned long length; unsigned long cas; /* CAS counter */ } value; } mmc_ascii_request_t; static int mmc_server_read_value(mmc_t *, mmc_request_t * TSRMLS_DC); static int mmc_stream_get_line(mmc_stream_t *io, char **line TSRMLS_DC) /* attempts to read a line from server, returns the line size or 0 if no complete line was available {{{ */ { size_t returned_len = 0; io->readline(io, io->input.value + io->input.idx, MMC_BUFFER_SIZE - io->input.idx, &returned_len TSRMLS_CC); io->input.idx += returned_len; if (io->input.idx && io->input.value[io->input.idx - 1] == '\n') { int result = io->input.idx; *line = io->input.value; io->input.idx = 0; return result; } return 0; } /* }}} */ static int mmc_request_check_response(const char *line, int line_len) /* checks for response status and error codes {{{ */ { int response; if (mmc_str_left(line, "OK", line_len, sizeof("OK")-1) || mmc_str_left(line, "STORED", line_len, sizeof("STORED")-1) || mmc_str_left(line, "DELETED", line_len, sizeof("DELETED")-1)) { response = MMC_OK; } else if (mmc_str_left(line, "NOT_FOUND", line_len, sizeof("NOT_FOUND")-1)) { response = MMC_RESPONSE_NOT_FOUND; } else if ( mmc_str_left(line, "NOT_STORED", line_len, sizeof("NOT_STORED")-1) || mmc_str_left(line, "EXISTS", line_len, sizeof("EXISTS")-1)) { response = MMC_RESPONSE_EXISTS; } else if (mmc_str_left(line, "SERVER_ERROR out of memory", line_len, sizeof("SERVER_ERROR out of memory")-1)) { response = MMC_RESPONSE_OUT_OF_MEMORY; } else if (mmc_str_left(line, "SERVER_ERROR object too large", line_len, sizeof("SERVER_ERROR object too large")-1)) { response = MMC_RESPONSE_TOO_LARGE; } else if ( mmc_str_left(line, "ERROR", line_len, sizeof("ERROR")-1) || mmc_str_left(line, "SERVER_ERROR", line_len, sizeof("SERVER_ERROR")-1)) { response = MMC_RESPONSE_ERROR; } else if (mmc_str_left(line, "CLIENT_ERROR", line_len, sizeof("CLIENT_ERROR")-1)) { response = MMC_RESPONSE_CLIENT_ERROR; } else { response = MMC_RESPONSE_UNKNOWN; } return response; } static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* reads a generic response header and delegates it to response_handler {{{ */ { char *line; int line_len = mmc_stream_get_line(request->io, &line TSRMLS_CC); if (line_len > 0) { int response = mmc_request_check_response(line, line_len); return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param TSRMLS_CC); } return MMC_REQUEST_MORE; } /* }}}*/ static int mmc_request_parse_mutate(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* reads and parses the response header {{{ */ { char *line; int line_len; line_len = mmc_stream_get_line(request->io, &line TSRMLS_CC); if (line_len > 0) { long lval; zval *value; int response = mmc_request_check_response(line, line_len); if (response != MMC_RESPONSE_UNKNOWN) { return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param TSRMLS_CC); } if (sscanf(line, "%lu", &lval) < 1) { return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0 TSRMLS_CC); } MAKE_STD_ZVAL(value); ZVAL_LONG(value, lval); return request->value_handler(request->key, request->key_len, value, 0, 0, request->value_handler_param TSRMLS_CC); } return MMC_REQUEST_MORE; } /* }}}*/ static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* reads and parses the VALUE response header and then reads the value body {{{ */ { char *line; int line_len; mmc_ascii_request_t *req = (mmc_ascii_request_t *)request; line_len = mmc_stream_get_line(request->io, &line TSRMLS_CC); if (line_len > 0) { if (mmc_str_left(line, "END", line_len, sizeof("END")-1)) { return MMC_REQUEST_DONE; } if (sscanf(line, MMC_VALUE_HEADER, req->value.key, &(req->value.flags), &(req->value.length), &(req->value.cas)) < 3) { return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0 TSRMLS_CC); } /* memory for data + \r\n */ mmc_buffer_alloc(&(request->readbuf), req->value.length + 2); /* allow read_value handler to read the value body */ request->parse = mmc_server_read_value; /* read more, php streams buffer input which must be read if available */ return MMC_REQUEST_AGAIN; } return MMC_REQUEST_MORE; } /* }}}*/ static int mmc_server_read_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* read the value body into the buffer {{{ */ { mmc_ascii_request_t *req = (mmc_ascii_request_t *)request; request->readbuf.idx += request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length + 2 - request->readbuf.idx TSRMLS_CC); /* done reading? */ if (request->readbuf.idx >= req->value.length + 2) { int result; /* allow parse_value to read next VALUE or END line */ request->parse = mmc_request_parse_value; mmc_buffer_reset(&(request->readbuf)); result = mmc_unpack_value( mmc, request, &(request->readbuf), req->value.key, strlen(req->value.key), req->value.flags, req->value.cas, req->value.length TSRMLS_CC); /* request more data (END line) */ if (result == MMC_REQUEST_DONE) { return MMC_REQUEST_AGAIN; } return result; } return MMC_REQUEST_MORE; } /* }}}*/ static mmc_request_t *mmc_ascii_create_request() /* {{{ */ { mmc_ascii_request_t *request = emalloc(sizeof(mmc_ascii_request_t)); memset(request, 0, sizeof(*request)); return (mmc_request_t *)request; } /* }}} */ static void mmc_ascii_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */ {} /* }}} */ static void mmc_ascii_reset_request(mmc_request_t *request) /* {{{ */ { mmc_ascii_request_t *req = (mmc_ascii_request_t *)request; req->value.cas = 0; mmc_request_reset(request); } /* }}} */ static void mmc_ascii_begin_get(mmc_request_t *request, int op) /* {{{ */ { request->parse = mmc_request_parse_value; if (op == MMC_OP_GETS) { smart_str_appendl(&(request->sendbuf.value), "gets", sizeof("gets")-1); } else { smart_str_appendl(&(request->sendbuf.value), "get", sizeof("get")-1); } } /* }}} */ static void mmc_ascii_append_get(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len) /* {{{ */ { smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_appendl(&(request->sendbuf.value), key, key_len); } /* }}} */ static void mmc_ascii_end_get(mmc_request_t *request) /* {{{ */ { smart_str_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); } /* }}} */ static void mmc_ascii_get(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len) /* {{{ */ { mmc_ascii_begin_get(request, op); mmc_ascii_append_get(request, zkey, key, key_len); mmc_ascii_end_get(request); } /* }}} */ static int mmc_ascii_store( mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len, unsigned int flags, unsigned int exptime, unsigned long cas, zval *value TSRMLS_DC) /* {{{ */ { int status; mmc_buffer_t buffer; request->parse = mmc_request_parse_response; memset(&buffer, 0, sizeof(buffer)); status = mmc_pack_value(pool, &buffer, value, &flags TSRMLS_CC); if (status != MMC_OK) { return status; } switch (op) { case MMC_OP_SET: smart_str_appendl(&(request->sendbuf.value), "set", sizeof("set")-1); break; case MMC_OP_ADD: smart_str_appendl(&(request->sendbuf.value), "add", sizeof("add")-1); break; case MMC_OP_REPLACE: smart_str_appendl(&(request->sendbuf.value), "replace", sizeof("replace")-1); break; case MMC_OP_CAS: smart_str_appendl(&(request->sendbuf.value), "cas", sizeof("cas")-1); break; case MMC_OP_APPEND: smart_str_appendl(&(request->sendbuf.value), "append", sizeof("append")-1); break; case MMC_OP_PREPEND: smart_str_appendl(&(request->sendbuf.value), "prepend", sizeof("prepend")-1); break; default: return MMC_REQUEST_FAILURE; } smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_appendl(&(request->sendbuf.value), key, key_len); smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_append_unsigned(&(request->sendbuf.value), flags); smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_append_unsigned(&(request->sendbuf.value), exptime); smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_append_unsigned(&(request->sendbuf.value), buffer.value.len); if (op == MMC_OP_CAS) { smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_append_unsigned(&(request->sendbuf.value), cas); } smart_str_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); smart_str_appendl(&(request->sendbuf.value), buffer.value.c, buffer.value.len); smart_str_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); mmc_buffer_free(&buffer); return MMC_OK; } /* }}} */ static void mmc_ascii_delete(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime) /* {{{ */ { request->parse = mmc_request_parse_response; smart_str_appendl(&(request->sendbuf.value), "delete", sizeof("delete")-1); smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_appendl(&(request->sendbuf.value), key, key_len); if (exptime > 0) { smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_append_unsigned(&(request->sendbuf.value), exptime); } smart_str_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); } /* }}} */ static void mmc_ascii_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime) /* {{{ */ { request->parse = mmc_request_parse_mutate; if (value >= 0) { smart_str_appendl(&(request->sendbuf.value), "incr", sizeof("incr")-1); } else { smart_str_appendl(&(request->sendbuf.value), "decr", sizeof("decr")-1); } smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_appendl(&(request->sendbuf.value), key, key_len); smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_append_unsigned(&(request->sendbuf.value), value >= 0 ? value : -value); smart_str_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); } /* }}} */ static void mmc_ascii_flush(mmc_request_t *request, unsigned int exptime) /* {{{ */ { request->parse = mmc_request_parse_response; smart_str_appendl(&(request->sendbuf.value), "flush_all", sizeof("flush_all")-1); if (exptime > 0) { smart_str_appendl(&(request->sendbuf.value), " ", 1); smart_str_append_unsigned(&(request->sendbuf.value), exptime); } smart_str_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1); } /* }}} */ static void mmc_ascii_version(mmc_request_t *request) /* {{{ */ { request->parse = mmc_request_parse_response; smart_str_appendl(&(request->sendbuf.value), "version\r\n", sizeof("version\r\n")-1); } /* }}} */ static void mmc_ascii_stats(mmc_request_t *request, const char *type, long slabid, long limit) /* {{{ */ { char *cmd; unsigned int cmd_len; request->parse = mmc_request_parse_response; if (slabid) { cmd_len = spprintf(&cmd, 0, "stats %s %ld %ld\r\n", type, slabid, limit); } else if (type) { cmd_len = spprintf(&cmd, 0, "stats %s\r\n", type); } else { cmd_len = spprintf(&cmd, 0, "stats\r\n"); } smart_str_appendl(&(request->sendbuf.value), cmd, cmd_len); efree(cmd); } /* }}} */ mmc_protocol_t mmc_ascii_protocol = { mmc_ascii_create_request, mmc_ascii_clone_request, mmc_ascii_reset_request, mmc_request_free, mmc_ascii_get, mmc_ascii_begin_get, mmc_ascii_append_get, mmc_ascii_end_get, mmc_ascii_store, mmc_ascii_delete, mmc_ascii_mutate, mmc_ascii_flush, mmc_ascii_version, mmc_ascii_stats }; /* * 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 */ memcache-3.0.8/memcache_binary_protocol.c0000644000076400007640000004535012130423646020365 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_binary_protocol.c 329835 2013-03-19 22:39:10Z hradtke $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef PHP_WIN32 #include #include #else #include #include #include #endif #include "memcache_pool.h" #include "ext/standard/php_smart_str.h" #if __BYTE_ORDER == __BIG_ENDIAN # define ntohll(x) (x) # define htonll(x) (x) #elif __BYTE_ORDER == __LITTLE_ENDIAN # include # define ntohll(x) bswap_64(x) # define htonll(x) bswap_64(x) #else # error "Could not determine byte order: __BYTE_ORDER uncorrectly defined" #endif #define MMC_REQUEST_MAGIC 0x80 #define MMC_RESPONSE_MAGIC 0x81 #define MMC_OP_GET 0x00 #define MMC_OP_SET 0x01 #define MMC_OP_ADD 0x02 #define MMC_OP_REPLACE 0x03 #define MMC_OP_DELETE 0x04 #define MMC_OP_INCR 0x05 #define MMC_OP_DECR 0x06 #define MMC_OP_QUIT 0x07 #define MMC_OP_FLUSH 0x08 #define MMC_OP_GETQ 0x09 #define MMC_OP_NOOP 0x0a #define MMC_OP_VERSION 0x0b typedef struct mmc_binary_request { mmc_request_t base; /* enable cast to mmc_request_t */ mmc_request_parser next_parse_handler; /* next payload parser state */ mmc_queue_t keys; /* mmc_queue_t, reqid -> key mappings */ struct { uint8_t opcode; uint8_t error; /* error received in current request */ uint32_t reqid; /* current reqid being processed */ } command; struct { /* stores value info while the body is being read */ unsigned int flags; unsigned long length; uint64_t cas; /* CAS counter */ } value; } mmc_binary_request_t; typedef struct mmc_request_header { uint8_t magic; uint8_t opcode; uint16_t key_len; uint8_t extras_len; uint8_t datatype; uint16_t _reserved; uint32_t length; /* trailing body length (not including this header) */ uint32_t reqid; /* opaque request id */ } mmc_request_header_t; typedef struct mmc_store_request_header { mmc_request_header_t base; uint64_t cas; uint32_t flags; uint32_t exptime; } mmc_store_request_header_t; typedef struct mmc_delete_request_header { mmc_request_header_t base; uint32_t exptime; } mmc_delete_request_header_t; typedef struct mmc_mutate_request_header { mmc_request_header_t base; uint64_t value; uint64_t defval; uint32_t exptime; } mmc_mutate_request_header_t; typedef struct mmc_response_header { uint8_t magic; uint8_t opcode; uint16_t error; uint8_t extras_len; uint8_t datatype; uint16_t _reserved; uint32_t length; /* trailing body length (not including this header) */ uint32_t reqid; /* echo'ed from request */ } mmc_response_header_t; typedef struct mmc_get_response_header { uint64_t cas; uint32_t flags; } mmc_get_response_header_t; typedef struct mmc_mutate_response_header { uint64_t value; } mmc_mutate_response_header_t; static int mmc_request_read_response(mmc_t *, mmc_request_t * TSRMLS_DC); static int mmc_request_parse_value(mmc_t *, mmc_request_t * TSRMLS_DC); static int mmc_request_read_value(mmc_t *, mmc_request_t * TSRMLS_DC); static inline char *mmc_stream_get(mmc_stream_t *io, size_t bytes TSRMLS_DC) /* attempts to read a number of bytes from server, returns the a pointer to the buffer on success, NULL if the complete number of bytes could not be read {{{ */ { io->input.idx += io->read(io, io->input.value + io->input.idx, bytes - io->input.idx TSRMLS_CC); if (io->input.idx >= bytes) { io->input.idx = 0; return io->input.value; } return NULL; } /* }}} */ static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* reads a generic response header and reads the response body {{{ */ { mmc_response_header_t *header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; header = (mmc_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC); if (header != NULL) { if (header->magic != MMC_RESPONSE_MAGIC) { return mmc_server_failure(mmc, request->io, "Malformed server response (invalid magic byte)", 0 TSRMLS_CC); } if (header->opcode == MMC_OP_NOOP) { return MMC_REQUEST_DONE; } req->command.opcode = header->opcode; req->command.error = ntohs(header->error); req->command.reqid = ntohl(header->reqid); req->value.length = ntohl(header->length); if (req->value.length == 0) { return request->response_handler(mmc, request, req->command.error, "", 0, request->response_handler_param TSRMLS_CC); } /* allow read_response handler to read the response body */ if (req->command.error) { request->parse = mmc_request_read_response; mmc_buffer_alloc(&(request->readbuf), req->value.length + 1); } else { request->parse = req->next_parse_handler; if (req->value.length >= header->extras_len) { req->value.length -= header->extras_len; } mmc_buffer_alloc(&(request->readbuf), req->value.length + 1); } /* read more, php streams buffer input which must be read if available */ return MMC_REQUEST_AGAIN; } return MMC_REQUEST_MORE; } /* }}}*/ static int mmc_request_parse_null(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* always returns MMC_REQUEST_DONE {{{ */ { return MMC_REQUEST_DONE; } static int mmc_request_read_response(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* read the response body into the buffer and delegates to response_handler {{{ */ { mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->readbuf.idx += request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length - request->readbuf.idx TSRMLS_CC); /* done reading? */ if (request->readbuf.idx >= req->value.length) { request->readbuf.value.c[req->value.length] = '\0'; return request->response_handler(mmc, request, req->command.error, request->readbuf.value.c, req->value.length, request->response_handler_param TSRMLS_CC); } return MMC_REQUEST_MORE; } /* }}}*/ static int mmc_request_read_mutate(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* reads and parses the mutate response header {{{ */ { mmc_mutate_response_header_t *header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; header = (mmc_mutate_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC); if (header != NULL) { int result; zval *key, *value; /* convert remembered key to string and unpack value */ key = (zval *)mmc_queue_item(&(req->keys), req->command.reqid); MAKE_STD_ZVAL(value); ZVAL_LONG(value, ntohll(header->value)); if (Z_TYPE_P(key) != IS_STRING) { zval keytmp = *key; zval_copy_ctor(&keytmp); INIT_PZVAL(&keytmp); convert_to_string(&keytmp); result = request->value_handler( Z_STRVAL(keytmp), Z_STRLEN(keytmp), value, req->value.flags, req->value.cas, request->value_handler_param TSRMLS_CC); zval_dtor(&keytmp); } else { result = request->value_handler( Z_STRVAL_P(key), Z_STRLEN_P(key), value, req->value.flags, req->value.cas, request->value_handler_param TSRMLS_CC); } return result; } return MMC_REQUEST_MORE; } /* }}}*/ static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* reads and parses the value response header and then reads the value body {{{ */ { mmc_get_response_header_t *header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; header = (mmc_get_response_header_t *)mmc_stream_get(request->io, sizeof(*header) TSRMLS_CC); if (header != NULL) { req->value.cas = ntohll(header->cas); req->value.flags = ntohl(header->flags); /* allow read_value handler to read the value body */ request->parse = mmc_request_read_value; /* read more, php streams buffer input which must be read if available */ return MMC_REQUEST_AGAIN; } return MMC_REQUEST_MORE; } /* }}}*/ static int mmc_request_read_value(mmc_t *mmc, mmc_request_t *request TSRMLS_DC) /* read the value body into the buffer {{{ */ { mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->readbuf.idx += request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length - request->readbuf.idx TSRMLS_CC); /* done reading? */ if (request->readbuf.idx >= req->value.length) { zval *key; int result; /* allow parse_value to read next VALUE or NOOP, done here to ensure reentrancy */ if (req->command.opcode == MMC_OP_GET) { request->parse = mmc_request_parse_null; } else { request->parse = mmc_request_parse_response; } mmc_buffer_reset(&(request->readbuf)); /* convert remembered key to string and unpack value */ key = (zval *)mmc_queue_item(&(req->keys), req->command.reqid); if (Z_TYPE_P(key) != IS_STRING) { zval keytmp = *key; zval_copy_ctor(&keytmp); INIT_PZVAL(&keytmp); convert_to_string(&keytmp); result = mmc_unpack_value( mmc, request, &(request->readbuf), Z_STRVAL(keytmp), Z_STRLEN(keytmp), req->value.flags, req->value.cas, req->value.length TSRMLS_CC); zval_dtor(&keytmp); } else { result = mmc_unpack_value( mmc, request, &(request->readbuf), Z_STRVAL_P(key), Z_STRLEN_P(key), req->value.flags, req->value.cas, req->value.length TSRMLS_CC); } if (result == MMC_REQUEST_DONE && (req->command.opcode == MMC_OP_GET || req->command.reqid >= req->keys.len)) { return MMC_REQUEST_DONE; } return MMC_REQUEST_AGAIN; } return MMC_REQUEST_MORE; } /* }}}*/ static inline void mmc_pack_header(mmc_request_header_t *header, uint8_t opcode, unsigned int reqid, unsigned int key_len, unsigned int extras_len, unsigned int length) /* {{{ */ { header->magic = MMC_REQUEST_MAGIC; header->opcode = opcode; header->key_len = htons(key_len); header->extras_len = extras_len; header->datatype = 0; header->_reserved = 0; header->length = htonl(key_len + extras_len + length); header->reqid = htonl(reqid); } /* }}} */ static mmc_request_t *mmc_binary_create_request() /* {{{ */ { mmc_binary_request_t *request = emalloc(sizeof(mmc_binary_request_t)); memset(request, 0, sizeof(*request)); return (mmc_request_t *)request; } /* }}} */ static void mmc_binary_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */ { mmc_binary_request_t *rcl = (mmc_binary_request_t *)clone; mmc_binary_request_t *req = (mmc_binary_request_t *)request; rcl->next_parse_handler = req->next_parse_handler; mmc_queue_copy(&(rcl->keys), &(req->keys)); } /* }}} */ static void mmc_binary_reset_request(mmc_request_t *request) /* {{{ */ { mmc_binary_request_t *req = (mmc_binary_request_t *)request; mmc_queue_reset(&(req->keys)); req->value.cas = 0; mmc_request_reset(request); } /* }}} */ static void mmc_binary_free_request(mmc_request_t *request) /* {{{ */ { mmc_binary_request_t *req = (mmc_binary_request_t *)request; mmc_queue_free(&(req->keys)); mmc_request_free(request); } /* }}} */ static void mmc_binary_begin_get(mmc_request_t *request, int op) /* {{{ */ { mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_parse_value; } /* }}} */ static void mmc_binary_append_get(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len) /* {{{ */ { mmc_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; /* reqid/opaque is the index into the collection of key pointers */ mmc_pack_header(&header, MMC_OP_GETQ, req->keys.len, key_len, 0, 0); smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); smart_str_appendl(&(request->sendbuf.value), key, key_len); /* store key to be used by the response handler */ mmc_queue_push(&(req->keys), zkey); } /* }}} */ static void mmc_binary_end_get(mmc_request_t *request) /* {{{ */ { mmc_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; mmc_pack_header(&header, MMC_OP_NOOP, req->keys.len, 0, 0, 0); smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); } /* }}} */ static void mmc_binary_get(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len) /* {{{ */ { mmc_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_parse_value; /* reqid/opaque is the index into the collection of key pointers */ mmc_pack_header(&header, MMC_OP_GET, req->keys.len, key_len, 0, 0); smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); smart_str_appendl(&(request->sendbuf.value), key, key_len); /* store key to be used by the response handler */ mmc_queue_push(&(req->keys), zkey); } /* }}} */ static int mmc_binary_store( mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len, unsigned int flags, unsigned int exptime, unsigned long cas, zval *value TSRMLS_DC) /* {{{ */ { int status, prevlen, valuelen; mmc_store_request_header_t *header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_read_response; prevlen = request->sendbuf.value.len; /* allocate space for header */ mmc_buffer_alloc(&(request->sendbuf), sizeof(*header)); request->sendbuf.value.len += sizeof(*header); /* append key and data */ smart_str_appendl(&(request->sendbuf.value), key, key_len); valuelen = request->sendbuf.value.len; status = mmc_pack_value(pool, &(request->sendbuf), value, &flags TSRMLS_CC); if (status != MMC_OK) { return status; } /* initialize header */ header = (mmc_store_request_header_t *)(request->sendbuf.value.c + prevlen); switch (op) { case MMC_OP_CAS: op = MMC_OP_SET; break; case MMC_OP_APPEND: case MMC_OP_PREPEND: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Binary protocol doesn't support append/prepend"); return MMC_REQUEST_FAILURE; } mmc_pack_header(&(header->base), op, 0, key_len, sizeof(*header) - sizeof(header->base), request->sendbuf.value.len - valuelen); header->cas = htonll(cas); header->flags = htonl(flags); header->exptime = htonl(exptime); return MMC_OK; } /* }}} */ static void mmc_binary_delete(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime) /* {{{ */ { mmc_delete_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_read_response; mmc_pack_header(&(header.base), MMC_OP_DELETE, 0, key_len, sizeof(header) - sizeof(header.base), 0); header.exptime = htonl(exptime); smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); smart_str_appendl(&(request->sendbuf.value), key, key_len); } /* }}} */ static void mmc_binary_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime) /* {{{ */ { mmc_mutate_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_read_mutate; if (value >= 0) { mmc_pack_header(&(header.base), MMC_OP_INCR, req->keys.len, key_len, sizeof(header) - sizeof(header.base), 0); header.value = htonll(value); } else { mmc_pack_header(&(header.base), MMC_OP_DECR, req->keys.len, key_len, sizeof(header) - sizeof(header.base), 0); header.value = htonll(-value); } header.defval = htonll(defval); if (defval_used) { /* server inserts defval if key doesn't exist */ header.exptime = htonl(exptime); } else { /* server replies with NOT_FOUND if exptime ~0 and key doesn't exist */ header.exptime = ~(uint32_t)0; } smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); smart_str_appendl(&(request->sendbuf.value), key, key_len); /* store key to be used by the response handler */ mmc_queue_push(&(req->keys), zkey); } /* }}} */ static void mmc_binary_flush(mmc_request_t *request, unsigned int exptime) /* {{{ */ { mmc_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_read_response; mmc_pack_header(&header, MMC_OP_FLUSH, 0, 0, 0, 0); smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); } /* }}} */ static void mmc_binary_version(mmc_request_t *request) /* {{{ */ { mmc_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_read_response; mmc_pack_header(&header, MMC_OP_VERSION, 0, 0, 0, 0); smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); } /* }}} */ static void mmc_binary_stats(mmc_request_t *request, const char *type, long slabid, long limit) /* {{{ */ { /* stats not supported */ mmc_request_header_t header; mmc_binary_request_t *req = (mmc_binary_request_t *)request; request->parse = mmc_request_parse_response; req->next_parse_handler = mmc_request_read_response; mmc_pack_header(&header, MMC_OP_NOOP, 0, 0, 0, 0); smart_str_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header)); } /* }}} */ mmc_protocol_t mmc_binary_protocol = { mmc_binary_create_request, mmc_binary_clone_request, mmc_binary_reset_request, mmc_binary_free_request, mmc_binary_get, mmc_binary_begin_get, mmc_binary_append_get, mmc_binary_end_get, mmc_binary_store, mmc_binary_delete, mmc_binary_mutate, mmc_binary_flush, mmc_binary_version, mmc_binary_stats }; /* * 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 */ memcache-3.0.8/memcache_standard_hash.c0000644000076400007640000000603512130423646017760 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_standard_hash.c 273595 2009-01-15 17:26:16Z mikl $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_memcache.h" ZEND_EXTERN_MODULE_GLOBALS(memcache) typedef struct mmc_standard_state { int num_servers; mmc_t **buckets; int num_buckets; mmc_hash_function_t *hash; } mmc_standard_state_t; void *mmc_standard_create_state(mmc_hash_function_t *hash) /* {{{ */ { mmc_standard_state_t *state = emalloc(sizeof(mmc_standard_state_t)); memset(state, 0, sizeof(mmc_standard_state_t)); state->hash = hash; return state; } /* }}} */ void mmc_standard_free_state(void *s) /* {{{ */ { mmc_standard_state_t *state = s; if (state != NULL) { if (state->buckets != NULL) { efree(state->buckets); } efree(state); } } /* }}} */ mmc_t *mmc_standard_find_server(void *s, const char *key, unsigned int key_len TSRMLS_DC) /* {{{ */ { mmc_standard_state_t *state = s; if (state->num_servers > 1) { /* "new-style" hash */ unsigned int hash = (mmc_hash(state->hash, key, key_len) >> 16) & 0x7fff; return state->buckets[(hash ? hash : 1) % state->num_buckets]; } return state->buckets[0]; } /* }}} */ void mmc_standard_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */ { mmc_standard_state_t *state = s; int i; /* add weight number of buckets for this server */ state->buckets = erealloc(state->buckets, sizeof(*state->buckets) * (state->num_buckets + weight)); for (i=0; ibuckets[state->num_buckets + i] = mmc; } state->num_buckets += weight; state->num_servers++; } /* }}} */ mmc_hash_strategy_t mmc_standard_hash = { mmc_standard_create_state, mmc_standard_free_state, mmc_standard_find_server, mmc_standard_add_server }; /* * 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 */ memcache-3.0.8/memcache_consistent_hash.c0000644000076400007640000001231312130423646020345 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_consistent_hash.c 273595 2009-01-15 17:26:16Z mikl $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "php.h" #include "php_memcache.h" ZEND_EXTERN_MODULE_GLOBALS(memcache) typedef struct mmc_consistent_point { mmc_t *server; unsigned int point; } mmc_consistent_point_t; typedef struct mmc_consistent_state { int num_servers; mmc_consistent_point_t *points; int num_points; mmc_t *buckets[MMC_CONSISTENT_BUCKETS]; int buckets_populated; mmc_hash_function_t *hash; } mmc_consistent_state_t; void *mmc_consistent_create_state(mmc_hash_function_t *hash) /* {{{ */ { mmc_consistent_state_t *state = emalloc(sizeof(mmc_consistent_state_t)); memset(state, 0, sizeof(mmc_consistent_state_t)); state->hash = hash; return state; } /* }}} */ void mmc_consistent_free_state(void *s) /* {{{ */ { mmc_consistent_state_t *state = s; if (state != NULL) { if (state->points != NULL) { efree(state->points); } efree(state); } } /* }}} */ static int mmc_consistent_compare(const void *a, const void *b) /* {{{ */ { if (((mmc_consistent_point_t *)a)->point < ((mmc_consistent_point_t *)b)->point) { return -1; } if (((mmc_consistent_point_t *)a)->point > ((mmc_consistent_point_t *)b)->point) { return 1; } return 0; } /* }}} */ static mmc_t *mmc_consistent_find(mmc_consistent_state_t *state, unsigned int point) /* {{{ */ { int lo = 0, hi = state->num_points - 1, mid; while (1) { /* point is outside interval or lo >= hi, wrap-around */ if (point <= state->points[lo].point || point > state->points[hi].point) { return state->points[lo].server; } /* test middle point */ mid = lo + (hi - lo) / 2; /* perfect match */ if (point <= state->points[mid].point && point > (mid ? state->points[mid-1].point : 0)) { return state->points[mid].server; } /* too low, go up */ if (state->points[mid].point < point) { lo = mid + 1; } else { hi = mid - 1; } } } /* }}} */ static void mmc_consistent_populate_buckets(mmc_consistent_state_t *state) /* {{{ */ { unsigned int i, step = 0xffffffff / MMC_CONSISTENT_BUCKETS; qsort((void *)state->points, state->num_points, sizeof(mmc_consistent_point_t), mmc_consistent_compare); for (i=0; ibuckets[i] = mmc_consistent_find(state, step * i); } state->buckets_populated = 1; } /* }}} */ mmc_t *mmc_consistent_find_server(void *s, const char *key, unsigned int key_len TSRMLS_DC) /* {{{ */ { mmc_consistent_state_t *state = s; if (state->num_servers > 1) { unsigned int hash; if (!state->buckets_populated) { mmc_consistent_populate_buckets(state); } hash = mmc_hash(state->hash, key, key_len); return state->buckets[hash % MMC_CONSISTENT_BUCKETS]; } return state->points[0].server; } /* }}} */ void mmc_consistent_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */ { mmc_consistent_state_t *state = s; int i, key_len, points = weight * MMC_CONSISTENT_POINTS; unsigned int seed = state->hash->init(), hash; /* buffer for "host:port-i\0" */ char *key = emalloc(strlen(mmc->host) + MAX_LENGTH_OF_LONG * 2 + 3); key_len = sprintf(key, "%s:%d-", mmc->host, mmc->tcp.port); seed = state->hash->combine(seed, key, key_len); /* add weight * MMC_CONSISTENT_POINTS number of points for this server */ state->points = erealloc(state->points, sizeof(*state->points) * (state->num_points + points)); for (i=0; ihash->finish(state->hash->combine(seed, key, key_len)); state->points[state->num_points + i].server = mmc; state->points[state->num_points + i].point = hash; } state->num_points += points; state->num_servers++; state->buckets_populated = 0; efree(key); } /* }}} */ mmc_hash_strategy_t mmc_consistent_hash = { mmc_consistent_create_state, mmc_consistent_free_state, mmc_consistent_find_server, mmc_consistent_add_server }; /* * 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 */ memcache-3.0.8/memcache.dsp0000644000076400007640000001124112130423646015434 0ustar vagrantvagrant# Microsoft Developer Studio Project File - Name="memcache" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=memcache - Win32 Debug_TS !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "memcache.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "memcache.mak" CFG="memcache - Win32 Debug_TS" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "memcache - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "memcache - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "memcache - Win32 Release_TS" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release_TS" # PROP BASE Intermediate_Dir "Release_TS" # PROP BASE Ignore_Export_Lib 0 # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release_TS" # PROP Intermediate_Dir "Release_TS" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MEMCACHE_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\php4" /I "..\..\..\php4\main" /I "..\..\..\php4\Zend" /I "..\..\..\php4\TSRM" /I "..\..\..\php4\win32" /D ZEND_DEBUG=0 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "PHP_EXPORTS" /D "COMPILE_DL_MEMCACHE" /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_MEMCACHE=1 /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 php4ts.lib zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS\php_memcache.dll" /libpath:"..\..\..\php4\Release_TS" /libpath:"..\..\..\php4\Release_TS_Inline" !ELSEIF "$(CFG)" == "memcache - Win32 Debug_TS" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug_TS" # PROP BASE Intermediate_Dir "Debug_TS" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug_TS" # PROP Intermediate_Dir "Debug_TS" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MEMCACHE_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\..\php4" /I "..\..\..\php4\main" /I "..\..\..\php4\Zend" /I "..\..\..\php4\TSRM" /I "..\..\..\php4\win32" /D ZEND_DEBUG=1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "PHP_EXPORTS" /D "COMPILE_DL_MEMCACHE" /D ZTS=1 /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_MEMCACHE=1 /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 php4ts_debug.lib zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS\php_memcache.dll" /pdbtype:sept /libpath:"..\..\..\php4\Debug_TS" !ENDIF # Begin Target # Name "memcache - Win32 Release_TS" # Name "memcache - Win32 Debug_TS" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\memcache.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\php_memcache.h # End Source File # End Group # End Target # End Project memcache-3.0.8/php_memcache.h0000644000076400007640000000762412130423646015756 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: php_memcache.h 330020 2013-04-08 02:12:04Z hradtke $ */ #ifndef PHP_MEMCACHE_H #define PHP_MEMCACHE_H extern zend_module_entry memcache_module_entry; #define phpext_memcache_ptr &memcache_module_entry #ifdef PHP_WIN32 #define PHP_MEMCACHE_API __declspec(dllexport) #else #define PHP_MEMCACHE_API #endif #include "memcache_pool.h" PHP_MINIT_FUNCTION(memcache); PHP_MSHUTDOWN_FUNCTION(memcache); PHP_MINFO_FUNCTION(memcache); PHP_NAMED_FUNCTION(zif_memcache_pool_connect); PHP_NAMED_FUNCTION(zif_memcache_pool_addserver); PHP_NAMED_FUNCTION(zif_memcache_pool_findserver); PHP_FUNCTION(memcache_connect); PHP_FUNCTION(memcache_pconnect); PHP_FUNCTION(memcache_add_server); PHP_FUNCTION(memcache_set_server_params); PHP_FUNCTION(memcache_set_failure_callback); PHP_FUNCTION(memcache_get_server_status); PHP_FUNCTION(memcache_get_version); PHP_FUNCTION(memcache_add); PHP_FUNCTION(memcache_set); PHP_FUNCTION(memcache_replace); PHP_FUNCTION(memcache_cas); PHP_FUNCTION(memcache_append); PHP_FUNCTION(memcache_prepend); PHP_FUNCTION(memcache_get); PHP_FUNCTION(memcache_delete); PHP_FUNCTION(memcache_debug); PHP_FUNCTION(memcache_get_stats); PHP_FUNCTION(memcache_get_extended_stats); PHP_FUNCTION(memcache_set_compress_threshold); PHP_FUNCTION(memcache_increment); PHP_FUNCTION(memcache_decrement); PHP_FUNCTION(memcache_close); PHP_FUNCTION(memcache_flush); #define PHP_MEMCACHE_VERSION "3.0.8" #define MMC_DEFAULT_TIMEOUT 1 /* seconds */ #define MMC_DEFAULT_RETRY 15 /* retry failed server after x seconds */ #define MMC_DEFAULT_CACHEDUMP_LIMIT 100 /* number of entries */ #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION >= 3) # define IS_CALLABLE(cb_zv, flags, cb_sp) zend_is_callable((cb_zv), (flags), (cb_sp) TSRMLS_CC) #else # define IS_CALLABLE(cb_zv, flags, cb_sp) zend_is_callable((cb_zv), (flags), (cb_sp)) #endif /* internal functions */ mmc_t *mmc_find_persistent(const char *, int, unsigned short, unsigned short, double, int TSRMLS_DC); int mmc_value_handler_single(const char *, unsigned int, zval *, unsigned int, unsigned long, void * TSRMLS_DC); int mmc_value_handler_multi(const char *, unsigned int, zval *, unsigned int, unsigned long, void * TSRMLS_DC); int mmc_stored_handler(mmc_t *, mmc_request_t *, int, const char *, unsigned int, void * TSRMLS_DC); int mmc_numeric_response_handler(mmc_t *, mmc_request_t *, int, const char *, unsigned int, void * TSRMLS_DC); /* session handler struct */ #if HAVE_MEMCACHE_SESSION #include "ext/session/php_session.h" extern ps_module ps_mod_memcache; #define ps_memcache_ptr &ps_mod_memcache PS_FUNCS(memcache); #endif #endif /* PHP_MEMCACHE_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 */ memcache-3.0.8/memcache_pool.h0000644000076400007640000004304412130423646016134 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_pool.h 328234 2012-11-04 19:34:04Z hradtke $ */ #ifndef MEMCACHE_POOL_H #define MEMCACHE_POOL_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef PHP_WIN32 #include #else #include #endif #include #include "php.h" #include "ext/standard/php_smart_str_public.h" #include "memcache_queue.h" #ifndef ZSTR #define ZSTR #define ZSTR_VAL(v) v #define zstr char * #else #define ZSTR_VAL(v) (v).s #endif /* * Mac OS X has no MSG_NOSIGNAL but >= 10.2 comes with SO_NOSIGPIPE which is a setsockopt() option * and not a send() parameter as MSG_NOSIGNAL. OpenBSD has none of the options so we need to ignore * SIGPIPE events */ #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif /*MSG_NOSIGNAL*/ /* use lowest byte for flags */ #define MMC_SERIALIZED 0x0001 #define MMC_COMPRESSED 0x0002 /* use second lowest byte to indicate data type */ #define MMC_TYPE_STRING 0x0000 #define MMC_TYPE_BOOL 0x0100 /*#define MMC_TYPE_INT 0x0200*/ #define MMC_TYPE_LONG 0x0300 /*#define MMC_TYPE_DATE 0x0400*/ /*#define MMC_TYPE_BYTE 0x0500*/ /*#define MMC_TYPE_FLOAT 0x0600*/ #define MMC_TYPE_DOUBLE 0x0700 /*#define MMC_TYPE_BLOB 0x0800*/ /* These flags are set here to reserve the for use by users of the pecl-memcache in set and get */ #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_12 0x10000 /* not used in pecl-memcache code */ #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_13 0x20000 /* not used in pecl-memcache code */ #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_14 0x40000 /* not used in pecl-memcache code */ #define MMC_RESERVED_APPLICATIONDEFINEDFLAG_15 0x80000 /* not used in pecl-memcache code */ #define MMC_BUFFER_SIZE 4096 #define MMC_MAX_UDP_LEN 1400 #define MMC_MAX_KEY_LEN 250 #define MMC_VALUE_HEADER "VALUE %250s %u %lu %lu" /* keep in sync with MMC_MAX_KEY_LEN */ #define MMC_COMPRESSION_LEVEL Z_DEFAULT_COMPRESSION #define MMC_DEFAULT_COMPRESS 20000 /* minimum 20k byte values for auto compression to be used */ #define MMC_DEFAULT_SAVINGS 0.2 /* minimum 20% savings for compression to be used */ #define MMC_PROTO_TCP 0 #define MMC_PROTO_UDP 1 #define MMC_STATUS_FAILED -1 /* server/stream is down */ #define MMC_STATUS_DISCONNECTED 0 /* stream is disconnected, ie. new connection */ #define MMC_STATUS_UNKNOWN 1 /* stream is in unknown state, ie. non-validated persistent connection */ #define MMC_STATUS_CONNECTED 2 /* stream is connected */ #define MMC_OK 0 #define MMC_REQUEST_FAILURE -1 /* operation failed, failover to other server */ #define MMC_REQUEST_DONE 0 /* ok result, or reading/writing is done */ #define MMC_REQUEST_MORE 1 /* more data follows in another packet, try select() again */ #define MMC_REQUEST_AGAIN 2 /* more data follows in this packet, try read/write again */ #define MMC_REQUEST_RETRY 3 /* retry/reschedule request */ #define MMC_RESPONSE_UNKNOWN -2 #define MMC_RESPONSE_ERROR -1 #define MMC_RESPONSE_NOT_FOUND 0x01 /* same as binary protocol */ #define MMC_RESPONSE_EXISTS 0x02 /* same as binary protocol */ #define MMC_RESPONSE_TOO_LARGE 0x03 /* same as binary protocol */ #define MMC_RESPONSE_NOT_STORED 0x05 /* same as binary protocol */ #define MMC_RESPONSE_CLIENT_ERROR 0x06 #define MMC_RESPONSE_UNKNOWN_CMD 0x81 /* same as binary protocol */ #define MMC_RESPONSE_OUT_OF_MEMORY 0x82 /* same as binary protocol */ #define MMC_STANDARD_HASH 1 #define MMC_CONSISTENT_HASH 2 #define MMC_HASH_CRC32 1 /* CRC32 hash function */ #define MMC_HASH_FNV1A 2 /* FNV-1a hash function */ #define MMC_CONSISTENT_POINTS 160 /* points per server */ #define MMC_CONSISTENT_BUCKETS 1024 /* number of precomputed buckets, should be power of 2 */ /* io buffer */ typedef struct mmc_buffer { smart_str value; unsigned int idx; /* current index */ } mmc_buffer_t; #define mmc_buffer_release(b) memset((b), 0, sizeof(*(b))) #define mmc_buffer_reset(b) (b)->value.len = (b)->idx = 0 inline void mmc_buffer_alloc(mmc_buffer_t *, unsigned int); inline void mmc_buffer_free(mmc_buffer_t *); /* stream handlers */ typedef struct mmc_stream mmc_stream_t; typedef size_t (*mmc_stream_read)(mmc_stream_t *stream, char *buf, size_t count TSRMLS_DC); typedef char *(*mmc_stream_readline)(mmc_stream_t *stream, char *buf, size_t maxlen, size_t *retlen TSRMLS_DC); /* stream container */ struct mmc_stream { php_stream *stream; int fd; /* file descriptor for select() */ unsigned short port; /* tcp/udp port stream is connected to */ int chunk_size; /* stream chunk size */ int status; /* stream status in MMC_STATUS_* status codes */ long failed; /* the timestamp the stream was marked as failed */ long retry_interval; /* seconds to wait before automatic reconnect */ mmc_buffer_t buffer; /* read buffer (when using udp) */ mmc_stream_read read; /* handles reading from stream */ mmc_stream_readline readline; /* handles reading lines from stream */ struct { /* temporary input buffer, see mmc_server_readline() */ char value[MMC_BUFFER_SIZE]; int idx; } input; }; /* request handlers */ typedef struct mmc mmc_t; typedef struct mmc_pool mmc_pool_t; typedef struct mmc_request mmc_request_t; typedef int (*mmc_request_reader)(mmc_t *mmc, mmc_request_t *request TSRMLS_DC); typedef int (*mmc_request_parser)(mmc_t *mmc, mmc_request_t *request TSRMLS_DC); typedef int (*mmc_request_value_handler)( const char *key, unsigned int key_len, zval *value, unsigned int flags, unsigned long cas, void *param TSRMLS_DC); typedef int (*mmc_request_response_handler)( mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param TSRMLS_DC); typedef int (*mmc_request_failover_handler)( mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param TSRMLS_DC); void mmc_request_reset(mmc_request_t *); void mmc_request_free(mmc_request_t *); /* server request */ struct mmc_request { mmc_stream_t *io; /* stream this request is sending on */ mmc_buffer_t sendbuf; /* request buffer */ mmc_buffer_t readbuf; /* response buffer */ char key[MMC_MAX_KEY_LEN + 1]; /* key buffer to use on failover of single-key requests */ unsigned int key_len; unsigned int protocol; /* protocol encoding of request {tcp, udp} */ mmc_queue_t failed_servers; /* mmc_queue_t, servers this request has failed at */ unsigned int failed_index; /* last index probed on failure */ mmc_request_reader read; /* handles reading (and validating datagrams) */ mmc_request_parser parse; /* called to parse response payload */ mmc_request_value_handler value_handler; /* called when result value have been parsed */ void *value_handler_param; mmc_request_response_handler response_handler; /* called when a non-value response has been received */ void *response_handler_param; mmc_request_failover_handler failover_handler; /* called to perform failover of failed request */ void *failover_handler_param; struct { uint16_t reqid; /* id of the request, increasing value set by client */ uint16_t seqid; /* next expected seqid */ uint16_t total; /* number of datagrams in response */ } udp; }; /* udp protocol header */ typedef struct mmc_udp_header { uint16_t reqid; /* id of the request, increasing value set by client */ uint16_t seqid; /* index of this datagram in response */ uint16_t total; /* number of datagrams in response */ uint16_t _reserved; } mmc_udp_header_t; /* server */ struct mmc { mmc_stream_t tcp; /* main stream, might be tcp or unix socket */ mmc_stream_t udp; /* auxiliary udp connection */ mmc_request_t *sendreq; /* currently sending request, NULL if none */ mmc_request_t *readreq; /* currently reading request, NULL if none */ mmc_request_t *buildreq; /* request currently being built, NULL if none */ mmc_queue_t sendqueue; /* mmc_queue_t, requests waiting to be sent */ mmc_queue_t readqueue; /* mmc_queue_t, requests waiting to be read */ char *host; struct timeval timeout; /* network timeout for this server */ int persistent; uint16_t reqid; /* next sequential request id */ char *error; /* last error message */ int errnum; /* last error code */ }; /* wire protocol */ #define MMC_ASCII_PROTOCOL 1 #define MMC_BINARY_PROTOCOL 2 /* same as in binary protocol */ #define MMC_OP_GET 0x00 #define MMC_OP_SET 0x01 #define MMC_OP_ADD 0x02 #define MMC_OP_REPLACE 0x03 #define MMC_OP_GETS 0x32 #define MMC_OP_CAS 0x33 #define MMC_OP_APPEND 0x34 /* not supported by binary protocol */ #define MMC_OP_PREPEND 0x35 /* not supported by binary protocol */ typedef mmc_request_t * (*mmc_protocol_create_request)(); typedef void (*mmc_protocol_clone_request)(mmc_request_t *clone, mmc_request_t *request); typedef void (*mmc_protocol_reset_request)(mmc_request_t *request); typedef void (*mmc_protocol_free_request)(mmc_request_t *request); typedef void (*mmc_protocol_get)(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len); typedef void (*mmc_protocol_begin_get)(mmc_request_t *request, int op); typedef void (*mmc_protocol_append_get)(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len); typedef void (*mmc_protocol_end_get)(mmc_request_t *request); typedef int (*mmc_protocol_store)( mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len, unsigned int flags, unsigned int exptime, unsigned long cas, zval *value TSRMLS_DC); typedef void (*mmc_protocol_delete)(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime); typedef void (*mmc_protocol_mutate)(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime); typedef void (*mmc_protocol_flush)(mmc_request_t *request, unsigned int exptime); typedef void (*mmc_protocol_stats)(mmc_request_t *request, const char *type, long slabid, long limit); typedef void (*mmc_protocol_version)(mmc_request_t *request); typedef struct mmc_protocol { mmc_protocol_create_request create_request; mmc_protocol_clone_request clone_request; mmc_protocol_reset_request reset_request; mmc_protocol_free_request free_request; mmc_protocol_get get; mmc_protocol_begin_get begin_get; mmc_protocol_append_get append_get; mmc_protocol_end_get end_get; mmc_protocol_store store; mmc_protocol_delete delete; mmc_protocol_mutate mutate; mmc_protocol_flush flush; mmc_protocol_version version; mmc_protocol_stats stats; } mmc_protocol_t; extern mmc_protocol_t mmc_ascii_protocol; extern mmc_protocol_t mmc_binary_protocol; /* hashing strategy */ typedef unsigned int (*mmc_hash_function_init)(); typedef unsigned int (*mmc_hash_function_combine)(unsigned int seed, const void *key, unsigned int key_len); typedef unsigned int (*mmc_hash_function_finish)(unsigned int seed); typedef struct mmc_hash_function { mmc_hash_function_init init; mmc_hash_function_combine combine; mmc_hash_function_finish finish; } mmc_hash_function_t; extern mmc_hash_function_t mmc_hash_crc32; extern mmc_hash_function_t mmc_hash_fnv1a; #define mmc_hash(hash, key, key_len) ((hash)->finish((hash)->combine((hash)->init(), (key), (key_len)))) typedef void * (*mmc_hash_create_state)(mmc_hash_function_t *); typedef void (*mmc_hash_free_state)(void *state); typedef mmc_t * (*mmc_hash_find_server)(void *state, const char *key, unsigned int key_len TSRMLS_DC); typedef void (*mmc_hash_add_server)(void *state, mmc_t *mmc, unsigned int weight); typedef struct mmc_hash_strategy { mmc_hash_create_state create_state; mmc_hash_free_state free_state; mmc_hash_find_server find_server; mmc_hash_add_server add_server; } mmc_hash_strategy_t; extern mmc_hash_strategy_t mmc_standard_hash; extern mmc_hash_strategy_t mmc_consistent_hash; /* 32 bit magic FNV-1a prime and init */ #define FNV_32_PRIME 0x01000193 #define FNV_32_INIT 0x811c9dc5 /* failure callback prototype */ typedef void (*mmc_failure_callback)(mmc_pool_t *pool, mmc_t *mmc, void *param TSRMLS_DC); /* server pool */ struct mmc_pool { mmc_t **servers; int num_servers; mmc_protocol_t *protocol; /* wire protocol */ mmc_hash_strategy_t *hash; /* hash strategy */ void *hash_state; /* strategy specific state */ fd_set wfds; fd_set rfds; struct timeval timeout; /* smallest timeout for any of the servers */ int in_select; mmc_queue_t *sending; /* mmc_queue_t, connections that want to send */ mmc_queue_t *reading; /* mmc_queue_t, connections that want to read */ mmc_queue_t _sending1, _sending2; mmc_queue_t _reading1, _reading2; mmc_queue_t pending; /* mmc_queue_t, connections that have non-finalized requests */ mmc_queue_t free_requests; /* mmc_queue_t, request free-list */ double min_compress_savings; unsigned int compress_threshold; mmc_failure_callback failure_callback; /* receives notification when a server fails */ void *failure_callback_param; }; /* server functions */ mmc_t *mmc_server_new(const char *, int, unsigned short, unsigned short, int, double, int TSRMLS_DC); void mmc_server_free(mmc_t * TSRMLS_DC); void mmc_server_disconnect(mmc_t *mmc, mmc_stream_t *io TSRMLS_DC); int mmc_server_valid(mmc_t * TSRMLS_DC); int mmc_server_failure(mmc_t *, mmc_stream_t *, const char *, int TSRMLS_DC); int mmc_request_failure(mmc_t *, mmc_stream_t *, const char *, unsigned int, int TSRMLS_DC); /* pool functions */ mmc_pool_t *mmc_pool_new(TSRMLS_D); void mmc_pool_free(mmc_pool_t * TSRMLS_DC); void mmc_pool_add(mmc_pool_t *, mmc_t *, unsigned int); void mmc_pool_close(mmc_pool_t * TSRMLS_DC); int mmc_pool_open(mmc_pool_t *, mmc_t *, mmc_stream_t *, int TSRMLS_DC); void mmc_pool_select(mmc_pool_t * TSRMLS_DC); void mmc_pool_run(mmc_pool_t * TSRMLS_DC); mmc_t *mmc_pool_find_next(mmc_pool_t *, const char *, unsigned int, mmc_queue_t *, unsigned int * TSRMLS_DC); mmc_t *mmc_pool_find(mmc_pool_t *, const char *, unsigned int TSRMLS_DC); int mmc_pool_schedule(mmc_pool_t *, mmc_t *, mmc_request_t * TSRMLS_DC); int mmc_pool_failover_handler(mmc_pool_t *, mmc_t *, mmc_request_t *, void * TSRMLS_DC); int mmc_pool_failover_handler_null(mmc_pool_t *, mmc_t *, mmc_request_t *, void * TSRMLS_DC); mmc_request_t *mmc_pool_request(mmc_pool_t *, int, mmc_request_response_handler, void *, mmc_request_failover_handler, void * TSRMLS_DC); mmc_request_t *mmc_pool_request_get(mmc_pool_t *, int, mmc_request_value_handler, void *, mmc_request_failover_handler, void * TSRMLS_DC); #define mmc_pool_release(p, r) mmc_queue_push(&((p)->free_requests), (r)) int mmc_prepare_store( mmc_pool_t *, mmc_request_t *, const char *, unsigned int, const char *, unsigned int, unsigned int, unsigned int, zval * TSRMLS_DC); int mmc_pool_schedule_key(mmc_pool_t *, const char *, unsigned int, mmc_request_t *, unsigned int TSRMLS_DC); int mmc_pool_schedule_get(mmc_pool_t *, int, int, zval *, mmc_request_value_handler, void *, mmc_request_failover_handler, void *, mmc_request_t * TSRMLS_DC); /* utility functions */ int mmc_pack_value(mmc_pool_t *, mmc_buffer_t *, zval *, unsigned int * TSRMLS_DC); int mmc_unpack_value(mmc_t *, mmc_request_t *, mmc_buffer_t *, const char *, unsigned int, unsigned int, unsigned long, unsigned int TSRMLS_DC); double timeval_to_double(struct timeval tv); struct timeval double_to_timeval(double sec); inline int mmc_prepare_key_ex(const char *, unsigned int, char *, unsigned int *); inline int mmc_prepare_key(zval *, char *, unsigned int *); #define mmc_str_left(h, n, hlen, nlen) ((hlen) >= (nlen) ? memcmp((h), (n), (nlen)) == 0 : 0) /* globals */ ZEND_BEGIN_MODULE_GLOBALS(memcache) long default_port; long chunk_size; long protocol; long hash_strategy; long hash_function; long allow_failover; long max_failover_attempts; long redundancy; long session_redundancy; long compress_threshold; long lock_timeout; ZEND_END_MODULE_GLOBALS(memcache) #ifdef ZTS #define MEMCACHE_G(v) TSRMG(memcache_globals_id, zend_memcache_globals *, v) #else #define MEMCACHE_G(v) (memcache_globals.v) #endif #endif /* MEMCACHE_POOL_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 */ memcache-3.0.8/memcache_queue.h0000644000076400007640000000466512130423646016315 0ustar vagrantvagrant/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Mikael Johansson | +----------------------------------------------------------------------+ */ /* $Id: memcache_queue.h 326387 2012-06-30 17:41:36Z ab $ */ #ifndef MEMCACHE_QUEUE_H_ #define MEMCACHE_QUEUE_H_ /* request / server stack */ #define MMC_QUEUE_PREALLOC 25 typedef struct mmc_queue { void **items; /* items on queue */ int alloc; /* allocated size */ int head; /* head index in ring buffer */ int tail; /* tail index in ring buffer */ int len; } mmc_queue_t; #define mmc_queue_release(q) memset((q), 0, sizeof(*(q))) #define mmc_queue_reset(q) (q)->len = (q)->head = (q)->tail = 0 #define mmc_queue_item(q, i) ((q)->tail + (i) < (q)->alloc ? (q)->items[(q)->tail + (i)] : (q)->items[(i) - ((q)->alloc - (q)->tail)]) #ifdef PHP_WIN32 #define MMC_QUEUE_INLINE #else #define MMC_QUEUE_INLINE inline #endif MMC_QUEUE_INLINE void mmc_queue_push(mmc_queue_t *, void *); MMC_QUEUE_INLINE void *mmc_queue_pop(mmc_queue_t *); MMC_QUEUE_INLINE int mmc_queue_contains(mmc_queue_t *, void *); MMC_QUEUE_INLINE void mmc_queue_free(mmc_queue_t *); MMC_QUEUE_INLINE void mmc_queue_copy(mmc_queue_t *, mmc_queue_t *); MMC_QUEUE_INLINE void mmc_queue_remove(mmc_queue_t *, void *); #endif /*MEMCACHE_QUEUE_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 */ memcache-3.0.8/README0000644000076400007640000001240512130423646014045 0ustar vagrantvagrantmemcached module for PHP ------------------------ This module requires zlib library, used for on-the-fly data (de)compression. Also, you'll need memcached to use it =) The memcached website is here: http://www.danga.com/memcached/ You will probably need libevent to install memcached: You can download it here: http://www.monkey.org/~provos/libevent/ How to run tests: 1. sh tests/memcache.sh 2. TEST_PHP_EXECUTABLE=/usr/local/bin/php php -dextension=modules/memcache.so run-tests.php -d extension=modules/memcache.so New API in 3.0 ------------------------ Version 3 introduces a new class "MemcachePool" which implements the new API, the old class "Memcache" is still retained (but is deprecated) with the same interface for backwards compatibility. Please note that you need a new memcached version to use the CAS, default value to increment/decrement, append and prepend, and binary protocol features. New INI directives are available to allow control over protocol, redundancy and hash strategy selection. These are # The binary protocol results in less traffic and is more efficient # for the client and server to generate/parse memcache.protocol = {ascii, binary} # default ascii # When enabled the client sends requests to N servers in parallel, resulting in # a somewhat crude reduncancy or mirroring, suitable when used as a session # storage. # # If data integrity is of greater importance a real replicating memcached # backend such as "repcached" (http://sourceforge.net/projects/repcached/) is # recommended memcache.redundancy = # default 1 memcache.session_redundancy = # default 2 # Hash strategy and function selection. The consistent hashing strategy # is now the default as it allows servers to be added and removed from # the pool without resulting in all or most keys being re-mapped to # other server (ie. voiding the cache) memcache.hash_strategy = {standard, consistent} # default consistent memcache.hash_function = {crc32, fnv} # default crc32 # Compression is enabled by default, the threshold which control the minimum # string length which triggers compresssion can be changed as memcache.compress_threshold = # default 20000 The directives are used by the MemcachePool constructor so you can instantiate several pools with different settings by using ini_set() creativly. For example ini_set('memcache.protocol', 'binary'); $binarypool = new MemcachePool(); $binarypool->addServer(...) ini_set('memcache.protocol', 'ascii'); ini_set('memcache.redundancy', '2'); $redundantpool = new MemcachePool(); $redundantpool->addServer(...) ini_set('memcache.redundancy', '1'); The new interface looks like class MemcachePool() { bool connect(string host, int tcp_port = 11211, int udp_port = 0, bool persistent = true, int weight = 1, int timeout = 1, int retry_interval = 15) bool addServer(string host, int tcp_port = 11211, int udp_port = 0, bool persistent = true, int weight = 1, int timeout = 1, int retry_interval = 15, bool status = true) bool setServerParams(string host, int tcp_port = 11211, int timeout = 1, int retry_interval = 15, bool status = true) /** * Supports fetching flags and CAS values */ mixed get(mixed key, mixed &flags = null, mixed &cas = null) /** * Supports multi-set, for example * $memcache->set(array('key1' => 'val1', 'key2' => 'val1'), null, 0, 60) */ bool add(mixed key, mixed var = null, int flag = 0, int exptime = 0) bool set(mixed key, mixed var = null, int flag = 0, int exptime = 0) bool replace(mixed key, mixed var = null, int flag = 0, int exptime = 0) /** * Compare-and-Swap, uses the CAS param from MemcachePool::get() */ bool cas(mixed key, mixed var = null, int flag = 0, int exptime = 0, int cas = 0) /** * Prepends/appends a value to an existing one */ bool append(mixed key, mixed var = null, int flag = 0, int exptime = 0) bool prepend(mixed key, mixed var = null, int flag = 0, int exptime = 0) /** * Supports multi-key operations, for example * $memcache->delete(array('key1', 'key2')) */ bool delete(mixed key, int exptime = 0) /** * Supports multi-key operations, for example * $memcache->increment(array('key1', 'key2'), 1, 0, 0) * * The new defval (default value) and exptime (expiration time) are used * if the key doesn't already exist. They must be supplied (even if 0) for * this to be enabled. * * Returns an integer with the new value if key is a string * Returns an array of integers if the key is an array */ mixed increment(mixed key, int value = 1, int defval = 0, int exptime = 0) mixed decrement(mixed key, int value = 1, int defval = 0, int exptime = 0) /** * Assigns a pool-specific failure callback which will be called when * a request fails. May be null in order to disable callbacks. The callback * receive arguments like * * function mycallback($host, $tcp_port, $udp_port, $error, $errnum) * * Where $host and $error are strings or null, the other params are integers. */ bool setFailureCallback(function callback) /** * Locates the server a given would be hashed to * * Returns a string "hostname:port" on success * Returns false on failure such as invalid key */ string findServer(string key) } Maintainers: Herman J. Radtke III hradtke at php dot net memcache-3.0.8/memcache.php0000644000076400007640000007066612130423646015455 0ustar vagrantvagrant | +----------------------------------------------------------------------+ */ $VERSION='$Id: memcache.php 326707 2012-07-19 19:02:42Z ab $'; define('ADMIN_USERNAME','memcache'); // Admin Username define('ADMIN_PASSWORD','password'); // Admin Password define('DATE_FORMAT','Y/m/d H:i:s'); define('GRAPH_SIZE',200); define('MAX_ITEM_DUMP',50); $MEMCACHE_SERVERS[] = 'mymemcache-server1:11211'; // add more as an array $MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array ////////// END OF DEFAULT CONFIG AREA ///////////////////////////////////////////////////////////// ///////////////// Password protect //////////////////////////////////////////////////////////////// if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||$_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) { Header("WWW-Authenticate: Basic realm=\"Memcache Login\""); Header("HTTP/1.0 401 Unauthorized"); echo <<

Rejected!

Wrong Username or Password! EOB; exit; } ///////////MEMCACHE FUNCTIONS ///////////////////////////////////////////////////////////////////// function get_host_port_from_server($server){ $values = explode(':', $server); if (($values[0] == 'unix') && (!is_numeric( $values[1]))) { return array($server, 0); } else { return $values; } } function sendMemcacheCommands($command){ global $MEMCACHE_SERVERS; $result = array(); foreach($MEMCACHE_SERVERS as $server){ $strs = get_host_port_from_server($server); $host = $strs[0]; $port = $strs[1]; $result[$server] = sendMemcacheCommand($host,$port,$command); } return $result; } function sendMemcacheCommand($server,$port,$command){ $s = @fsockopen($server,$port); if (!$s){ die("Cant connect to:".$server.':'.$port); } fwrite($s, $command."\r\n"); $buf=''; while ((!feof($s))) { $buf .= fgets($s, 256); if (strpos($buf,"END\r\n")!==false){ // stat says end break; } if (strpos($buf,"DELETED\r\n")!==false || strpos($buf,"NOT_FOUND\r\n")!==false){ // delete says these break; } if (strpos($buf,"OK\r\n")!==false){ // flush_all says ok break; } } fclose($s); return parseMemcacheResults($buf); } function parseMemcacheResults($str){ $res = array(); $lines = explode("\r\n",$str); $cnt = count($lines); for($i=0; $i< $cnt; $i++){ $line = $lines[$i]; $l = explode(' ',$line,3); if (count($l)==3){ $res[$l[0]][$l[1]]=$l[2]; if ($l[0]=='VALUE'){ // next line is the value $res[$l[0]][$l[1]] = array(); list ($flag,$size)=explode(' ',$l[2]); $res[$l[0]][$l[1]]['stat']=array('flag'=>$flag,'size'=>$size); $res[$l[0]][$l[1]]['value']=$lines[++$i]; } }elseif($line=='DELETED' || $line=='NOT_FOUND' || $line=='OK'){ return $line; } } return $res; } function dumpCacheSlab($server,$slabId,$limit){ list($host,$port) = get_host_port_from_server($server); $resp = sendMemcacheCommand($host,$port,'stats cachedump '.$slabId.' '.$limit); return $resp; } function flushServer($server){ list($host,$port) = get_host_port_from_server($server); $resp = sendMemcacheCommand($host,$port,'flush_all'); return $resp; } function getCacheItems(){ $items = sendMemcacheCommands('stats items'); $serverItems = array(); $totalItems = array(); foreach ($items as $server=>$itemlist){ $serverItems[$server] = array(); $totalItems[$server]=0; if (!isset($itemlist['STAT'])){ continue; } $iteminfo = $itemlist['STAT']; foreach($iteminfo as $keyinfo=>$value){ if (preg_match('/items\:(\d+?)\:(.+?)$/',$keyinfo,$matches)){ $serverItems[$server][$matches[1]][$matches[2]] = $value; if ($matches[2]=='number'){ $totalItems[$server] +=$value; } } } } return array('items'=>$serverItems,'counts'=>$totalItems); } function getMemcacheStats($total=true){ $resp = sendMemcacheCommands('stats'); if ($total){ $res = array(); foreach($resp as $server=>$r){ foreach($r['STAT'] as $key=>$row){ if (!isset($res[$key])){ $res[$key]=null; } switch ($key){ case 'pid': $res['pid'][$server]=$row; break; case 'uptime': $res['uptime'][$server]=$row; break; case 'time': $res['time'][$server]=$row; break; case 'version': $res['version'][$server]=$row; break; case 'pointer_size': $res['pointer_size'][$server]=$row; break; case 'rusage_user': $res['rusage_user'][$server]=$row; break; case 'rusage_system': $res['rusage_system'][$server]=$row; break; case 'curr_items': $res['curr_items']+=$row; break; case 'total_items': $res['total_items']+=$row; break; case 'bytes': $res['bytes']+=$row; break; case 'curr_connections': $res['curr_connections']+=$row; break; case 'total_connections': $res['total_connections']+=$row; break; case 'connection_structures': $res['connection_structures']+=$row; break; case 'cmd_get': $res['cmd_get']+=$row; break; case 'cmd_set': $res['cmd_set']+=$row; break; case 'get_hits': $res['get_hits']+=$row; break; case 'get_misses': $res['get_misses']+=$row; break; case 'evictions': $res['evictions']+=$row; break; case 'bytes_read': $res['bytes_read']+=$row; break; case 'bytes_written': $res['bytes_written']+=$row; break; case 'limit_maxbytes': $res['limit_maxbytes']+=$row; break; case 'threads': $res['rusage_system'][$server]=$row; break; } } } return $res; } return $resp; } ////////////////////////////////////////////////////// // // don't cache this page // header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); // HTTP/1.0 function duration($ts) { global $time; $years = (int)((($time - $ts)/(7*86400))/52.177457); $rem = (int)(($time-$ts)-($years * 52.177457 * 7 * 86400)); $weeks = (int)(($rem)/(7*86400)); $days = (int)(($rem)/86400) - $weeks*7; $hours = (int)(($rem)/3600) - $days*24 - $weeks*7*24; $mins = (int)(($rem)/60) - $hours*60 - $days*24*60 - $weeks*7*24*60; $str = ''; if($years==1) $str .= "$years year, "; if($years>1) $str .= "$years years, "; if($weeks==1) $str .= "$weeks week, "; if($weeks>1) $str .= "$weeks weeks, "; if($days==1) $str .= "$days day,"; if($days>1) $str .= "$days days,"; if($hours == 1) $str .= " $hours hour and"; if($hours>1) $str .= " $hours hours and"; if($mins == 1) $str .= " 1 minute"; else $str .= " $mins minutes"; return $str; } // create graphics // function graphics_avail() { return extension_loaded('gd'); } function bsize($s) { foreach (array('','K','M','G') as $i => $k) { if ($s < 1024) break; $s/=1024; } return sprintf("%5.1f %sBytes",$s,$k); } // create menu entry function menu_entry($ob,$title) { global $PHP_SELF; if ($ob==$_GET['op']){ return "
  • $title
  • "; } return "
  • $title
  • "; } function getHeader(){ $header = << MEMCACHE INFO

    memcache.php by Harun Yayli


    EOB; return $header; } function getFooter(){ global $VERSION; $footer = '
    '; return $footer; } function getMenu(){ global $PHP_SELF; echo "