package.xml 0000644 0000765 0000024 00000076323 14423450270 012215 0 ustar tomas staff
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.
The extension allows use to store sessions in memcached via memcache.
Tomas Srnka
tomassrnka
tomas.srnka@gmail.com
yes
Antony Dovgal
tony2001
tony@daylessday.org
no
Mikael Johansson
mikl
mikael@synd.info
no
Pierre-Alain Joye
pajoye
pierre.php@gmail.com
no
Herman J. Radtke III
hradtke
hradtke@php.net
no
2023-04-30
8.2
8.2
stable
stable
PHP License
- Version 8.x support PHP 8.x
- Version 4.x supports PHP 7.0-7.4.
- Version 4.x is considered to be stable for production usage.
- Support for PHP 5.x has been removed, please use memcache extension ver. 3.x
- Special thanks to Frantisek Drojak - thesource93 (github) and Zaffy (github) for making this happen
8.0.0
1.4.0b1
memcache
2020-12-06
8.2
8.2
stable
stable
PHP License
- 8.2 adds support for PHP 8.2.x
2020-12-06
8.0
8.0
stable
stable
PHP License
- 8.0 supports PHP 8.0+ and drops support for older PHP versions
2019-12-19
4.0.5.1
4.0.5.1
stable
stable
PHP License
- 4.0.5.1 fix in version number
2019-12-19
4.0.5
4.0.5
stable
stable
PHP License
- First official release for PHP 7+, supports PHP 7.0-7.4
- Support for PHP 5.x has been removed, please use 3.x version of this extension
- 4.0.5 version consits of fix for PHP 7.4 behaviour changes
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-8.2/docker/host.conf 0000644 0000765 0000024 00000000033 14423450270 015223 0 ustar tomas staff order hosts,bind
multi off
memcache-8.2/docker/start.sh 0000755 0000765 0000024 00000001511 14423450270 015075 0 ustar tomas staff #!/bin/bash
export CFLAGS="-fstack-protector-strong -O2"
export CPPFLAGS="${CFLAGS}"
export LDFLAGS="-Wl,-O1 -Wl,--hash-style=both"
# Build extension
set -eux
cd /usr/src
git clone https://github.com/websupport-sk/pecl-memcache.git
cd pecl-memcache;
phpize
./configure
make -j"$(nproc)"
# Spawn memcached for tests
echo "Starting memcached... "
mkdir -p /var/run/memcached
chown memcache:memcache /var/run/memcached
/usr/bin/memcached -m 64 -u memcache -s /var/run/memcached/memcached.sock -d
/usr/bin/memcached -m 64 -u memcache -U 11211 -l 127.0.0.1 -p 11211 -d
/usr/bin/memcached -m 64 -u memcache -U 11212 -l 127.0.0.1 -p 11212 -d
# Let's start tests
cd /usr/src/pecl-memcache
export NO_INTERACTION=1
export TEST_PHP_ARGS="--show-diff --keep-all -w fails.log"
MEMCACHE_PROTOCOL=ascii make test
MEMCACHE_PROTOCOL=binary make test
memcache-8.2/src/memcache.c 0000644 0000765 0000024 00000232273 14423450270 014642 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"
#include "php_memcache.h"
/* True global resources - no need for thread safety here */
static int le_memcache_pool, le_memcache_server;
static zend_class_entry *memcache_pool_ce;
static zend_class_entry *memcache_ce;
ZEND_EXTERN_MODULE_GLOBALS(memcache)
/* {{{ memcache_functions[]
*/
#ifndef ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, ref, num, type) \
ZEND_BEGIN_ARG_INFO_EX(name, 0u, ref, num)
#endif
#ifndef ZEND_ARG_TYPE_MASK
#define ZEND_ARG_TYPE_MASK(ref, name, mask, def) \
ZEND_ARG_INFO(ref, name)
#endif
ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_connect, 0, 0, 1)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, port)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, unused4)
ZEND_ARG_INFO(0, unused5)
ZEND_ARG_INFO(0, unused6)
ZEND_ARG_INFO(0, unused7)
ZEND_ARG_INFO(0, unugsed8)
ZEND_END_ARG_INFO()
#define arginfo_memcache_pconnect arginfo_memcache_connect
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_add_server, 0, 2, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, port)
ZEND_ARG_INFO(0, tcp_port)
ZEND_ARG_INFO(0, persistent)
ZEND_ARG_INFO(0, weight)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, retry_interval)
ZEND_ARG_INFO(0, status)
ZEND_ARG_INFO(0, failure_callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_server_params, 0, 2, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0)
ZEND_ARG_TYPE_INFO(0, retry_interval, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, status, _IS_BOOL, 0)
ZEND_ARG_INFO(0, failure_callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_failure_callback, 0, 2, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_INFO(0, failure_callback, IS_CALLABLE, 1)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_get_server_status, 0, 2, MAY_BE_BOOL|MAY_BE_LONG)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_get_version, 0, 1, MAY_BE_STRING|MAY_BE_BOOL)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_add, 0, 2, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0)
#ifdef IS_MIXED
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
#else
ZEND_ARG_INFO(0, value)
#endif
ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, cas, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_memcache_set arginfo_memcache_add
#define arginfo_memcache_replace arginfo_memcache_add
#define arginfo_memcache_cas arginfo_memcache_add
#define arginfo_memcache_append arginfo_memcache_add
#define arginfo_memcache_prepend arginfo_memcache_add
#ifdef IS_MIXED
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_get, 0, 2, IS_MIXED, 0)
#else
ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_get, 0, 0, 1)
#endif
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(1, flags)
ZEND_ARG_INFO(1, cas)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_delete, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, exptime)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_debug, 0, 1, _IS_BOOL, 0)
ZEND_ARG_INFO(0, on_off)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_get_stats, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_INFO(0, type, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, slabid, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_memcache_get_extended_stats arginfo_memcache_get_stats
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_compress_threshold, 0, 2, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_INFO(0, threshold, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, min_savings, IS_DOUBLE, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_increment, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY|MAY_BE_LONG)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, defval, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_memcache_decrement arginfo_memcache_increment
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_close, 0, 1, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_flush, 0, 1, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_INFO(0, delay, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_set_sasl_auth_data, 0, 3, _IS_BOOL, 0)
ZEND_ARG_OBJ_INFO(0, memcache, MemcachePool, 0)
ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)
ZEND_END_ARG_INFO()
zend_function_entry memcache_functions[] = {
PHP_FE(memcache_connect, arginfo_memcache_connect)
PHP_FE(memcache_pconnect, arginfo_memcache_pconnect)
PHP_FE(memcache_add_server, arginfo_memcache_add_server)
PHP_FE(memcache_set_server_params, arginfo_memcache_set_server_params)
PHP_FE(memcache_set_failure_callback, arginfo_memcache_set_failure_callback)
PHP_FE(memcache_get_server_status, arginfo_memcache_get_server_status)
PHP_FE(memcache_get_version, arginfo_memcache_get_version)
PHP_FE(memcache_add, arginfo_memcache_add)
PHP_FE(memcache_set, arginfo_memcache_set)
PHP_FE(memcache_replace, arginfo_memcache_replace)
PHP_FE(memcache_cas, arginfo_memcache_cas)
PHP_FE(memcache_append, arginfo_memcache_append)
PHP_FE(memcache_prepend, arginfo_memcache_prepend)
PHP_FE(memcache_get, arginfo_memcache_get)
PHP_FE(memcache_delete, arginfo_memcache_delete)
PHP_FE(memcache_debug, arginfo_memcache_debug)
PHP_FE(memcache_get_stats, arginfo_memcache_get_stats)
PHP_FE(memcache_get_extended_stats, arginfo_memcache_get_extended_stats)
PHP_FE(memcache_set_compress_threshold, arginfo_memcache_set_compress_threshold)
PHP_FE(memcache_increment, arginfo_memcache_increment)
PHP_FE(memcache_decrement, arginfo_memcache_decrement)
PHP_FE(memcache_close, arginfo_memcache_close)
PHP_FE(memcache_flush, arginfo_memcache_flush)
PHP_FE(memcache_set_sasl_auth_data, arginfo_memcache_set_sasl_auth_data)
ZEND_FE_END
};
ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_pool_object_connect, 0, 0, 1)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, tcp_port)
ZEND_ARG_INFO(0, udp_port)
ZEND_ARG_INFO(0, persistent)
ZEND_ARG_INFO(0, weight)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, retry_interval)
ZEND_END_ARG_INFO()
#define arginfo_memcache_object_connect arginfo_memcache_connect
#define arginfo_memcache_object_pconnect arginfo_memcache_connect
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_pool_object_addserver, 0, 1, _IS_BOOL, 0)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, tcp_port)
ZEND_ARG_INFO(0, udp_port)
ZEND_ARG_INFO(0, persistent)
ZEND_ARG_INFO(0, weight)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, retry_interval)
ZEND_ARG_INFO(0, status)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_addserver, 0, 1, _IS_BOOL, 0)
ZEND_ARG_INFO(0, host)
ZEND_ARG_INFO(0, tcp_port)
ZEND_ARG_INFO(0, persistent)
ZEND_ARG_INFO(0, weight)
ZEND_ARG_INFO(0, timeout)
ZEND_ARG_INFO(0, retry_interval)
ZEND_ARG_INFO(0, status)
ZEND_ARG_INFO(0, failure_callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setserverparams, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0)
ZEND_ARG_TYPE_INFO(0, retry_interval, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, status, _IS_BOOL, 0)
ZEND_ARG_INFO(0, failure_callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setfailurecallback, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, failure_callback, IS_CALLABLE, 1)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_getserverstatus, 0, 1, MAY_BE_BOOL|MAY_BE_LONG)
ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, tcp_port, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_findserver, 0, 1, MAY_BE_STRING|MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_getversion, 0, 0, MAY_BE_STRING|MAY_BE_BOOL)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_add, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0)
#ifdef IS_MIXED
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
#else
ZEND_ARG_INFO(0, value)
#endif
ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, cas, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_memcache_object_set arginfo_memcache_object_add
#define arginfo_memcache_object_replace arginfo_memcache_object_add
#define arginfo_memcache_object_set arginfo_memcache_object_add
#define arginfo_memcache_object_cas arginfo_memcache_object_add
#define arginfo_memcache_object_append arginfo_memcache_object_add
#define arginfo_memcache_object_prepend arginfo_memcache_object_add
#ifdef IS_MIXED
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_get, 0, 1, IS_MIXED, 0)
#else
ZEND_BEGIN_ARG_INFO_EX(arginfo_memcache_object_get, 0, 0, 1)
#endif
ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0)
#ifdef IS_MIXED
ZEND_ARG_TYPE_INFO(1, flags, IS_MIXED, 0)
ZEND_ARG_TYPE_INFO(1, cas, IS_MIXED, 0)
#else
ZEND_ARG_INFO(1, flags)
ZEND_ARG_INFO(1, cas)
#endif
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_delete, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY)
ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_getstats, 0, 0, MAY_BE_BOOL|MAY_BE_ARRAY)
ZEND_ARG_TYPE_INFO(0, type, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, slabid, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_memcache_object_getextendedstats arginfo_memcache_object_getstats
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setcompressthreshold, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, threshold, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, min_savings, IS_DOUBLE, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_memcache_object_increment, 0, 1, MAY_BE_BOOL|MAY_BE_ARRAY|MAY_BE_LONG)
ZEND_ARG_TYPE_MASK(0, key, MAY_BE_STRING|MAY_BE_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, defval, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, exptime, IS_LONG, 0)
ZEND_END_ARG_INFO()
#define arginfo_memcache_object_decrement arginfo_memcache_object_increment
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_close, 0, 0, _IS_BOOL, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_flush, 0, 0, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, delay, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_memcache_object_setSaslAuthData, 0, 2, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)
ZEND_END_ARG_INFO()
static zend_function_entry php_memcache_pool_class_functions[] = {
PHP_NAMED_FE(connect, zif_memcache_pool_connect, arginfo_memcache_pool_object_connect)
PHP_NAMED_FE(addserver, zif_memcache_pool_addserver, arginfo_memcache_pool_object_addserver)
PHP_FALIAS(setserverparams, memcache_set_server_params, arginfo_memcache_object_setserverparams)
PHP_FALIAS(setfailurecallback, memcache_set_failure_callback, arginfo_memcache_object_setfailurecallback)
PHP_FALIAS(getserverstatus, memcache_get_server_status, arginfo_memcache_object_getserverstatus)
PHP_NAMED_FE(findserver, zif_memcache_pool_findserver, arginfo_memcache_object_findserver)
PHP_FALIAS(getversion, memcache_get_version, arginfo_memcache_object_getversion)
PHP_FALIAS(add, memcache_add, arginfo_memcache_object_add)
PHP_FALIAS(set, memcache_set, arginfo_memcache_object_set)
PHP_FALIAS(replace, memcache_replace, arginfo_memcache_object_replace)
PHP_FALIAS(cas, memcache_cas, arginfo_memcache_object_cas)
PHP_FALIAS(append, memcache_append, arginfo_memcache_object_append)
PHP_FALIAS(prepend, memcache_prepend, arginfo_memcache_object_prepend)
PHP_FALIAS(get, memcache_get, arginfo_memcache_object_get)
PHP_FALIAS(delete, memcache_delete, arginfo_memcache_object_delete)
PHP_FALIAS(getstats, memcache_get_stats, arginfo_memcache_object_getstats)
PHP_FALIAS(getextendedstats, memcache_get_extended_stats, arginfo_memcache_object_getextendedstats)
PHP_FALIAS(setcompressthreshold, memcache_set_compress_threshold, arginfo_memcache_object_setcompressthreshold)
PHP_FALIAS(increment, memcache_increment, arginfo_memcache_object_increment)
PHP_FALIAS(decrement, memcache_decrement, arginfo_memcache_object_decrement)
PHP_FALIAS(close, memcache_close, arginfo_memcache_object_close)
PHP_FALIAS(flush, memcache_flush, arginfo_memcache_object_flush)
PHP_FALIAS(setSaslAuthData, memcache_set_sasl_auth_data, arginfo_memcache_object_setSaslAuthData)
ZEND_FE_END
};
static zend_function_entry php_memcache_class_functions[] = {
PHP_ME_MAPPING(connect, memcache_connect, arginfo_memcache_object_connect, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(pconnect, memcache_pconnect, arginfo_memcache_object_pconnect, ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(addserver, memcache_add_server, arginfo_memcache_object_addserver, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
/* }}} */
/* {{{ memcache_module_entry
*/
zend_module_entry memcache_module_entry = {
STANDARD_MODULE_HEADER,
"memcache",
memcache_functions,
PHP_MINIT(memcache),
PHP_MSHUTDOWN(memcache),
PHP_RINIT(memcache),
NULL,
PHP_MINFO(memcache),
PHP_MEMCACHE_VERSION,
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_MEMCACHE
ZEND_GET_MODULE(memcache)
#endif
static PHP_INI_MH(OnUpdateChunkSize) /* {{{ */
{
zend_long val;
char *endptr = NULL;
val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
if (!endptr || (*endptr != '\0') || val <= 0) {
php_error_docref(NULL, E_WARNING, "memcache.chunk_size must be a positive integer ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
/* }}} */
static PHP_INI_MH(OnUpdateFailoverAttempts) /* {{{ */
{
zend_long val;
char *endptr = NULL;
val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
if (!endptr || (*endptr != '\0') || val <= 0) {
php_error_docref(NULL, E_WARNING, "memcache.max_failover_attempts must be a positive integer ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
/* }}} */
static PHP_INI_MH(OnUpdateProtocol) /* {{{ */
{
if (!strcasecmp(ZSTR_VAL(new_value), "ascii")) {
MEMCACHE_G(protocol) = MMC_ASCII_PROTOCOL;
}
else if (!strcasecmp(ZSTR_VAL(new_value), "binary")) {
MEMCACHE_G(protocol) = MMC_BINARY_PROTOCOL;
}
else {
php_error_docref(NULL, E_WARNING, "memcache.protocol must be in set {ascii, binary} ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return SUCCESS;
}
/* }}} */
static PHP_INI_MH(OnUpdateHashStrategy) /* {{{ */
{
if (!strcasecmp(ZSTR_VAL(new_value), "standard")) {
MEMCACHE_G(hash_strategy) = MMC_STANDARD_HASH;
}
else if (!strcasecmp(ZSTR_VAL(new_value), "consistent")) {
MEMCACHE_G(hash_strategy) = MMC_CONSISTENT_HASH;
}
else {
php_error_docref(NULL, E_WARNING, "memcache.hash_strategy must be in set {standard, consistent} ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return SUCCESS;
}
/* }}} */
static PHP_INI_MH(OnUpdateHashFunction) /* {{{ */
{
if (!strcasecmp(ZSTR_VAL(new_value), "crc32")) {
MEMCACHE_G(hash_function) = MMC_HASH_CRC32;
}
else if (!strcasecmp(ZSTR_VAL(new_value), "fnv")) {
MEMCACHE_G(hash_function) = MMC_HASH_FNV1A;
}
else {
php_error_docref(NULL, E_WARNING, "memcache.hash_function must be in set {crc32, fnv} ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return SUCCESS;
}
/* }}} */
static PHP_INI_MH(OnUpdateRedundancy) /* {{{ */
{
zend_long val;
char *endptr = NULL;
val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
if (!endptr || (*endptr != '\0') || val <= 0) {
php_error_docref(NULL, E_WARNING, "memcache.redundancy must be a positive integer ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
/* }}} */
static PHP_INI_MH(OnUpdateCompressThreshold) /* {{{ */
{
zend_long val;
char *endptr = NULL;
val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
if (!endptr || (*endptr != '\0') || val < 0) {
php_error_docref(NULL, E_WARNING, "memcache.compress_threshold must be a positive integer ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
/* }}} */
static PHP_INI_MH(OnUpdateLockTimeout) /* {{{ */
{
zend_long val;
char *endptr = NULL;
val = ZEND_STRTOL(ZSTR_VAL(new_value), &endptr, 10);
if (!endptr || (*endptr != '\0') || val <= 0) {
php_error_docref(NULL, E_WARNING, "memcache.lock_timeout must be a positive integer ('%s' given)", ZSTR_VAL(new_value));
return FAILURE;
}
return OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
/* }}} */
static PHP_INI_MH(OnUpdatePrefixStaticKey) /* {{{ */
{
int i;
if (new_value) {
for (i=0 ; i0 ; i--) {
if (dots==sizeof(dots_ptr)) {
break;
}
if (server_name[i]=='.') {
dots_ptr[dots]=&server_name[i];
dots++;
}
}
if (dots_ptr[1] && *(dots_ptr[1]+1)) {
server_name=dots_ptr[1]+1;
}
}
server_name_len=(strlen(server_name));
}
}
if (!static_key_len && !server_name_len) {
return NULL;
}
prefix=emalloc(static_key_len + server_name_len + 1);
if (static_key_len)
memcpy(prefix, MEMCACHE_G(session_prefix_static_key), static_key_len);
if (server_name_len)
memcpy(prefix + static_key_len, server_name, server_name_len);
prefix[static_key_len + server_name_len]='\0';
return prefix;
}
/* }}} */
/* get_key_prefix
*/
static char *get_key_prefix() {
char *server_name=NULL, *prefix=NULL;
int static_key_len=0, server_name_len=0, i;
zval *array, *token;
if (MEMCACHE_G(prefix_static_key)) {
static_key_len=strlen(MEMCACHE_G(prefix_static_key));
}
if (MEMCACHE_G(prefix_host_key)) {
if ((array = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1)) &&
Z_TYPE_P(array) == IS_ARRAY &&
(token = zend_hash_str_find(Z_ARRVAL_P(array), "SERVER_NAME", sizeof("SERVER_NAME")-1)) &&
Z_TYPE_P(token) == IS_STRING) {
if (MEMCACHE_G(prefix_host_key_remove_www) && !strncasecmp("www.",Z_STRVAL_P(token),4)) {
server_name=Z_STRVAL_P(token)+4;
} else {
server_name=Z_STRVAL_P(token);
}
if(MEMCACHE_G(prefix_host_key_remove_subdomain) && server_name) {
int dots=0;
char *dots_ptr[3]={NULL,NULL,NULL};
for (i=strlen(server_name) ; i>0 ; i--) {
if (dots==sizeof(dots_ptr)) {
break;
}
if (server_name[i]=='.') {
dots_ptr[dots]=&server_name[i];
dots++;
}
}
if (dots_ptr[1] && *(dots_ptr[1]+1)) {
server_name=dots_ptr[1]+1;
}
}
server_name_len=(strlen(server_name));
}
}
if (!static_key_len && !server_name_len) {
return NULL;
}
prefix=emalloc(static_key_len + server_name_len + 1);
if (static_key_len)
memcpy(prefix, MEMCACHE_G(prefix_static_key), static_key_len);
if (server_name_len)
memcpy(prefix + static_key_len, server_name, server_name_len);
prefix[static_key_len + server_name_len]='\0';
return prefix;
}
/* }}} */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(memcache)
{
MEMCACHE_G(session_key_prefix) = get_session_key_prefix();
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(memcache)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "MemcachePool", php_memcache_pool_class_functions);
memcache_pool_ce = zend_register_internal_class(&ce);
#if PHP_VERSION_ID >= 80200
memcache_pool_ce->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
#endif
INIT_CLASS_ENTRY(ce, "Memcache", php_memcache_class_functions);
memcache_ce = zend_register_internal_class_ex(&ce, memcache_pool_ce);
#if PHP_VERSION_ID >= 80200
memcache_ce->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
#endif
le_memcache_pool = zend_register_list_destructors_ex(_mmc_pool_list_dtor, NULL, "memcache connection", module_number);
le_memcache_server = zend_register_list_destructors_ex(NULL, _mmc_server_list_dtor, "persistent memcache connection", module_number);
#ifdef ZTS
ts_allocate_id(&memcache_globals_id, sizeof(zend_memcache_globals), (ts_allocate_ctor) php_memcache_init_globals, NULL);
#else
php_memcache_init_globals(&memcache_globals);
#endif
REGISTER_LONG_CONSTANT("MEMCACHE_COMPRESSED", MMC_COMPRESSED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MEMCACHE_USER1", MMC_RESERVED_APPLICATIONDEFINEDFLAG_12, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MEMCACHE_USER2", MMC_RESERVED_APPLICATIONDEFINEDFLAG_13, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MEMCACHE_USER3", MMC_RESERVED_APPLICATIONDEFINEDFLAG_14, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("MEMCACHE_USER4", MMC_RESERVED_APPLICATIONDEFINEDFLAG_15, CONST_CS | CONST_PERSISTENT);
REGISTER_INI_ENTRIES();
#if HAVE_MEMCACHE_SESSION
REGISTER_LONG_CONSTANT("MEMCACHE_HAVE_SESSION", 1, CONST_CS | CONST_PERSISTENT);
php_session_register_module(ps_memcache_ptr);
#else
REGISTER_LONG_CONSTANT("MEMCACHE_HAVE_SESSION", 0, CONST_CS | CONST_PERSISTENT);
#endif
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(memcache)
{
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(memcache)
{
php_info_print_table_start();
php_info_print_table_header(2, "memcache support", "enabled");
php_info_print_table_row(2, "Version", PHP_MEMCACHE_VERSION);
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
/* }}} */
/* ------------------
internal functions
------------------ */
static void _mmc_pool_list_dtor(zend_resource *rsrc) /* {{{ */
{
mmc_pool_t *pool = (mmc_pool_t *)rsrc->ptr;
if (!Z_ISUNDEF(pool->failure_callback_param)) {
Z_DELREF(pool->failure_callback_param);
ZVAL_UNDEF(&pool->failure_callback_param);
}
mmc_pool_free(pool);
}
/* }}} */
static void _mmc_server_list_dtor(zend_resource *rsrc) /* {{{ */
{
mmc_server_free((mmc_t *)rsrc->ptr);
}
/* }}} */
static int mmc_get_pool(zval *id, mmc_pool_t **pool) /* {{{ */
{
zval *zv;
if (Z_TYPE_P(id) != IS_OBJECT || (zv = zend_hash_str_find(Z_OBJPROP_P(id), "connection", sizeof("connection")-1)) == NULL) {
php_error_docref(NULL, E_WARNING, "No servers added to memcache connection");
return 0;
}
if (Z_TYPE_P(zv) != IS_RESOURCE || (*pool = zend_fetch_resource_ex(zv, "connection", le_memcache_pool)) == NULL) {
php_error_docref(NULL, E_WARNING, "Invalid MemcachePool->connection member variable");
return 0;
}
return 1;
}
/* }}} */
int mmc_stored_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /*
handles SET/ADD/REPLACE response, param is a zval pointer to store result into {{{ */
{
zval *result = (zval *)param;
if (response == MMC_OK) {
if (Z_TYPE_P(result) == IS_NULL) {
ZVAL_TRUE(result);
}
return MMC_REQUEST_DONE;
}
/* return FALSE or catch memory errors without failover */
if (response == MMC_RESPONSE_EXISTS || response == MMC_RESPONSE_OUT_OF_MEMORY || response == MMC_RESPONSE_TOO_LARGE
|| response == MMC_RESPONSE_CLIENT_ERROR) {
ZVAL_FALSE(result);
if (response != MMC_RESPONSE_EXISTS) {
/* trigger notice but no need for failover */
php_error_docref(NULL, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)",
mmc->host, mmc->tcp.port, mmc->udp.port, message, response);
}
return MMC_REQUEST_DONE;
}
return mmc_request_failure(mmc, request->io, message, message_len, 0);
}
/* }}} */
static void php_mmc_store(INTERNAL_FUNCTION_PARAMETERS, int op) /* {{{ */
{
mmc_pool_t *pool;
mmc_request_t *request;
zval *keys, *value = 0, *mmc_object = getThis();
zend_long flags = 0, exptime = 0, cas = 0;
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|zlll", &mmc_object, memcache_pool_ce, &keys, &value, &flags, &exptime, &cas) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|zlll", &keys, &value, &flags, &exptime, &cas) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) {
RETURN_FALSE;
}
RETVAL_NULL();
if (Z_TYPE_P(keys) == IS_ARRAY) {
zend_string *key;
zval *val;
zend_ulong index;
int release;
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(keys), index, key, val ) {
if (key == NULL) {
key = strpprintf(0, ZEND_ULONG_FMT, index);
release = 1;
} else {
release = 0;
}
/* allocate request */
request = mmc_pool_request(pool, MMC_PROTO_TCP,
mmc_stored_handler, return_value, mmc_pool_failover_handler, NULL);
if (mmc_prepare_key_ex(ZSTR_VAL(key), ZSTR_LEN(key), request->key, &(request->key_len), MEMCACHE_G(key_prefix)) != MMC_OK) {
php_error_docref(NULL, E_WARNING, "Invalid key");
mmc_pool_release(pool, request);
if (release) {
zend_string_release(key);
}
continue;
}
if (release) {
zend_string_release(key);
}
/* assemble command */
if (pool->protocol->store(pool, request, op, request->key, request->key_len, flags, exptime, cas, val) != MMC_OK) {
mmc_pool_release(pool, request);
continue;
}
/* schedule request */
if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) {
continue;
}
/* begin sending requests immediately */
mmc_pool_select(pool);
} ZEND_HASH_FOREACH_END();
}
else if (value) {
/* allocate request */
request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, return_value, mmc_pool_failover_handler, NULL);
if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) {
php_error_docref(NULL, E_WARNING, "Invalid key");
mmc_pool_release(pool, request);
RETURN_FALSE;
}
/* assemble command */
if (pool->protocol->store(pool, request, op, request->key, request->key_len, flags, exptime, cas, value) != MMC_OK) {
mmc_pool_release(pool, request);
RETURN_FALSE;
}
/* schedule request */
if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) {
RETURN_FALSE;
}
}
else {
WRONG_PARAM_COUNT;
}
/* execute all requests */
mmc_pool_run(pool);
if (Z_TYPE_P(return_value) == IS_NULL) {
RETVAL_FALSE;
}
}
/* }}} */
int mmc_numeric_response_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /*
handles a mutate response line, param is a zval pointer to store result into {{{ */
{
zval *result = (zval *)param;
if (response == MMC_OK) {
if (Z_TYPE_P(result) == IS_ARRAY) {
add_assoc_bool_ex(result, request->key, request->key_len + 1, 1);
}
else if (Z_TYPE_P(result) == IS_NULL) {
/* switch only from null to true, not from false to true */
ZVAL_TRUE(result);
}
return MMC_REQUEST_DONE;
}
if (response == MMC_RESPONSE_NOT_FOUND || response == MMC_RESPONSE_CLIENT_ERROR) {
if (Z_TYPE_P(result) == IS_ARRAY) {
add_assoc_bool_ex(result, request->key, request->key_len + 1, 0);
}
else {
ZVAL_FALSE(result);
}
if (response != MMC_RESPONSE_NOT_FOUND) {
php_error_docref(NULL,
E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)",
mmc->host, mmc->tcp.port,
mmc->udp.port, message, response);
}
return MMC_REQUEST_DONE;
}
return mmc_request_failure(mmc, request->io, message, message_len, 0);
}
/* }}} */
static void php_mmc_numeric(INTERNAL_FUNCTION_PARAMETERS, int deleted, int invert) /*
sends one or several commands which have a single optional numeric parameter (incr, decr, delete) {{{ */
{
mmc_pool_t *pool;
zval *mmc_object = getThis();
zval *keys;
zend_long value = 1, defval = 0, exptime = 0;
mmc_request_t *request;
void *value_handler_param[3];
int defval_used = 0;
if (mmc_object == NULL) {
if (deleted) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|l", &mmc_object, memcache_pool_ce, &keys, &value) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|lll", &mmc_object, memcache_pool_ce, &keys, &value, &defval, &exptime) == FAILURE) {
return;
}
defval_used = ZEND_NUM_ARGS() >= 4;
}
}
else {
if (deleted) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &keys, &value) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|lll", &keys, &value, &defval, &exptime) == FAILURE) {
return;
}
defval_used = ZEND_NUM_ARGS() >= 3;
}
}
if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) {
RETURN_FALSE;
}
value_handler_param[0] = return_value;
value_handler_param[1] = NULL;
value_handler_param[2] = NULL;
if (Z_TYPE_P(keys) == IS_ARRAY) {
zval *key;
if (deleted) {
/* changed to true/false by mmc_numeric_response_handler */
RETVAL_NULL();
}
else {
/* populated with responses by mmc_numeric_response_handler and mmc_value_handler_multi */
array_init(return_value);
}
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), key) {
/* allocate request */
request = mmc_pool_request(
pool, MMC_PROTO_TCP, mmc_numeric_response_handler, return_value,
mmc_pool_failover_handler, NULL);
request->value_handler = mmc_value_handler_multi;
request->value_handler_param = value_handler_param;
if (mmc_prepare_key(key, request->key, &(request->key_len)) != MMC_OK) {
mmc_pool_release(pool, request);
php_error_docref(NULL, E_WARNING, "Invalid key");
continue;
}
if (deleted) {
pool->protocol->delete(request, request->key, request->key_len, exptime);
}
else {
pool->protocol->mutate(request, key, request->key, request->key_len, invert ? -value : value, defval, defval_used, exptime);
}
/* schedule request */
if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) {
continue;
}
/* begin sending requests immediately */
mmc_pool_select(pool);
} ZEND_HASH_FOREACH_END();
}
else {
/* changed to true/false by mmc_numeric_response_handler or set to a value
* by mmc_value_handler_single if incr/decr returns one */
RETVAL_NULL();
/* allocate request */
request = mmc_pool_request(pool, MMC_PROTO_TCP,
mmc_numeric_response_handler, return_value, mmc_pool_failover_handler, NULL);
request->value_handler = mmc_value_handler_single;
request->value_handler_param = value_handler_param;
if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) {
mmc_pool_release(pool, request);
php_error_docref(NULL, E_WARNING, "Invalid key");
RETURN_FALSE;
}
if (deleted) {
pool->protocol->delete(request, request->key, request->key_len, exptime);
}
else {
pool->protocol->mutate(request, keys, request->key, request->key_len, invert ? -value : value, defval, defval_used, exptime);
}
/* schedule request */
if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) {
RETURN_FALSE;
}
}
/* execute all requests */
mmc_pool_run(pool);
if (Z_TYPE_P(return_value) == IS_NULL) {
RETURN_FALSE;
}
}
/* }}} */
/*TODO: in php73, we should use zend_register_persistent_resource , e.g.:
char *persistent_id;
persistent_id = pemalloc(key_len + 1, 1);
memcpy((char *)persistent_id, key, key_len+1);
if (zend_register_persistent_resource ( (char*) persistent_id, key_len, mmc, le_memcache_server) == NULL) ;
then not forget to pefree, check refcounts in _mmc_server_free / _mmc_server_list_dtor , etc.
otherwise we will leak mem with persistent connections /run into other trouble with later versions
*/
mmc_t *mmc_find_persistent(const char *host, int host_len, unsigned short port, unsigned short udp_port, double timeout, int retry_interval) /* {{{ */
{
mmc_t *mmc;
zend_resource *le;
char *key;
int key_len;
key_len = spprintf(&key, 0, "memcache:server:%s:%u:%u", host, port, udp_port);
if ((le = zend_hash_str_find_ptr(&EG(persistent_list), key, key_len)) == NULL) {
mmc = mmc_server_new(host, host_len, port, udp_port, 1, timeout, retry_interval);
le = zend_register_resource(mmc, le_memcache_server);
/* register new persistent connection */
if (zend_hash_str_update_mem(&EG(persistent_list), key, key_len, le, sizeof(*le)) == NULL) {
mmc_server_free(mmc);
mmc = NULL;
} else {
MEMCACHE_LIST_INSERT(mmc, le_memcache_server);
}
}
else if (le->type != le_memcache_server || le->ptr == NULL) {
zend_hash_str_del(&EG(persistent_list), key, key_len);
mmc = mmc_server_new(host, host_len, port, udp_port, 1, timeout, retry_interval);
le->type = le_memcache_server;
le->ptr = mmc;
#if PHP_VERSION_ID < 70300
GC_REFCOUNT(le) = 1;
#else
GC_SET_REFCOUNT(le, 1);
#endif
/* register new persistent connection */
if (zend_hash_str_update_mem(&EG(persistent_list), key, key_len, le, sizeof(*le)) == NULL) {
mmc_server_free(mmc);
mmc = NULL;
}
else {
MEMCACHE_LIST_INSERT(mmc, le_memcache_server);
}
}
else {
mmc = (mmc_t *)le->ptr;
mmc->timeout = double_to_timeval(timeout);
mmc->tcp.retry_interval = retry_interval;
/* attempt to reconnect this node before failover in case connection has gone away */
if (mmc->tcp.status == MMC_STATUS_CONNECTED) {
mmc->tcp.status = MMC_STATUS_UNKNOWN;
}
if (mmc->udp.status == MMC_STATUS_CONNECTED) {
mmc->udp.status = MMC_STATUS_UNKNOWN;
}
}
efree(key);
return mmc;
}
/* }}} */
static mmc_t *php_mmc_pool_addserver(
zval *mmc_object, const char *host, int host_len, long tcp_port, long udp_port, long weight,
zend_bool persistent, double timeout, long retry_interval, zend_bool status, mmc_pool_t **pool_result) /* {{{ */
{
zval *connection;
mmc_pool_t *pool;
mmc_t *mmc;
zend_resource *list_res;
if (weight < 1) {
php_error_docref(NULL, E_WARNING, "weight must be a positive integer");
return NULL;
}
if (tcp_port > 65635 || tcp_port < 0) {
php_error_docref(NULL, E_WARNING, "invalid tcp port number");
return NULL;
}
if (udp_port > 65635 || udp_port < 0) {
php_error_docref(NULL, E_WARNING, "invalid udp port number");
return NULL;
}
/* initialize pool if need be */
if ((connection = zend_hash_str_find(Z_OBJPROP_P(mmc_object), "connection", sizeof("connection")-1)) == NULL) {
pool = mmc_pool_new();
pool->failure_callback = (mmc_failure_callback) &php_mmc_failure_callback;
list_res = zend_register_resource(pool, le_memcache_pool);
add_property_resource(mmc_object, "connection", list_res);
#if PHP_VERSION_ID < 70300
GC_REFCOUNT(list_res)++;
#else
GC_ADDREF(list_res);
#endif
}
else {
pool = zend_fetch_resource_ex(connection, "connection", le_memcache_pool);
if (!pool) {
php_error_docref(NULL, E_WARNING, "Unknown connection identifier");
return NULL;
}
}
/* binary protocol isn't support over UDP yet */
if (udp_port && pool->protocol == &mmc_binary_protocol) {
php_error_docref(NULL, E_NOTICE, "binary protocol isn't support over UDP, defaulting to TCP");
udp_port = 0;
}
/* lazy initialization of server struct */
if (persistent && status) {
mmc = mmc_find_persistent(host, host_len, (unsigned short) tcp_port, (unsigned short) udp_port, timeout, retry_interval);
}
else {
mmc = mmc_server_new(host, host_len, (unsigned short) tcp_port, (unsigned short) udp_port, 0, timeout, retry_interval);
}
/* add server in failed mode */
if (!status) {
mmc->tcp.status = MMC_STATUS_FAILED;
mmc->udp.status = MMC_STATUS_FAILED;
}
mmc_pool_add(pool, mmc, weight);
if (pool_result != NULL) {
*pool_result = pool;
}
if (pool->protocol == &mmc_binary_protocol) {
zval rv1, rv2;
#if PHP_VERSION_ID >= 80000
zval *username = zend_read_property(memcache_ce, Z_OBJ_P(mmc_object), "username", strlen("username"), 1, &rv1);
zval *password = zend_read_property(memcache_ce, Z_OBJ_P(mmc_object), "password", strlen("password"), 1, &rv2);
#else
zval *username = zend_read_property(memcache_ce, mmc_object, "username", strlen("username"), 1, &rv1);
zval *password = zend_read_property(memcache_ce, mmc_object, "password", strlen("password"), 1, &rv2);
#endif
if (Z_TYPE_P(username) == IS_STRING && Z_TYPE_P(password) == IS_STRING) {
if (Z_STRLEN_P(username) > 1 && Z_STRLEN_P(password) > 1) {
mmc_request_t *request;
zval sasl_value;
/* allocate request */
request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stored_handler, &sasl_value, mmc_pool_failover_handler, NULL);
pool->protocol->set_sasl_auth_data(pool, request, Z_STRVAL_P(username), Z_STRVAL_P(password));
/* schedule request */
if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, MEMCACHE_G(redundancy)) != MMC_OK) {
return NULL;
}
}
}
}
return mmc;
}
/* }}} */
static void php_mmc_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool persistent) /* {{{ */
{
zval *mmc_object = getThis();
mmc_pool_t *pool;
mmc_t *mmc;
char *host;
size_t host_len;
zend_long tcp_port = MEMCACHE_G(default_port);
double timeout = MMC_DEFAULT_TIMEOUT;
zend_bool null_port;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l!d", &host, &host_len, &tcp_port, &null_port, &timeout) == FAILURE) {
return;
}
/* initialize pool and object if need be */
if (!mmc_object) {
zend_resource *list_res;
pool = mmc_pool_new();
pool->failure_callback = (mmc_failure_callback) &php_mmc_failure_callback;
list_res = zend_register_resource(pool, le_memcache_pool);
mmc_object = return_value;
object_init_ex(mmc_object, memcache_ce);
add_property_resource(mmc_object, "connection", list_res);
#if PHP_VERSION_ID < 70300
GC_REFCOUNT(list_res)++;
#else
GC_ADDREF(list_res);
#endif
} else {
RETVAL_TRUE;
}
mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, 0, 1, persistent, timeout, MMC_DEFAULT_RETRY, 1, NULL);
if (mmc == NULL) {
RETURN_FALSE;
}
/* force a reconnect attempt if stream EOF */
if (mmc->tcp.stream != NULL && php_stream_eof(mmc->tcp.stream)) {
mmc_server_disconnect(mmc, &(mmc->tcp));
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
/* force a tcp connect (if not persistently connected) */
if (mmc_pool_open(pool, mmc, &(mmc->tcp), 0) != MMC_OK) {
php_error_docref(NULL, E_WARNING, "Can't connect to %s:%d, %s (%d)", host, mmc->tcp.port, mmc->error ? mmc->error : "Unknown error", mmc->errnum);
RETURN_FALSE;
}
}
/* }}} */
/*
* STAT 6:chunk_size 64
*/
static int mmc_stats_parse_stat(char *start, char *end, zval *result) /* {{{ */
{
char *key;
const char *space, *colon;
zend_long index = 0;
if (Z_TYPE_P(result) != IS_ARRAY) {
array_init(result);
}
/* find space delimiting key and value */
if ((space = php_memnstr(start, " ", 1, end)) == NULL) {
return 0;
}
/* find colon delimiting subkeys */
if ((colon = php_memnstr(start, ":", 1, space - 1)) != NULL) {
zval *element, *elem;
zval new_element;
key = estrndup(start, colon - start);
/* find existing or create subkey array in result */
if ((is_numeric_string(key, colon - start, &index, NULL, 0) &&
(elem = zend_hash_index_find(Z_ARRVAL_P(result), index)) != NULL) ||
(elem = zend_hash_str_find(Z_ARRVAL_P(result), key, colon - start)) != NULL) {
element = elem;
}
else {
array_init(&new_element);
add_assoc_zval_ex(result, key, colon - start, &new_element);
element = &new_element;
}
efree(key);
return mmc_stats_parse_stat(((char *) colon) + 1, end, element);
}
/* no more subkeys, add value under last subkey */
key = estrndup(start, space - start);
add_assoc_stringl_ex(result, key, ((char *) space) - start, ((char *) space) + 1, end - ((char *) space));
efree(key);
return 1;
}
/* }}} */
/*
* ITEM test_key [3 b; 1157099416 s]
*/
static int mmc_stats_parse_item(char *start, char *end, zval *result) /* {{{ */
{
char *key;
const char *space, *value, *value_end;
zval element;
if (Z_TYPE_P(result) != IS_ARRAY) {
array_init(result);
}
/* find space delimiting key and value */
if ((space = php_memnstr(start, " ", 1, end)) == NULL) {
return 0;
}
array_init(&element);
/* parse each contained value */
for (value = php_memnstr(space, "[", 1, end); value != NULL && value <= end; value = php_memnstr(value + 1, ";", 1, end)) {
do {
value++;
} while (*value == ' ' && value <= end);
if (value <= end && (value_end = php_memnstr(value, " ", 1, end)) != NULL && value_end <= end) {
add_next_index_stringl(&element, value, value_end - value);
}
}
/* add parsed values under key */
key = estrndup(start, space - start);
add_assoc_zval_ex(result, key, space - start, &element);
efree(key);
return 1;
}
/* }}} */
static int mmc_stats_parse_generic(char *start, char *end, zval *result) /* {{{ */
{
const char *space;
char *key;
if (Z_TYPE_P(result) != IS_ARRAY) {
array_init(result);
}
if (start < end) {
if ((space = php_memnstr(start, " ", 1, end)) != NULL) {
key = estrndup(start, space - start);
add_assoc_stringl_ex(result, key, ((char *) space) - start + 1, ((char *) space) + 1, end - ((char *) space));
efree(key);
}
else {
add_next_index_stringl(result, start, end - start);
}
}
else {
return 0;
}
return 1;
}
/* }}} */
static void php_mmc_failure_callback(mmc_pool_t *pool, mmc_t *mmc, zval *param) /* {{{ */
{
zval *callback;
/* check for userspace callback */
if (!Z_ISUNDEF_P(param) && (callback = zend_hash_str_find(Z_OBJPROP_P((zval *)param), "_failureCallback", sizeof("_failureCallback")-1)) != NULL && Z_TYPE_P(callback) != IS_NULL) {
if (MEMCACHE_IS_CALLABLE(callback, 0, NULL)) {
zval retval;
zval *host, *tcp_port, *udp_port, *error, *errnum;
zval params[5];
ZVAL_UNDEF(&retval);
host = ¶ms[0];
tcp_port = ¶ms[1];
udp_port = ¶ms[2];
error = ¶ms[3];
errnum = ¶ms[4];
ZVAL_STRING(host, mmc->host);
ZVAL_LONG(tcp_port, mmc->tcp.port); ZVAL_LONG(udp_port, mmc->udp.port);
if (mmc->error != NULL) {
ZVAL_STRING(error, mmc->error);
}
else {
ZVAL_NULL(error);
}
ZVAL_LONG(errnum, mmc->errnum);
call_user_function(EG(function_table), NULL, callback, &retval, 5, params);
zval_ptr_dtor(host);
zval_ptr_dtor(tcp_port); zval_ptr_dtor(udp_port);
zval_ptr_dtor(error); zval_ptr_dtor(errnum);
if (Z_TYPE(retval) != IS_UNDEF) {
zval_ptr_dtor(&retval);
}
}
else {
php_mmc_set_failure_callback(pool, param, NULL);
php_error_docref(NULL, E_WARNING, "Invalid failure callback");
}
}
else {
php_error_docref(NULL, E_NOTICE, "Server %s (tcp %d, udp %d) failed with: %s (%d)",
mmc->host, mmc->tcp.port, mmc->udp.port, mmc->error, mmc->errnum);
}
}
/* }}} */
static void php_mmc_set_failure_callback(mmc_pool_t *pool, zval *mmc_object, zval *callback) /* {{{ */
{
// Decrease refcount of old mmc_object
if (!Z_ISUNDEF(pool->failure_callback_param)) {
Z_DELREF(pool->failure_callback_param);
}
if (callback != NULL) {
zval callback_tmp;
callback_tmp = *callback;
zval_copy_ctor(&callback_tmp);
add_property_zval(mmc_object, "_failureCallback", &callback_tmp);
zval_ptr_dtor(&callback_tmp);
pool->failure_callback_param = *mmc_object;
Z_ADDREF_P(mmc_object);
}
else {
add_property_null(mmc_object, "_failureCallback");
ZVAL_UNDEF(&pool->failure_callback_param);
}
}
/* }}} */
/* ----------------
module functions
---------------- */
/* {{{ proto bool MemcachePool::connect(string host [, int tcp_port [, int udp_port [, bool persistent [, int weight [, double timeout [, int retry_interval] ] ] ] ] ])
Connects to server and returns a Memcache object */
PHP_NAMED_FUNCTION(zif_memcache_pool_connect)
{
zval *mmc_object = getThis();
mmc_pool_t *pool;
mmc_t *mmc;
char *host;
size_t host_len;
zend_long tcp_port = MEMCACHE_G(default_port), udp_port = 0, weight = 1, retry_interval = MMC_DEFAULT_RETRY;
double timeout = MMC_DEFAULT_TIMEOUT;
zend_bool persistent = 1;
MEMCACHE_G(key_prefix)=get_key_prefix();
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llbldl",
&host, &host_len, &tcp_port, &udp_port, &persistent, &weight, &timeout, &retry_interval) == FAILURE) {
return;
}
mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, udp_port, weight, persistent, timeout, retry_interval, 1, NULL);
if (mmc == NULL) {
RETURN_FALSE;
}
/* force a reconnect attempt if stream EOF */
if (mmc->tcp.stream != NULL && php_stream_eof(mmc->tcp.stream)) {
mmc_server_disconnect(mmc, &(mmc->tcp));
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
/* force a tcp connect (if not persistently connected) */
if (mmc_pool_open(pool, mmc, &(mmc->tcp), 0) != MMC_OK) {
php_error_docref(NULL, E_WARNING, "Can't connect to %s:%d, %s (%d)", host, mmc->tcp.port, mmc->error ? mmc->error : "Unknown error", mmc->errnum);
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto object memcache_connect(string host [, int port [, double timeout ] ])
Connects to server and returns a Memcache object */
PHP_FUNCTION(memcache_connect)
{
MEMCACHE_G(key_prefix)=get_key_prefix();
php_mmc_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto object memcache_pconnect(string host [, int port [, double timeout ] ])
Connects to server and returns a Memcache object */
PHP_FUNCTION(memcache_pconnect)
{
MEMCACHE_G(key_prefix)=get_key_prefix();
php_mmc_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ proto bool MemcachePool::addServer(string host [, int tcp_port [, int udp_port [, bool persistent [, int weight [, double timeout [, int retry_interval [, bool status] ] ] ] ])
Adds a server to the pool */
PHP_NAMED_FUNCTION(zif_memcache_pool_addserver)
{
zval *mmc_object = getThis();
mmc_t *mmc;
char *host;
size_t host_len;
zend_long tcp_port = MEMCACHE_G(default_port), udp_port = 0, weight = 1, retry_interval = MMC_DEFAULT_RETRY;
double timeout = MMC_DEFAULT_TIMEOUT;
zend_bool persistent = 1, status = 1;
MEMCACHE_G(key_prefix)=get_key_prefix();
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|llbldlb",
&host, &host_len, &tcp_port, &udp_port, &persistent, &weight, &timeout, &retry_interval, &status) == FAILURE) {
return;
}
mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, udp_port, weight, persistent, timeout, retry_interval, status, NULL);
if (mmc == NULL) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto string MemcachePool::findServer(string key)
Returns the server corresponding to a key
*/
PHP_NAMED_FUNCTION(zif_memcache_pool_findserver)
{
zval *mmc_object = getThis();
mmc_pool_t *pool;
mmc_t *mmc;
zval *zkey;
char key[MMC_MAX_KEY_LEN + 1];
unsigned int key_len;
zend_string *hostname;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zkey) == FAILURE) {
return;
}
if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) {
RETURN_FALSE;
}
if (mmc_prepare_key(zkey, key, &key_len) != MMC_OK) {
php_error_docref(NULL, E_WARNING, "Invalid key");
RETURN_FALSE;
}
mmc = mmc_pool_find(pool, key, key_len);
hostname = strpprintf(0, "%s:%d", mmc->host, mmc->tcp.port);
RETURN_STR(hostname);
}
/* }}} */
/* {{{ proto bool memcache_add_server(string host [, int port [, bool persistent [, int weight [, double timeout [, int retry_interval [, bool status [, callback failure_callback ] ] ] ] ] ] ])
Adds a connection to the pool. The order in which this function is called is significant */
PHP_FUNCTION(memcache_add_server)
{
zval *mmc_object = getThis(), *failure_callback = NULL;
mmc_pool_t *pool;
mmc_t *mmc;
char *host;
size_t host_len;
zend_long tcp_port = MEMCACHE_G(default_port), weight = 1, retry_interval = MMC_DEFAULT_RETRY;
double timeout = MMC_DEFAULT_TIMEOUT;
zend_bool persistent = 1, status = 1;
MEMCACHE_G(key_prefix)=get_key_prefix();
if (mmc_object) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lbldlbz",
&host, &host_len, &tcp_port, &persistent, &weight, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|lbldlbz", &mmc_object, memcache_ce,
&host, &host_len, &tcp_port, &persistent, &weight, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) {
return;
}
}
if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) {
if (!MEMCACHE_IS_CALLABLE(failure_callback, 0, NULL)) {
php_error_docref(NULL, E_WARNING, "Invalid failure callback");
RETURN_FALSE;
}
}
mmc = php_mmc_pool_addserver(mmc_object, host, host_len, tcp_port, 0, weight, persistent, timeout, retry_interval, status, &pool);
if (mmc == NULL) {
RETURN_FALSE;
}
if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) {
php_mmc_set_failure_callback(pool, mmc_object, failure_callback);
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto bool memcache_set_server_params( string host [, int port [, double timeout [, int retry_interval [, bool status [, callback failure_callback ] ] ] ] ])
Changes server parameters at runtime */
PHP_FUNCTION(memcache_set_server_params)
{
zval *mmc_object = getThis(), *failure_callback = NULL;
mmc_pool_t *pool;
mmc_t *mmc = NULL;
zend_long tcp_port = MEMCACHE_G(default_port), retry_interval = MMC_DEFAULT_RETRY;
double timeout = MMC_DEFAULT_TIMEOUT;
zend_bool status = 1;
size_t host_len;
int i;
char *host;
if (mmc_object) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ldlbz",
&host, &host_len, &tcp_port, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|ldlbz", &mmc_object, memcache_pool_ce,
&host, &host_len, &tcp_port, &timeout, &retry_interval, &status, &failure_callback) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
for (i=0; 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, E_WARNING, "Server not found in pool");
RETURN_FALSE;
}
if (failure_callback != NULL && Z_TYPE_P(failure_callback) != IS_NULL) {
if (!MEMCACHE_IS_CALLABLE(failure_callback, 0, NULL)) {
php_error_docref(NULL, E_WARNING, "Invalid failure callback");
RETURN_FALSE;
}
}
mmc->timeout = double_to_timeval(timeout);
mmc->tcp.retry_interval = retry_interval;
/* store the smallest timeout for any server */
if (timeval_to_double(mmc->timeout) < timeval_to_double(pool->timeout)) {
pool->timeout = mmc->timeout;
}
if (!status) {
mmc->tcp.status = MMC_STATUS_FAILED;
mmc->udp.status = MMC_STATUS_FAILED;
}
else {
if (mmc->tcp.status == MMC_STATUS_FAILED) {
mmc->tcp.status = MMC_STATUS_DISCONNECTED;
}
if (mmc->udp.status == MMC_STATUS_FAILED) {
mmc->udp.status = MMC_STATUS_DISCONNECTED;
}
}
if (failure_callback != NULL) {
if (Z_TYPE_P(failure_callback) != IS_NULL) {
php_mmc_set_failure_callback(pool, mmc_object, failure_callback);
}
else {
php_mmc_set_failure_callback(pool, mmc_object, NULL);
}
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto bool memcache_set_failure_callback( callback failure_callback )
Changes the failover callback */
PHP_FUNCTION(memcache_set_failure_callback)
{
zval *mmc_object = getThis(), *failure_callback;
mmc_pool_t *pool;
if (mmc_object) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z",
&failure_callback) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &mmc_object, memcache_pool_ce,
&failure_callback) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
if (Z_TYPE_P(failure_callback) != IS_NULL) {
if (!MEMCACHE_IS_CALLABLE(failure_callback, 0, NULL)) {
php_error_docref(NULL, E_WARNING, "Invalid failure callback");
RETURN_FALSE;
}
}
if (Z_TYPE_P(failure_callback) != IS_NULL) {
php_mmc_set_failure_callback(pool, mmc_object, failure_callback);
}
else {
php_mmc_set_failure_callback(pool, mmc_object, NULL);
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto int memcache_get_server_status( string host [, int port ])
Returns server status (0 if server is failed, otherwise non-zero) */
PHP_FUNCTION(memcache_get_server_status)
{
zval *mmc_object = getThis();
mmc_pool_t *pool;
mmc_t *mmc = NULL;
zend_long tcp_port = MEMCACHE_G(default_port);
size_t host_len;
int i;
char *host;
if (mmc_object) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &host, &host_len, &tcp_port) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &mmc_object, memcache_pool_ce, &host, &host_len, &tcp_port) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
for (i=0; 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, E_WARNING, "Server not found in pool");
RETURN_FALSE;
}
RETURN_LONG(mmc->tcp.status > MMC_STATUS_FAILED ? 1 : 0);
}
/* }}} */
static int mmc_version_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /*
parses the VERSION response line, param is a zval pointer to store version into {{{ */
{
if (response != MMC_RESPONSE_ERROR) {
char *version = emalloc(message_len + 1);
if (sscanf(message, "VERSION %s", version) == 1) {
ZVAL_STRING((zval *)param, version);
efree(version);
}
else {
efree(version);
ZVAL_STRINGL((zval *)param, (char *)message, message_len);
}
return MMC_REQUEST_DONE;
}
return mmc_request_failure(mmc, request->io, message, message_len, 0);
}
/* }}} */
/* {{{ proto string memcache_get_version( object memcache )
Returns server's version */
PHP_FUNCTION(memcache_get_version)
{
mmc_pool_t *pool;
zval *mmc_object = getThis();
int i;
mmc_request_t *request;
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &mmc_object, memcache_pool_ce) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) {
RETURN_FALSE;
}
RETVAL_FALSE;
for (i=0; 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);
pool->protocol->version(request);
if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) {
mmc_pool_run(pool);
if (Z_TYPE_P(return_value) == IS_STRING) {
break;
}
}
}
if (Z_TYPE_P(return_value) == IS_NULL) {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto bool memcache_add(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ])
Adds new item. Item with such key should not exist. */
PHP_FUNCTION(memcache_add)
{
php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_ADD);
}
/* }}} */
/* {{{ proto bool memcache_set(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ])
Sets the value of an item. Item may exist or not */
PHP_FUNCTION(memcache_set)
{
php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_SET);
}
/* }}} */
/* {{{ proto bool memcache_replace(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] )
Replaces existing item. Returns false if item doesn't exist */
PHP_FUNCTION(memcache_replace)
{
php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_REPLACE);
}
/* }}} */
/* {{{ proto bool memcache_cas(object memcache, mixed key [, mixed var [, int flag [, int exptime [, long cas ] ] ] ])
Sets the value of an item if the CAS value is the same (Compare-And-Swap) */
PHP_FUNCTION(memcache_cas)
{
php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_CAS);
}
/* }}} */
/* {{{ proto bool memcache_prepend(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ])
Appends a value to the stored value, value must exist */
PHP_FUNCTION(memcache_append)
{
php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_APPEND);
}
/* }}} */
/* {{{ proto bool memcache_prepend(object memcache, mixed key [, mixed var [, int flag [, int exptime ] ] ])
Prepends a value to the stored value, value must exist */
PHP_FUNCTION(memcache_prepend)
{
php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, MMC_OP_PREPEND);
}
/* }}} */
int mmc_value_handler_multi(
const char *key, unsigned int key_len, zval *value,
unsigned int flags, unsigned long cas, void *param) /*
receives a multiple values, param is a zval** array to store value and flags in {{{ */
{
zval **result = (zval **)param;
/* add value to result */
if (Z_TYPE_P(result[0]) != IS_ARRAY) {
array_init(result[0]);
}
ZEND_ASSERT(key_len > 0);
add_assoc_zval_ex(result[0], (char *)key, key_len, value);
/* add flags to result */
if (result[1] != NULL) {
if (Z_TYPE_P(result[1]) != IS_ARRAY) {
array_init(result[1]);
}
add_assoc_long_ex(result[1], (char *)key, key_len, flags);
}
/* add CAS value to result */
if (result[2] != NULL) {
if (Z_TYPE_P(result[2]) != IS_ARRAY) {
array_init(result[2]);
}
add_assoc_long_ex(result[2], (char *)key, key_len, cas);
}
return MMC_REQUEST_DONE;
}
/* }}} */
int mmc_value_handler_single(
const char *key, unsigned int key_len, zval *value,
unsigned int flags, unsigned long cas, void *param) /*
receives a single value, param is a zval pointer to store value to {{{ */
{
zval **result = (zval **)param;
ZVAL_ZVAL(result[0], value, 1, 1);
if (result[1] != NULL) {
ZVAL_LONG(result[1], flags);
}
if (result[2] != NULL) {
ZVAL_LONG(result[2], cas);
}
return MMC_REQUEST_DONE;
}
/* }}} */
static int mmc_value_failover_handler(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param) /*
uses keys and return value to reschedule requests to other servers, param is a zval ** pointer {{{ */
{
zval *keys = ((zval **)param)[0], **value_handler_param = (zval **)((void **)param)[1];
zval *key;
if (!MEMCACHE_G(allow_failover) || request->failed_servers.len >= MEMCACHE_G(max_failover_attempts)) {
mmc_pool_release(pool, request);
return MMC_REQUEST_FAILURE;
}
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), key) {
if (Z_TYPE_P(value_handler_param[0]) != IS_ARRAY ||
!zend_hash_str_exists(Z_ARRVAL_P(value_handler_param[0]), Z_STRVAL_P(key), Z_STRLEN_P(key)))
{
mmc_pool_schedule_get(pool, MMC_PROTO_UDP,
value_handler_param[2] != NULL ? MMC_OP_GETS : MMC_OP_GET, key,
request->value_handler, request->value_handler_param,
request->failover_handler, request->failover_handler_param, request);
}
} ZEND_HASH_FOREACH_END();
mmc_pool_release(pool, request);
return MMC_OK;
}
/* }}}*/
/* {{{ proto mixed memcache_get( object memcache, mixed key [, mixed &flags [, mixed &cas ] ] )
Returns value of existing item or false */
PHP_FUNCTION(memcache_get)
{
mmc_pool_t *pool;
zval *keys, *flags = NULL, *cas = NULL, *mmc_object = getThis();
void *value_handler_param[3], *failover_handler_param[2];
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|z/z/", &mmc_object, memcache_pool_ce, &keys, &flags, &cas) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|z/z/", &keys, &flags, &cas) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) {
RETURN_FALSE;
}
value_handler_param[0] = return_value;
value_handler_param[1] = flags;
value_handler_param[2] = cas;
if (Z_TYPE_P(keys) == IS_ARRAY) {
zval *zv;
/* return empty array if no keys found */
array_init(return_value);
failover_handler_param[0] = keys;
failover_handler_param[1] = value_handler_param;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), zv) {
/* schedule request */
mmc_pool_schedule_get(pool, MMC_PROTO_UDP,
cas != NULL ? MMC_OP_GETS : MMC_OP_GET, zv,
mmc_value_handler_multi, value_handler_param,
mmc_value_failover_handler, failover_handler_param, NULL);
} ZEND_HASH_FOREACH_END();
}
else {
mmc_request_t *request;
/* return false if key isn't found */
ZVAL_FALSE(return_value);
/* allocate request */
request = mmc_pool_request_get(
pool, MMC_PROTO_TCP,
mmc_value_handler_single, value_handler_param,
mmc_pool_failover_handler, NULL);
if (mmc_prepare_key(keys, request->key, &(request->key_len)) != MMC_OK) {
mmc_pool_release(pool, request);
php_error_docref(NULL, E_WARNING, "Invalid key");
RETURN_FALSE;
}
pool->protocol->get(request, cas != NULL ? MMC_OP_GETS : MMC_OP_GET, keys, request->key, request->key_len);
/* schedule request */
if (mmc_pool_schedule_key(pool, request->key, request->key_len, request, 1) != MMC_OK) {
RETURN_FALSE;
}
}
/* execute all requests */
mmc_pool_run(pool);
if (Z_TYPE_P(return_value) == IS_NULL) {
RETURN_FALSE;
}
}
/* }}} */
static int mmc_stats_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /*
parses the stats response line, param is a zval pointer to store stats into {{{ */
{
if (response != MMC_RESPONSE_ERROR)
{
char *line = (char *)message;
if(!message_len) {
return MMC_REQUEST_DONE;
}
if (mmc_str_left(line, "RESET", message_len, sizeof("RESET")-1)) {
ZVAL_TRUE((zval *)param);
return MMC_REQUEST_DONE;
}
else if (mmc_str_left(line, "STAT ", message_len, sizeof("STAT ")-1)) {
if (mmc_stats_parse_stat(line + sizeof("STAT ")-1, line + message_len - 1, (zval *)param)) {
return MMC_REQUEST_AGAIN;
}
}
else if (mmc_str_left(line, "ITEM ", message_len, sizeof("ITEM ")-1)) {
if (mmc_stats_parse_item(line + sizeof("ITEM ")-1, line + message_len - 1, (zval *)param)) {
return MMC_REQUEST_AGAIN;
}
}
else if (mmc_str_left(line, "END", message_len, sizeof("END")-1)) {
return MMC_REQUEST_DONE;
}
else if (mmc_stats_parse_generic(line, line + message_len, (zval *)param)) {
return MMC_REQUEST_AGAIN;
}
zval_dtor((zval *)param);
ZVAL_FALSE((zval *)param);
return MMC_REQUEST_FAILURE;
}
return mmc_request_failure(mmc, request->io, message, message_len, 0);
}
/* }}} */
static int mmc_stats_checktype(const char *type) { /* {{{ */
return type == NULL ||
!strcmp(type, "reset") ||
!strcmp(type, "malloc") ||
!strcmp(type, "slabs") ||
!strcmp(type, "cachedump") ||
!strcmp(type, "items") ||
!strcmp(type, "sizes");
}
/* }}} */
/* {{{ proto array memcache_get_stats( object memcache [, string type [, int slabid [, int limit ] ] ])
Returns server's statistics */
PHP_FUNCTION(memcache_get_stats)
{
mmc_pool_t *pool;
zval *mmc_object = getThis();
char *type = NULL;
int i;
size_t type_len = 0;
zend_long slabid = 0, limit = MMC_DEFAULT_CACHEDUMP_LIMIT;
mmc_request_t *request;
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|sll", &mmc_object, memcache_pool_ce, &type, &type_len, &slabid, &limit) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sll", &type, &type_len, &slabid, &limit) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) {
RETURN_FALSE;
}
if (!mmc_stats_checktype(type)) {
php_error_docref(NULL, E_WARNING, "Invalid stats type");
RETURN_FALSE;
}
ZVAL_FALSE(return_value);
for (i=0; inum_servers; i++) {
request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stats_handler, return_value, NULL, NULL);
pool->protocol->stats(request, type, slabid, limit);
/* run command and check for valid return value */
if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) {
mmc_pool_run(pool);
if (Z_TYPE_P(return_value) != IS_FALSE) {
break;
}
}
}
/* execute all requests */
mmc_pool_run(pool);
if (Z_TYPE_P(return_value) == IS_NULL) {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto array memcache_get_extended_stats( object memcache [, string type [, int slabid [, int limit ] ] ])
Returns statistics for each server in the pool */
PHP_FUNCTION(memcache_get_extended_stats)
{
mmc_pool_t *pool;
zval *mmc_object = getThis();
char *host, *type = NULL;
int i;
size_t host_len, type_len = 0;
zend_long slabid = 0, limit = MMC_DEFAULT_CACHEDUMP_LIMIT;
mmc_request_t *request;
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|sll", &mmc_object, memcache_pool_ce, &type, &type_len, &slabid, &limit) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sll", &type, &type_len, &slabid, &limit) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool) || !pool->num_servers) {
RETURN_FALSE;
}
if (!mmc_stats_checktype(type)) {
php_error_docref(NULL, E_WARNING, "Invalid stats type");
RETURN_FALSE;
}
array_init(return_value);
for (i=0; inum_servers; i++) {
zval new_stats;
zval *stats;
ZVAL_FALSE(&new_stats);
host_len = spprintf(&host, 0, "%s:%u", pool->servers[i]->host, pool->servers[i]->tcp.port);
stats = zend_symtable_str_update(Z_ARRVAL_P(return_value), host, host_len, &new_stats);
efree(host);
request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_stats_handler, stats, NULL, NULL);
pool->protocol->stats(request, type, slabid, limit);
if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) {
mmc_pool_run(pool);
}
}
/* execute all requests */
mmc_pool_run(pool);
if (Z_TYPE_P(return_value) == IS_NULL) {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto array memcache_set_compress_threshold( object memcache, int threshold [, float min_savings ] )
Set automatic compress threshold */
PHP_FUNCTION(memcache_set_compress_threshold)
{
mmc_pool_t *pool;
zval *mmc_object = getThis();
zend_long threshold;
double min_savings = MMC_DEFAULT_SAVINGS;
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|d", &mmc_object, memcache_pool_ce, &threshold, &min_savings) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|d", &threshold, &min_savings) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
if (threshold < 0) {
php_error_docref(NULL, E_WARNING, "threshold must be a positive integer");
RETURN_FALSE;
}
pool->compress_threshold = threshold;
if (min_savings != MMC_DEFAULT_SAVINGS) {
if (min_savings < 0 || min_savings > 1) {
php_error_docref(NULL, E_WARNING, "min_savings must be a float in the 0..1 range");
RETURN_FALSE;
}
pool->min_compress_savings = min_savings;
}
else {
pool->min_compress_savings = MMC_DEFAULT_SAVINGS;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto bool memcache_delete(object memcache, mixed key [, int exptime ])
Deletes existing item */
PHP_FUNCTION(memcache_delete)
{
php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
}
/* }}} */
/* {{{ proto mixed memcache_increment(object memcache, mixed key [, int value [, int defval [, int exptime ] ] ])
Increments existing variable */
PHP_FUNCTION(memcache_increment)
{
php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
}
/* }}} */
/* {{{ proto mixed memcache_decrement(object memcache, mixed key [, int value [, int defval [, int exptime ] ] ])
Decrements existing variable */
PHP_FUNCTION(memcache_decrement)
{
php_mmc_numeric(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
}
/* }}} */
/* {{{ proto bool memcache_close( object memcache )
Closes connection to memcached */
PHP_FUNCTION(memcache_close)
{
mmc_pool_t *pool;
zval *mmc_object = getThis();
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &mmc_object, memcache_pool_ce) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
mmc_pool_close(pool);
RETURN_TRUE;
}
/* }}} */
static int mmc_flush_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param) /*
parses the OK response line, param is an int pointer to increment on success {{{ */
{
if (response == MMC_OK) {
(*((int *)param))++;
return MMC_REQUEST_DONE;
}
if (response == MMC_RESPONSE_CLIENT_ERROR) {
php_error_docref(NULL, E_NOTICE,
"Server %s (tcp %d, udp %d) failed with: %s (%d)",
mmc->host, mmc->tcp.port,
mmc->udp.port, message, response);
return MMC_REQUEST_DONE;
}
return mmc_request_failure(mmc, request->io, message, message_len, 0);
}
/* }}} */
/* {{{ proto bool memcache_flush( object memcache [, int delay ] )
Flushes cache, optionally at after the specified delay */
PHP_FUNCTION(memcache_flush)
{
mmc_pool_t *pool;
zval *mmc_object = getThis();
mmc_request_t *request;
unsigned int i, responses = 0;
zend_long delay = 0;
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &mmc_object, memcache_pool_ce, &delay) == FAILURE) {
return;
}
}
else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &delay) == FAILURE) {
return;
}
}
if (!mmc_get_pool(mmc_object, &pool)) {
RETURN_FALSE;
}
for (i=0; inum_servers; i++) {
request = mmc_pool_request(pool, MMC_PROTO_TCP, mmc_flush_handler, &responses, NULL, NULL);
pool->protocol->flush(request, delay);
if (mmc_pool_schedule(pool, pool->servers[i], request) == MMC_OK) {
/* begin sending requests immediately */
mmc_pool_select(pool);
}
}
/* execute all requests */
mmc_pool_run(pool);
if (responses < pool->num_servers) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto string memcache_set_sasl_data(object memcache, string username, string password)
Set credentials for sals authentification */
PHP_FUNCTION(memcache_set_sasl_auth_data)
{
zval *mmc_object = getThis();
char *user;
size_t user_length;
char *password;
size_t password_length;
if (mmc_object == NULL) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oss", &mmc_object, memcache_pool_ce, &user, &user_length, &password, &password_length) == FAILURE) {
return;
}
} else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &user, &user_length, &password, &password_length) == FAILURE) {
return;
}
}
if (user_length < 1 || password_length < 1) {
RETURN_FALSE;
}
#if PHP_VERSION_ID >= 80000
zend_update_property_stringl(memcache_pool_ce, Z_OBJ_P(mmc_object), "username", strlen("username"), user, user_length);
zend_update_property_stringl(memcache_pool_ce, Z_OBJ_P(mmc_object), "password", strlen("password"), password, password_length);
#else
zend_update_property_stringl(memcache_pool_ce, mmc_object, "username", strlen("username"), user, user_length);
zend_update_property_stringl(memcache_pool_ce, mmc_object, "password", strlen("password"), password, password_length);
#endif
RETURN_TRUE;
}
/* }}} */
/* {{{ proto bool memcache_debug( bool onoff ) */
PHP_FUNCTION(memcache_debug)
{
php_error_docref(NULL, E_WARNING, "memcache_debug() is deprecated, please use a debugger (like Eclipse + CDT)");
RETVAL_BOOL(PHP_DEBUG);
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/
memcache-8.2/src/memcache_pool.c 0000644 0000765 0000024 00000137056 14423450270 015676 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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_string.h"
#include "zend_smart_str.h"
#include "memcache_pool.h"
ZEND_DECLARE_MODULE_GLOBALS(memcache)
#if PHP_VERSION_ID >= 80000
#define mmc_string_concat2 zend_string_concat2
#else
static zend_string* mmc_string_concat2(
const char *str1, size_t str1_len,
const char *str2, size_t str2_len)
{
size_t len = str1_len + str2_len;
zend_string *res = zend_string_alloc(len, 0);
memcpy(ZSTR_VAL(res), str1, str1_len);
memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len);
ZSTR_VAL(res)[len] = '\0';
return res;
}
#endif
MMC_POOL_INLINE void mmc_buffer_alloc(mmc_buffer_t *buffer, unsigned int size) /*
ensures space for an additional size bytes {{{ */
{
#if PHP_VERSION_ID < 70200
register size_t newlen;
#endif
smart_string_alloc((&(buffer->value)), size, 0);
}
/* }}} */
MMC_POOL_INLINE void mmc_buffer_free(mmc_buffer_t *buffer) /* {{{ */
{
if (buffer->value.c != NULL) {
smart_string_free(&(buffer->value));
}
ZEND_SECURE_ZERO(buffer, 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) /*
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) /*
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) /* {{{ */
{
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) /* {{{ */
{
return php_stream_get_line(io->stream, 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) /* {{{ */
{
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);
}
}
/* }}} */
static int mmc_request_read_udp(mmc_t *mmc, mmc_request_t *request) /*
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);
}
if (bytes > MMC_MAX_UDP_LEN) {
return mmc_server_failure(mmc, request->io, "Server sent packet larger than MMC_MAX_UDP_LEN bytes", 0);
}
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, 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) /* {{{ */
{
/* 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 */
ZEND_SECURE_ZERO(buffer, sizeof(*buffer));
mmc_buffer_alloc(buffer, prev.value.len + result_len);
/* append prev header */
smart_string_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_string_appendl(&(buffer->value), value, value_len);
*flags &= ~MMC_COMPRESSED;
}
if (copy) {
mmc_buffer_free(&prev);
}
}
else if (!copy) {
smart_string_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) /*
does serialization and compression to pack a zval into the buffer {{{ */
{
if (*flags & 0xffff & ~MMC_COMPRESSED) {
php_error_docref(NULL, 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);
break;
case IS_LONG:
*flags |= MMC_TYPE_LONG;
*flags &= ~MMC_COMPRESSED;
smart_string_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_string_appendl(&(buffer->value), buf, len);
break;
}
case IS_TRUE:
case IS_FALSE:
*flags |= MMC_TYPE_BOOL;
*flags &= ~MMC_COMPRESSED;
smart_string_appendc(&(buffer->value), Z_TYPE_P(value) == IS_TRUE ? '1' : '0');
break;
default: {
php_serialize_data_t value_hash;
zval value_copy, *value_copy_ptr;
size_t prev_len = buffer->value.len;
smart_str buf = {0};
/* 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(&buf, value_copy_ptr, &value_hash);
PHP_VAR_SERIALIZE_DESTROY(value_hash);
if (!buf.s) {
zval_dtor(&value_copy);
php_error_docref(NULL, E_WARNING, "Failed to serialize value");
return MMC_REQUEST_FAILURE;
}
smart_string_appendl(&(buffer->value), ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
smart_str_free(&buf);
/* 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, 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);
}
}
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) /*
does uncompression and unserializing to reconstruct a zval {{{ */
{
char *data = NULL;
unsigned long data_len;
zval object;
if (flags & MMC_COMPRESSED) {
if (mmc_uncompress(buffer->value.c, bytes, &data, &data_len) != MMC_OK) {
php_error_docref(NULL, 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)) {
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, 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);
}
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);
efree(data);
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);
}
}
/* }}}*/
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) /* {{{ */
{
mmc_t *mmc = pemalloc(sizeof(mmc_t), persistent);
ZEND_SECURE_ZERO(mmc, 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) /* {{{ */
{
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) /* {{{ */
{
_mmc_server_disconnect(mmc, io, 1);
}
/* }}} */
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) /*
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));
mmc_server_disconnect(mmc, &(mmc->udp));
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);
}
mmc_queue_free(&readqueue);
/* fire userspace failure event */
if (pool->failure_callback != NULL) {
pool->failure_callback(pool, mmc, &pool->failure_callback_param);
}
}
/* }}} */
int mmc_server_failure(mmc_t *mmc, mmc_stream_t *io, const char *error, int errnum) /*
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) /*
checks for a valid server generated error message and calls mmc_server_failure() {{{ */
{
if (message_len) {
return mmc_server_failure(mmc, io, message, errnum);
}
return mmc_server_failure(mmc, io, "Malformed server response", errnum);
}
/* }}} */
static int mmc_server_connect(mmc_pool_t *pool, mmc_t *mmc, mmc_stream_t *io, int udp) /*
connects a stream, calls mmc_server_deactivate() on failure {{{ */
{
char *host, *hash_key = NULL;
zend_string *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);
}
if (mmc->persistent) {
spprintf(&hash_key, 0, "memcache:stream:%s:%u:%d", mmc->host, io->port, udp);
}
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,
REPORT_ERRORS | (mmc->persistent ? STREAM_OPEN_PERSISTENT : 0),
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
hash_key, &tv, NULL, &errstr, &errnum);
efree(host);
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) {
if (errstr != NULL) {
zend_string* error = mmc_string_concat2(
"Connection failed: ", sizeof("Connection failed: ") - 1,
ZSTR_VAL(errstr), ZSTR_LEN(errstr));
mmc_server_seterror(mmc, ZSTR_VAL(error), errnum);
zend_string_release(error);
} else {
mmc_server_seterror(mmc, "Connection failed", errnum);
}
mmc_server_deactivate(pool, mmc);
if (errstr != NULL) {
efree(errstr);
}
return MMC_REQUEST_FAILURE;
}
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);
io->fd = fd;
io->status = MMC_STATUS_CONNECTED;
/* 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) /*
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) /*
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) /* {{{ */
{
mmc_server_sleep(mmc);
_mmc_server_disconnect(mmc, &(mmc->tcp), 0);
_mmc_server_disconnect(mmc, &(mmc->udp), 0);
pefree(mmc->host, mmc->persistent);
pefree(mmc, mmc->persistent);
}
/* }}} */
static void mmc_pool_init_hash(mmc_pool_t *pool) /* {{{ */
{
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() /* {{{ */
{
mmc_pool_t *pool = emalloc(sizeof(mmc_pool_t));
ZEND_SECURE_ZERO(pool, 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);
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) /* {{{ */
{
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]);
} else {
mmc_server_free(pool->servers[i]);
}
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) /*
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]);
} else {
mmc_server_free(pool->servers[i]);
}
}
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);
}
}
/* }}} */
int mmc_pool_open(mmc_pool_t *pool, mmc_t *mmc, mmc_stream_t *io, int udp) /*
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);
}
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) /*
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);
} 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) /*
maps a key to a non-failed server {{{ */
{
mmc_t *mmc = pool->hash->find_server(pool->hash_state, key, key_len);
/* check validity and try to failover otherwise */
if (!mmc_server_valid(mmc) && MEMCACHE_G(allow_failover)) {
unsigned int last_index = 0;
do {
mmc = mmc_pool_find_next(pool, key, key_len, NULL, &last_index);
} while (!mmc_server_valid(mmc) && 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) /*
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));
} while (!mmc_server_valid(mmc) && request->failed_index < MEMCACHE_G(max_failover_attempts) && request->failed_servers.len < pool->num_servers);
return mmc_pool_schedule(pool, mmc, request);
}
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) /*
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) /*
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) /* {{{ */
{
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_string_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) /*
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);
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) /*
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);
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) /*
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);
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) /* {{{ */
{
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) == 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) == 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);
}
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) /*
schedules a request against a server, return MMC_OK on success {{{ */
{
if (!mmc_server_valid(mmc)) {
/* delegate to failover handler if connect fails */
return request->failover_handler(pool, mmc, request, request->failover_handler_param);
}
/* 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) != MMC_OK) {
return request->failover_handler(pool, mmc, request, request->failover_handler_param);
}
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) /*
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);
result = mmc_pool_schedule(pool, mmc, request);
/* 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);
if (mmc_server_valid(mmc)) {
mmc_pool_schedule(pool, mmc, mmc_pool_clone_request(pool, request));
}
}
mmc_queue_free(&skip_servers);
return result;
}
return mmc_pool_schedule(pool, mmc_pool_find(pool, key, key_len), request);
}
/* }}} */
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) /*
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, E_WARNING, "Invalid key");
return MMC_REQUEST_FAILURE;
}
mmc = mmc_pool_find(pool, key, key_len);
if (!mmc_server_valid(mmc)) {
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);
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);
/* begin sending requests immediately */
mmc_pool_select(pool);
mmc->buildreq = mmc_pool_request_get(
pool, protocol, value_handler, value_handler_param,
failover_handler, failover_handler_param);
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) /* {{{ */
{
if (result == 0) {
/* timeout expired, non-responsive server */
if (mmc_server_failure(mmc, request->io, "Network timeout", 0) == 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);
return MMC_REQUEST_FAILURE;
}
/* }}} */
static void mmc_select_retry(mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request) /*
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);
/* 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);
}
/* }}} */
void mmc_pool_select(mmc_pool_t *pool) /*
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) == MMC_REQUEST_RETRY) {
/* allow request to try and send again */
mmc_select_retry(pool, mmc, mmc->sendreq);
}
}
}
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) == MMC_REQUEST_RETRY) {
/* allow request to try and read again */
mmc_select_retry(pool, mmc, mmc->readreq);
}
}
}
}
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);
/* 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);
/* 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);
break;
case MMC_REQUEST_DONE:
/* shift next request into send slot */
mmc_pool_slot_send(pool, mmc, mmc_queue_pop(&(mmc->sendqueue)), 1);
break;
case MMC_REQUEST_MORE:
/* send more data to socket */
break;
default:
php_error_docref(NULL, 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);
}
if ( ! pool->sending->len && ( mmc->sendreq != NULL || mmc->sendqueue.len ) ) {
php_error_docref( NULL, E_WARNING, "mmc_pool_select() failed to cleanup when sending! Sendqueue: %d", mmc->sendqueue.len );
}
}
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);
if (result != MMC_OK) {
switch (result) {
case MMC_REQUEST_FAILURE:
/* take server offline and failover requests */
mmc_server_deactivate(pool, mmc);
break;
case MMC_REQUEST_RETRY:
/* allow request to reschedule itself */
mmc_select_retry(pool, mmc, mmc->readreq);
break;
case MMC_REQUEST_MORE:
/* add server to read queue once more */
mmc_queue_push(pool->reading, mmc);
break;
default:
php_error_docref(NULL, 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);
/* 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);
break;
case MMC_REQUEST_RETRY:
/* allow request to reschedule itself */
mmc_select_retry(pool, mmc, mmc->readreq);
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);
/* shift next request into send slot */
mmc_pool_slot_send(pool, mmc, mmc_queue_pop(&(mmc->sendqueue)), 1);
/* 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);
if (result == MMC_REQUEST_FAILURE) {
/* take server offline and failover requests */
mmc_server_deactivate(pool, mmc);
} else {
mmc_select_retry(pool, mmc, mmc->readreq);
}
}
break;
case MMC_REQUEST_AGAIN:
/* request wants another loop */
break;
default:
php_error_docref(NULL, 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);
}
if ( ! pool->reading->len && ( mmc->readreq != NULL || mmc->readqueue.len ) ) {
php_error_docref( NULL, E_WARNING, "mmc_pool_select() failed to cleanup when reading! Readqueue: %d", mmc->readqueue.len );
}
}
pool->in_select = 0;
}
/* }}} */
void mmc_pool_schedule_pending(mmc_pool_t *pool) {
mmc_t *mmc;
while ((mmc = mmc_queue_pop(&(pool->pending))) != NULL) {
pool->protocol->end_get(mmc->buildreq);
mmc_pool_schedule(pool, mmc, mmc->buildreq);
mmc->buildreq = NULL;
}
}
void mmc_pool_run(mmc_pool_t *pool) /*
runs all scheduled requests to completion {{{ */
{
mmc_t *mmc;
mmc_pool_schedule_pending(pool);
while (pool->reading->len || pool->sending->len) {
mmc_pool_select(pool);
mmc_pool_schedule_pending(pool);
}
}
/* }}} */
MMC_POOL_INLINE int mmc_prepare_key_ex(const char *key, unsigned int key_len, char *result, unsigned int *result_len, char *prefix) /* {{{ */
{
unsigned int i, j, prefix_len=0;
if (key_len == 0) {
return MMC_REQUEST_FAILURE;
}
if (prefix) {
prefix_len = strlen(prefix);
}
*result_len = (prefix_len + key_len) < MMC_MAX_KEY_LEN ? (prefix_len + key_len) : MMC_MAX_KEY_LEN;
result[*result_len] = '\0';
if (prefix_len) {
for (i=0; i ' ' ? prefix[i] : '_';
}
for (j=0; j+i<*result_len; j++) {
result[j+i] = ((unsigned char)key[j]) > ' ' ? key[j] : '_';
}
result[*result_len] = '\0';
} else {
for (i=0; i<*result_len; i++) {
result[i] = ((unsigned char)key[i]) > ' ' ? key[i] : '_';
}
}
return MMC_OK;
}
/* }}} */
MMC_POOL_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, MEMCACHE_G(key_prefix));
} 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, MEMCACHE_G(key_prefix));
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-8.2/src/memcache_queue.c 0000644 0000765 0000024 00000006721 14423450270 016043 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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);
}
ZEND_SECURE_ZERO(queue, 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-8.2/src/memcache_session.c 0000644 0000765 0000024 00000041444 14423450270 016403 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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_string.h"
#include "ext/standard/url.h"
#include "ext/session/php_session.h"
#ifdef PHP_WIN32
#include "win32/time.h"
#endif
#include "php_memcache.h"
static int mmc_deleted_handler(mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param);
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;
const char *path = MEMCACHE_G(session_save_path);
if (!path) {
/* allow to work with standard session.save_path option
and session_save_path function */
path = save_path;
}
if (!path) {
PS_SET_MOD_DATA(NULL);
ZEND_ASSERT(0 && "open");
return FAILURE;
}
pool = mmc_pool_new();
for (i=0,path_len=strlen(path); iquery != NULL) {
array_init(¶ms);
/* parse parameters */
#if PHP_VERSION_ID < 70300
sapi_module.treat_data(PARSE_STRING, estrdup(url->query), ¶ms);
#else
sapi_module.treat_data(PARSE_STRING, estrdup(ZSTR_VAL(url->query)), ¶ms);
#endif
if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent", sizeof("persistent")-1)) != NULL) {
convert_to_boolean_ex(param);
persistent = Z_TYPE_P(param) == IS_TRUE;
}
if ((param = zend_hash_str_find(Z_ARRVAL(params), "udp_port", sizeof("udp_port")-1)) != NULL) {
convert_to_long_ex(param);
udp_port = Z_LVAL_P(param);
}
if ((param = zend_hash_str_find(Z_ARRVAL(params), "weight", sizeof("weight")-1)) != NULL) {
convert_to_long_ex(param);
weight = Z_LVAL_P(param);
}
if ((param = zend_hash_str_find(Z_ARRVAL(params), "timeout", sizeof("timeout")-1)) != NULL) {
convert_to_double_ex(param);
timeout = Z_DVAL_P(param);
}
if ((param = zend_hash_str_find(Z_ARRVAL(params), "retry_interval", sizeof("retry_interval")-1)) != NULL) {
convert_to_long_ex(param);
retry_interval = Z_LVAL_P(param);
}
zval_ptr_dtor(¶ms);
}
#if PHP_VERSION_ID < 70300
if (url->scheme && url->path && !strcmp(url->scheme, "file")) {
char *host;
int host_len = spprintf(&host, 0, "unix://%s", url->path);
#else
if (url->scheme && url->path && !strcmp(ZSTR_VAL(url->scheme), "file")) {
char *host;
int host_len = spprintf(&host, 0, "unix://%s", ZSTR_VAL(url->path));
#endif
/* 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);
}
else {
mmc = mmc_server_new(host, host_len, 0, 0, 0, timeout, retry_interval);
}
efree(host);
}
else {
if (url->host == NULL || weight <= 0 || timeout <= 0) {
php_url_free(url);
mmc_pool_free(pool);
PS_SET_MOD_DATA(NULL);
return FAILURE;
}
#if PHP_VERSION_ID < 70300
if (persistent) {
mmc = mmc_find_persistent(url->host, strlen(url->host), url->port, udp_port, timeout, retry_interval);
}
else {
mmc = mmc_server_new(url->host, strlen(url->host), url->port, udp_port, 0, timeout, retry_interval);
}
#else
if (persistent) {
mmc = mmc_find_persistent(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, udp_port, timeout, retry_interval);
}
else {
mmc = mmc_server_new(ZSTR_VAL(url->host), ZSTR_LEN(url->host), url->port, udp_port, 0, timeout, retry_interval);
}
#endif
}
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);
PS_SET_MOD_DATA(NULL);
ZEND_ASSERT(0 &&"open");
return FAILURE;
}
/* }}} */
/* {{{ PS_CLOSE_FUNC
*/
PS_CLOSE_FUNC(memcache)
{
mmc_pool_t *pool = PS_GET_MOD_DATA();
if (pool) {
mmc_pool_free(pool);
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) /* {{{ */
{
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);
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);
/* 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);
/* prepare key */
if (mmc_prepare_key_ex(Z_STRVAL_P(zkey), Z_STRLEN_P(zkey), dreq->key, &(dreq->key_len), MEMCACHE_G(session_key_prefix)) != 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);
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)
{
*val = ZSTR_EMPTY_ALLOC();
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_STR(&zkey, key);
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) != MMC_OK) {
return FAILURE;
}
/* find next server in line */
prev_index = last_index;
mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index);
/* schedule the requests */
if (!mmc_server_valid(mmc) ||
mmc_pool_schedule(pool, mmc, lockrequest) != MMC_OK ||
/*pool->protocol != &mmc_binary_protocol && */mmc_pool_schedule(pool, mmc, addrequest) != MMC_OK ||
mmc_pool_schedule(pool, mmc, datarequest) != 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);
if ((Z_TYPE(lockresult) == IS_LONG && Z_LVAL(lockresult) == 1) || ((Z_TYPE(addresult) == IS_TRUE || Z_TYPE(addresult) == IS_FALSE) && Z_TYPE(addresult) == IS_TRUE)) {
if (Z_TYPE(dataresult) == IS_STRING) {
/* break if successfully locked with existing value */
mmc_queue_free(&skip_servers);
*val = zend_string_init(Z_STRVAL(dataresult), Z_STRLEN(dataresult), 0);
zval_ptr_dtor(&dataresult);
return SUCCESS;
}
/* if missing value, skip this server and try next */
zval_dtor(&dataresult);
mmc_queue_push(&skip_servers, mmc);
/* if it is the last server in pool and connection was ok return success and empty string due to php70 changes */
if (skip_servers.len == pool->num_servers && skip_servers.len < MEMCACHE_G(session_redundancy)) {
*val = ZSTR_EMPTY_ALLOC();
mmc_queue_free(&skip_servers);
return SUCCESS;
}
}
else {
zval_dtor(&dataresult);
/* 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) && skip_servers.len < pool->num_servers && remainingtime > 0);
mmc_queue_free(&skip_servers);
zval_dtor(&dataresult);
return SUCCESS;
}
else
{
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};
size_t lifetime = (size_t)time(NULL) + INI_INT("session.gc_maxlifetime");
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);
if (mmc_prepare_key_ex(ZSTR_VAL(key), ZSTR_LEN(key), datarequest->key, &(datarequest->key_len), MEMCACHE_G(session_key_prefix)) != MMC_OK) {
mmc_pool_release(pool, datarequest);
if (lockrequest != NULL) {
mmc_pool_release(pool, lockrequest);
}
break;
}
/* append .lock to key */
lockrequest = mmc_pool_request(
pool, MMC_PROTO_TCP, mmc_stored_handler, &lockresult,
mmc_pool_failover_handler_null, NULL);
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_STR(&value, val);
/* assemble commands to store data and reset lock */
if (pool->protocol->store(pool, datarequest, MMC_OP_SET, datarequest->key, datarequest->key_len, 0, lifetime, 0, &value) != MMC_OK ||
pool->protocol->store(pool, lockrequest, MMC_OP_SET, lockrequest->key, lockrequest->key_len, 0, MEMCACHE_G(lock_timeout), 0, &lockvalue) != MMC_OK) {
mmc_pool_release(pool, datarequest);
mmc_pool_release(pool, lockrequest);
mmc_queue_push(&skip_servers, mmc);
break;
}
/* find next server in line */
mmc = mmc_pool_find_next(pool, datarequest->key, datarequest->key_len, &skip_servers, &last_index);
mmc_queue_push(&skip_servers, mmc);
if (!mmc_server_valid(mmc) ||
mmc_pool_schedule(pool, mmc, datarequest) != MMC_OK ||
mmc_pool_schedule(pool, mmc, lockrequest) != MMC_OK) {
mmc_pool_release(pool, datarequest);
mmc_pool_release(pool, lockrequest);
lockrequest = NULL;
continue;
}
} while (skip_servers.len < MEMCACHE_G(session_redundancy) && skip_servers.len < pool->num_servers);
mmc_queue_free(&skip_servers);
/* execute requests */
mmc_pool_run(pool);
if (Z_TYPE(lockresult) == IS_TRUE && Z_TYPE(dataresult) == IS_TRUE) {
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) /*
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, 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);
}
/* }}} */
/* {{{ 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);
if (mmc_prepare_key_ex(ZSTR_VAL(key), ZSTR_LEN(key), datarequest->key, &(datarequest->key_len), MEMCACHE_G(session_key_prefix)) != 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);
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);
mmc_queue_push(&skip_servers, mmc);
if (!mmc_server_valid(mmc) ||
mmc_pool_schedule(pool, mmc, datarequest) != MMC_OK ||
mmc_pool_schedule(pool, mmc, lockrequest) != MMC_OK) {
mmc_pool_release(pool, datarequest);
mmc_pool_release(pool, lockrequest);
continue;
}
} while (skip_servers.len < MEMCACHE_G(session_redundancy) && skip_servers.len < pool->num_servers);
mmc_queue_free(&skip_servers);
/* execute requests */
mmc_pool_run(pool);
if (Z_TYPE(lockresult) == IS_TRUE && Z_TYPE(dataresult) == IS_TRUE) {
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-8.2/src/memcache_ascii_protocol.c 0000644 0000765 0000024 00000033015 14423450270 017724 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "memcache_pool.h"
#include "ext/standard/php_smart_string.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 *);
static int mmc_stream_get_line(mmc_stream_t *io, char **line) /*
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);
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(char *line, int line_len) /*
checks for response status and error codes {{{ */
{
int response;
// remove newline and thus prevent passing it to userland
if (line_len >= 2 && line[line_len - 2] == '\r' && line[line_len - 1] == '\n') {
line[line_len - 2] = '\0';
}
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) /*
reads a generic response header and delegates it to response_handler {{{ */
{
char *line;
int line_len = mmc_stream_get_line(request->io, &line);
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);
}
return MMC_REQUEST_MORE;
}
/* }}}*/
static int mmc_request_parse_mutate(mmc_t *mmc, mmc_request_t *request) /*
reads and parses the response header {{{ */
{
char *line;
int line_len;
line_len = mmc_stream_get_line(request->io, &line);
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);
}
if (sscanf(line, "%ld", &lval) < 1) {
return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0);
}
ZVAL_LONG(&value, lval);
return request->value_handler(request->key, request->key_len, &value, 0, 0, request->value_handler_param);
}
return MMC_REQUEST_MORE;
}
/* }}}*/
static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request) /*
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);
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);
}
/* 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) /*
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);
/* 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);
/* 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));
ZEND_SECURE_ZERO(request, 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_string_appendl(&(request->sendbuf.value), "gets", sizeof("gets")-1);
}
else {
smart_string_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_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_appendl(&(request->sendbuf.value), key, key_len);
}
/* }}} */
static void mmc_ascii_end_get(mmc_request_t *request) /* {{{ */
{
smart_string_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) /* {{{ */
{
int status;
mmc_buffer_t buffer;
request->parse = mmc_request_parse_response;
ZEND_SECURE_ZERO(&buffer, sizeof(buffer));
status = mmc_pack_value(pool, &buffer, value, &flags);
if (status != MMC_OK) {
return status;
}
switch (op) {
case MMC_OP_SET:
smart_string_appendl(&(request->sendbuf.value), "set", sizeof("set")-1);
break;
case MMC_OP_ADD:
smart_string_appendl(&(request->sendbuf.value), "add", sizeof("add")-1);
break;
case MMC_OP_REPLACE:
smart_string_appendl(&(request->sendbuf.value), "replace", sizeof("replace")-1);
break;
case MMC_OP_CAS:
smart_string_appendl(&(request->sendbuf.value), "cas", sizeof("cas")-1);
break;
case MMC_OP_APPEND:
smart_string_appendl(&(request->sendbuf.value), "append", sizeof("append")-1);
break;
case MMC_OP_PREPEND:
smart_string_appendl(&(request->sendbuf.value), "prepend", sizeof("prepend")-1);
break;
default:
return MMC_REQUEST_FAILURE;
}
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_appendl(&(request->sendbuf.value), key, key_len);
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_append_unsigned(&(request->sendbuf.value), flags);
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_append_unsigned(&(request->sendbuf.value), exptime);
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_append_unsigned(&(request->sendbuf.value), buffer.value.len);
if (op == MMC_OP_CAS) {
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_append_unsigned(&(request->sendbuf.value), cas);
}
smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
smart_string_appendl(&(request->sendbuf.value), buffer.value.c, buffer.value.len);
smart_string_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_string_appendl(&(request->sendbuf.value), "delete", sizeof("delete")-1);
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_appendl(&(request->sendbuf.value), key, key_len);
if (exptime > 0) {
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_append_unsigned(&(request->sendbuf.value), exptime);
}
smart_string_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_string_appendl(&(request->sendbuf.value), "incr", sizeof("incr")-1);
}
else {
smart_string_appendl(&(request->sendbuf.value), "decr", sizeof("decr")-1);
}
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_appendl(&(request->sendbuf.value), key, key_len);
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_append_unsigned(&(request->sendbuf.value), value >= 0 ? value : -value);
smart_string_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_string_appendl(&(request->sendbuf.value), "flush_all", sizeof("flush_all")-1);
if (exptime > 0) {
smart_string_appendl(&(request->sendbuf.value), " ", 1);
smart_string_append_unsigned(&(request->sendbuf.value), exptime);
}
smart_string_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_string_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_string_appendl(&(request->sendbuf.value), cmd, cmd_len);
efree(cmd);
}
/* }}} */
static void mmc_set_sasl_auth_data(mmc_pool_t *pool, mmc_request_t *request, const char *user, const char *password) /* {{{ */
{
/* stats not supported */
}
/* }}} */
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,
mmc_set_sasl_auth_data
};
/*
* 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-8.2/src/memcache_binary_protocol.c 0000644 0000765 0000024 00000061437 14423450270 020131 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define MMC_DEBUG 0
#ifdef PHP_WIN32
#include
#include
#else
#include
#include
#include
#endif
#include "memcache_pool.h"
#include "ext/standard/php_smart_string.h"
#ifdef htonll
#undef htonll
#endif
#ifdef ntohll
#undef ntohll
#endif
#ifndef PHP_WIN32
#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
#else
uint64_t mmc_htonll(uint64_t value);
# define ntohll mmc_htonll
# define htonll mmc_htonll
#endif
#ifdef __GNUC__
# define MMC_ATTR_PACKED __attribute__((packed))
#else
# define MMC_ATTR_PACKED
#endif /* UD_ATTR_PACKED */
#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
#define MMC_BIN_OP_APPEND 0x0e
#define MMC_BIN_OP_PREPEND 0x0f
#define MMC_BINARY_STATUS_OK 0x00
#define MMC_BINARY_STATUS_KEY_NOT_FOUND 0x01
#define MMC_BINARY_STATUS_KEY_EXISTS 0x02
#define MMC_BINARY_STATUS_VALUE_TOO_LARGE 0x03
#define MMC_BINARY_STATUS_INVALID_ARGS 0x04
#define MMC_BINARY_STATUS_ITEM_NOT_STORED 0x05
#define MMC_BINARY_STATUS_INCR_DECR_ERROR 0x06 /* Incr/Decr on non-numeric value */
#define MMC_BINARY_STATUS_UNKNOWN_COMMAND 0x81
#define MMC_BINARY_STATUS_OUT_OF_MEMORY 0x82
#define MMC_OP_SASL_LIST 0x20
#define MMC_OP_SASL_AUTH 0x21
#define MMC_OP_SASL_AUTH_STEP 0x21
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;
uint16_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 total_body_length (not including this header) */
uint32_t reqid; /* opaque request id */
uint64_t cas;
} MMC_ATTR_PACKED mmc_request_header_t;
typedef struct mmc_get_request_header {
mmc_request_header_t base;
} MMC_ATTR_PACKED mmc_get_request_header_t;
typedef struct mmc_version_request_header {
mmc_request_header_t base;
} MMC_ATTR_PACKED mmc_version_request_header_t;
typedef struct mmc_store_request_header {
mmc_request_header_t base;
uint32_t flags;
uint32_t exptime;
} MMC_ATTR_PACKED mmc_store_request_header_t;
typedef struct mmc_store_append_header {
mmc_request_header_t base;
} MMC_ATTR_PACKED mmc_store_append_header_t;
typedef struct mmc_delete_request_header {
mmc_request_header_t base;
} MMC_ATTR_PACKED mmc_delete_request_header_t;
typedef struct mmc_mutate_request_header {
mmc_request_header_t base;
uint64_t delta;
uint64_t initial;
uint32_t expiration;
} MMC_ATTR_PACKED mmc_mutate_request_header_t;
typedef struct mmc_sasl_request_header {
mmc_request_header_t base;
} MMC_ATTR_PACKED mmc_sasl_request_header;
typedef struct mmc_response_header {
uint8_t magic;
uint8_t opcode;
uint16_t key_len;
uint8_t extras_len;
uint8_t datatype;
uint16_t status;
uint32_t total_body_length; /* trailing body total_body_length (not including this header) */
uint32_t reqid; /* echo'ed from request */
uint64_t cas;
} MMC_ATTR_PACKED mmc_response_header_t;
typedef struct mmc_get_response_header {
uint32_t flags;
} MMC_ATTR_PACKED mmc_get_response_header_t;
typedef struct mmc_mutate_response_header {
uint64_t value;
} MMC_ATTR_PACKED mmc_mutate_response_header_t;
static int mmc_request_read_response(mmc_t *, mmc_request_t *);
static int mmc_request_parse_value(mmc_t *, mmc_request_t *);
static int mmc_request_read_value(mmc_t *, mmc_request_t *);
void mmc_binary_hexdump(void *mem, unsigned int len)
{
# define HEXDUMP_COLS 4
unsigned int i, j;
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) {
if(i % HEXDUMP_COLS == 0) {
printf("%06i: ", i);
}
if(i < len) {
printf("%02x ", 0xFF & ((char*)mem)[i]);
} else {
printf(" ");
}
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
if(j >= len) {
putchar(' ');
} else if(isprint(((char*)mem)[j])) {
putchar(0xFF & ((char*)mem)[j]);
} else {
putchar('.');
}
}
putchar('\n');
}
}
}
#ifdef PHP_WIN32
uint64_t mmc_htonll(uint64_t value)
{
if (value == 0) {
return 0x0LL;
} else {
static const int num = 42;
// Check the endianness
if (*(const char *)(&num) == num) {
const uint32_t high_part = htonl((uint32_t)(value >> 32));
const uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL));
return ((uint64_t)(low_part) << 32) | high_part;
} else {
return value;
}
}
}
#endif
static inline char *mmc_stream_get(mmc_stream_t *io, size_t bytes) /*
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);
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) /*
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(mmc_response_header_t));
if (header != NULL) {
if (header->magic != MMC_RESPONSE_MAGIC) {
return mmc_server_failure(mmc, request->io, "Malformed server response (invalid magic byte)", 0);
}
if (header->opcode == MMC_OP_NOOP) {
return MMC_REQUEST_DONE;
}
req->command.opcode = header->opcode;
switch (ntohs(header->status)) {
case MMC_BINARY_STATUS_OK:
req->command.error = MMC_OK;
break;
case MMC_BINARY_STATUS_KEY_NOT_FOUND:
req->command.error = MMC_RESPONSE_NOT_FOUND;
break;
case MMC_BINARY_STATUS_KEY_EXISTS:
req->command.error = MMC_RESPONSE_EXISTS;
break;
case MMC_BINARY_STATUS_VALUE_TOO_LARGE:
req->command.error = MMC_RESPONSE_TOO_LARGE;
break;
case MMC_BINARY_STATUS_INVALID_ARGS:
case MMC_BINARY_STATUS_INCR_DECR_ERROR:
req->command.error = MMC_RESPONSE_CLIENT_ERROR;
break;
case MMC_BINARY_STATUS_ITEM_NOT_STORED:
req->command.error = MMC_RESPONSE_NOT_STORED;
break;
case MMC_BINARY_STATUS_UNKNOWN_COMMAND:
req->command.error = MMC_RESPONSE_UNKNOWN_CMD;
break;
case MMC_BINARY_STATUS_OUT_OF_MEMORY:
req->command.error = MMC_RESPONSE_OUT_OF_MEMORY;
break;
default:
req->command.error = MMC_RESPONSE_UNKNOWN;
break;
}
req->command.reqid = ntohl(header->reqid);
req->value.length = ntohl(header->total_body_length);
req->value.cas = ntohll(header->cas);
if (req->value.length == 0) {
return request->response_handler(mmc, request, req->command.error, "", 0, request->response_handler_param);
}
/* 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) /*
always returns MMC_REQUEST_DONE {{{ */
{
return MMC_REQUEST_DONE;
}
static int mmc_request_read_response(mmc_t *mmc, mmc_request_t *request) /*
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);
/* 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);
}
return MMC_REQUEST_MORE;
}
/* }}}*/
static int mmc_request_read_mutate(mmc_t *mmc, mmc_request_t *request) /*
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));
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);
ZVAL_LONG(&value, ntohll(header->value));
if (Z_TYPE_P(key) != IS_STRING) {
zval keytmp = *key;
zval_copy_ctor(&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);
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);
}
return result;
}
return MMC_REQUEST_MORE;
}
/* }}}*/
static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request) /*
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(mmc_get_response_header_t));
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) /*
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);
/* 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);
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);
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);
}
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);
header->cas = 0;
}
/* }}} */
static mmc_request_t *mmc_binary_create_request() /* {{{ */
{
mmc_binary_request_t *request = emalloc(sizeof(mmc_binary_request_t));
ZEND_SECURE_ZERO(request, sizeof(mmc_binary_request_t));
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_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(mmc_request_header_t));
smart_string_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_string_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_get_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.base), MMC_OP_GET, req->keys.len, key_len, 0, 0);
header.base.cas = 0x0;
smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(mmc_get_request_header_t));
smart_string_appendl(&(request->sendbuf.value), key, key_len);
#if MMC_DEBUG
mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
#endif
/* 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) /* {{{ */
{
mmc_binary_request_t *req = (mmc_binary_request_t *)request;
int status, prevlen, valuelen;
request->parse = mmc_request_parse_response;
req->next_parse_handler = mmc_request_read_response;
prevlen = request->sendbuf.value.len;
/* placeholder for upcoming append/prepend support */
#if 1
if (op == MMC_OP_APPEND || op == MMC_OP_PREPEND) {
mmc_store_append_header_t *header;
if (op == MMC_OP_APPEND) {
op = MMC_BIN_OP_APPEND;
} else {
op = MMC_BIN_OP_PREPEND;
}
/* allocate space for header */
mmc_buffer_alloc(&(request->sendbuf), sizeof(mmc_store_append_header_t));
request->sendbuf.value.len += sizeof(mmc_store_append_header_t);
/* append key and data */
smart_string_appendl(&(request->sendbuf.value), key, key_len);
valuelen = request->sendbuf.value.len;
status = mmc_pack_value(pool, &(request->sendbuf), value, &flags);
if (status != MMC_OK) {
return status;
}
header = (mmc_store_append_header_t *)(request->sendbuf.value.c + prevlen);
mmc_pack_header(&(header->base), op, 0, key_len, sizeof(mmc_store_append_header_t) - sizeof(mmc_request_header_t), request->sendbuf.value.len - valuelen);
header->base.cas = htonll(cas);
#if MMC_DEBUG
mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
#endif
/* todo */
return MMC_OK;
} else
#endif
{
mmc_store_request_header_t *header;
/* allocate space for header */
mmc_buffer_alloc(&(request->sendbuf), sizeof(mmc_store_request_header_t));
request->sendbuf.value.len += sizeof(mmc_store_request_header_t);
/* append key and data */
smart_string_appendl(&(request->sendbuf.value), key, key_len);
valuelen = request->sendbuf.value.len;
status = mmc_pack_value(pool, &(request->sendbuf), value, &flags);
if (status != MMC_OK) {
return status;
}
/* initialize header */
header = (mmc_store_request_header_t *)(request->sendbuf.value.c + prevlen);
if (op == MMC_OP_CAS) {
op = MMC_OP_SET;
}
mmc_pack_header(&(header->base), op, 0, key_len, sizeof(mmc_store_request_header_t) - sizeof(mmc_request_header_t), request->sendbuf.value.len - valuelen);
header->base.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, 0, 0);
smart_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
smart_string_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;
uint8_t op;
request->parse = mmc_request_parse_response;
req->next_parse_handler = mmc_request_read_mutate;
if (value >= 0) {
op = MMC_OP_INCR;
header.delta = htonll((uint64_t)value);
//header.delta = (uint64_t)value;
} else {
op = MMC_OP_DECR;
header.delta = htonll((uint64_t)-value);
}
/* extra is always 20 bytes
https://code.google.com/p/memcached/wiki/BinaryProtocolRevamped#Increment,_Decrement
TODO: add flags to do not force alignments so we can rely on sizeof instead of
fixed sizes, safer&cleaner */
mmc_pack_header(&(header.base), op, req->keys.len, key_len, 20, 0);
header.base.cas = 0x0;
header.initial = htonll((int64_t)defval);
if (defval_used) {
/* server inserts defval if key doesn't exist */
header.expiration = htonl(exptime);
}
else {
/* server replies with NOT_FOUND if exptime ~0 and key doesn't exist */
header.expiration = ~(uint32_t)0;
}
/* mutate request is 43 bytes */
smart_string_appendl(&(request->sendbuf.value), (const char *)&header, 44);
smart_string_appendl(&(request->sendbuf.value), key, key_len);
#if MMC_DEBUG
mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
#endif
/* 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_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
}
/* }}} */
static void mmc_binary_version(mmc_request_t *request) /* {{{ */
{
mmc_version_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_VERSION, 0, 0, 0, 0);
header.base.cas = 0x0;
smart_string_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_string_appendl(&(request->sendbuf.value), (const char *)&header, sizeof(header));
}
/* }}} */
static void mmc_set_sasl_auth_data(mmc_pool_t *pool, mmc_request_t *request, const char *user, const char *password) /* {{{ */
{
const unsigned int key_len = 5;
int prevlen;
mmc_sasl_request_header *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;
memcpy(request->key, "PLAIN", 5 + 1);
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_string_appendl(&(request->sendbuf.value), "PLAIN", 5);
/* initialize header */
header = (mmc_sasl_request_header *)(request->sendbuf.value.c + prevlen);
(header->base).magic = MMC_REQUEST_MAGIC;
(header->base).opcode = MMC_OP_SASL_AUTH;
(header->base).key_len = htons(key_len);
(header->base).extras_len = 0x0;
(header->base).datatype = 0x0;
(header->base)._reserved = 0x0;
(header->base).length = htonl(strlen(user) + strlen(password) + key_len + 2);
(header->base).reqid = htonl(0);
header->base.cas = 0x0;
smart_string_appendl(&(request->sendbuf.value), "\0", 1);
smart_string_appendl(&(request->sendbuf.value), user, strlen(user));
smart_string_appendl(&(request->sendbuf.value), "\0", 1);
smart_string_appendl(&(request->sendbuf.value), password, strlen(password));
#if MMC_DEBUG
mmc_binary_hexdump(request->sendbuf.value.c, request->sendbuf.value.len);
#endif
return;
}
/* }}} */
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,
mmc_set_sasl_auth_data
};
/*
* 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-8.2/src/memcache_standard_hash.c 0000644 0000765 0000024 00000005736 14423450270 017527 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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));
ZEND_SECURE_ZERO(state, 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) /* {{{ */
{
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-8.2/src/memcache_consistent_hash.c 0000644 0000765 0000024 00000012212 14423450270 020103 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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));
ZEND_SECURE_ZERO(state, 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) /* {{{ */
{
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-8.2/src/php_memcache.h 0000644 0000765 0000024 00000007432 14423450270 015513 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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_RINIT_FUNCTION(memcache);
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);
PHP_FUNCTION(memcache_set_sasl_auth_data);
#define PHP_MEMCACHE_VERSION "8.2"
#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 */
#define MEMCACHE_IS_CALLABLE(cb_zv, flags, cb_sp) zend_is_callable((cb_zv), (flags), (cb_sp))
#define MEMCACHE_LIST_INSERT(list, val) zend_list_insert((list), (val))
/* internal functions */
mmc_t *mmc_find_persistent(const char *, int, unsigned short, unsigned short, double, int);
int mmc_value_handler_single(const char *, unsigned int, zval *, unsigned int, unsigned long, void *);
int mmc_value_handler_multi(const char *, unsigned int, zval *, unsigned int, unsigned long, void *);
int mmc_stored_handler(mmc_t *, mmc_request_t *, int, const char *, unsigned int, void *);
int mmc_numeric_response_handler(mmc_t *, mmc_request_t *, int, const char *, unsigned int, void *);
/* 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-8.2/src/memcache_pool.h 0000644 0000765 0000024 00000043513 14423450270 015675 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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_string_public.h"
#include "memcache_queue.h"
/*
* 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_string value;
unsigned int idx; /* current index */
} mmc_buffer_t;
#define mmc_buffer_release(b) ZEND_SECURE_ZERO((b), sizeof(*(b)))
#define mmc_buffer_reset(b) (b)->value.len = (b)->idx = 0
#ifdef PHP_WIN32
#define MMC_POOL_INLINE
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
/* see https://gcc.gnu.org/gcc-5/porting_to.html */
#define MMC_POOL_INLINE extern inline
#else
#define MMC_POOL_INLINE inline
#endif
void mmc_buffer_alloc(mmc_buffer_t *, unsigned int);
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);
typedef char *(*mmc_stream_readline)(mmc_stream_t *stream, char *buf, size_t maxlen, size_t *retlen);
/* 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);
typedef int (*mmc_request_parser)(mmc_t *mmc, mmc_request_t *request);
typedef int (*mmc_request_value_handler)(
const char *key, unsigned int key_len, zval *value,
unsigned int flags, unsigned long cas, void *param);
typedef int (*mmc_request_response_handler)(
mmc_t *mmc, mmc_request_t *request, int response, const char *message, unsigned int message_len, void *param);
typedef int (*mmc_request_failover_handler)(
mmc_pool_t *pool, mmc_t *mmc, mmc_request_t *request, void *param);
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);
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 void (*mmc_protocol_set_sasl_auth_data)(mmc_pool_t *pool, mmc_request_t *request, const char *user, const char *password);
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_set_sasl_auth_data set_sasl_auth_data;
} 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);
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, zval *param);
/* 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 */
zval failure_callback_param;
};
/* server functions */
mmc_t *mmc_server_new(const char *, int, unsigned short, unsigned short, int, double, int);
void mmc_server_free(mmc_t *);
void mmc_server_disconnect(mmc_t *mmc, mmc_stream_t *io);
int mmc_server_valid(mmc_t *);
int mmc_server_failure(mmc_t *, mmc_stream_t *, const char *, int);
int mmc_request_failure(mmc_t *, mmc_stream_t *, const char *, unsigned int, int);
/* pool functions */
mmc_pool_t *mmc_pool_new();
void mmc_pool_free(mmc_pool_t *);
void mmc_pool_add(mmc_pool_t *, mmc_t *, unsigned int);
void mmc_pool_close(mmc_pool_t *);
int mmc_pool_open(mmc_pool_t *, mmc_t *, mmc_stream_t *, int);
void mmc_pool_select(mmc_pool_t *);
void mmc_pool_run(mmc_pool_t *);
mmc_t *mmc_pool_find_next(mmc_pool_t *, const char *, unsigned int, mmc_queue_t *, unsigned int *);
mmc_t *mmc_pool_find(mmc_pool_t *, const char *, unsigned int);
int mmc_pool_schedule(mmc_pool_t *, mmc_t *, mmc_request_t *);
int mmc_pool_failover_handler(mmc_pool_t *, mmc_t *, mmc_request_t *, void *);
int mmc_pool_failover_handler_null(mmc_pool_t *, mmc_t *, mmc_request_t *, void *);
mmc_request_t *mmc_pool_request(mmc_pool_t *, int,
mmc_request_response_handler, void *, mmc_request_failover_handler, void *);
mmc_request_t *mmc_pool_request_get(mmc_pool_t *, int,
mmc_request_value_handler, void *, mmc_request_failover_handler, void *);
#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 *);
int mmc_pool_schedule_key(mmc_pool_t *, const char *, unsigned int, mmc_request_t *, unsigned int);
int mmc_pool_schedule_get(mmc_pool_t *, int, int, zval *,
mmc_request_value_handler, void *, mmc_request_failover_handler, void *, mmc_request_t *);
/* utility functions */
int mmc_pack_value(mmc_pool_t *, mmc_buffer_t *, zval *, unsigned int *);
int mmc_unpack_value(mmc_t *, mmc_request_t *, mmc_buffer_t *, const char *, unsigned int, unsigned int, unsigned long, unsigned int);
double timeval_to_double(struct timeval tv);
struct timeval double_to_timeval(double sec);
int mmc_prepare_key_ex(const char *, unsigned int, char *, unsigned int *, char *);
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;
char *session_key_prefix;
zend_bool session_prefix_host_key;
zend_bool session_prefix_host_key_remove_www;
zend_bool session_prefix_host_key_remove_subdomain;
char *session_prefix_static_key;
char *session_save_path;
char *key_prefix;
zend_bool prefix_host_key;
zend_bool prefix_host_key_remove_www;
zend_bool prefix_host_key_remove_subdomain;
char *prefix_static_key;
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-8.2/src/memcache_queue.h 0000644 0000765 0000024 00000004676 14423450270 016057 0 ustar tomas staff /*
+----------------------------------------------------------------------+
| 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$ */
#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) ZEND_SECURE_ZERO((q), 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
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
/* see https://gcc.gnu.org/gcc-5/porting_to.html */
#define MMC_QUEUE_INLINE extern inline
#else
#define MMC_QUEUE_INLINE inline
#endif
void mmc_queue_push(mmc_queue_t *, void *);
void *mmc_queue_pop(mmc_queue_t *);
int mmc_queue_contains(mmc_queue_t *, void *);
void mmc_queue_free(mmc_queue_t *);
void mmc_queue_copy(mmc_queue_t *, mmc_queue_t *);
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-8.2/config.m4 0000644 0000765 0000024 00000000103 14423450270 013635 0 ustar tomas staff dnl $Id$
dnl this file is required by phpize
sinclude(config9.m4)
memcache-8.2/config9.m4 0000644 0000765 0000024 00000010701 14423450270 013733 0 ustar tomas staff dnl
dnl $Id$
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)
PHP_SUBST(MEMCACHE_SHARED_LIBADD)
fi
AC_MSG_CHECKING(PHP version)
if test -d $abs_srcdir/src ; then
dnl # only when for PECL, not for PHP
export OLD_CPPFLAGS="$CPPFLAGS"
export CPPFLAGS="$CPPFLAGS $INCLUDES"
AC_TRY_COMPILE([#include ], [
#if PHP_MAJOR_VERSION < 7
#error "PHP < 7"
#endif
], [
subdir=src
AC_MSG_RESULT([PHP 7.x])
],
AC_MSG_ERROR([PHP 7.x required for pecl-php-memcache ver 4+. Use pecl-php-memcache ver 3.x for PHP 5.x.])
)
export CPPFLAGS="$OLD_CPPFLAGS"
else
AC_MSG_ERROR([unknown])
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
SOURCES_EX="memcache.c memcache_pool.c memcache_queue.c memcache_ascii_protocol.c memcache_binary_protocol.c memcache_standard_hash.c memcache_consistent_hash.c"
SESSION_SOURCES_EX="memcache_session.c"
SOURCES=`echo "$subdir/$SOURCES_EX" |sed "s:[ ]: $subdir/:g"`
SESSION_SOURCES=`echo "$subdir/$SESSION_SOURCES_EX" |sed "s:[ ]: $subdir/:g"`
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, $SOURCES $SESSION_SOURCES, $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, $SOURCES, $ext_shared)
fi
if test ! -d $subdir; then
mkdir $subdir
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-8.2/config.w32 0000644 0000765 0000024 00000002071 14423450270 013736 0 ustar tomas staff // $Id$
// vim:ft=javascript
ARG_ENABLE("memcache", "memcache support", "no");
if (PHP_MEMCACHE != "no") {
if (!PHP_ZLIB_SHARED || CHECK_LIB("zlib_a.lib", "memcache", PHP_MEMCACHE)) {
var dll = get_define('PHPDLL');
var old_conf_dir = configure_module_dirname;
if (dll.match(/^php[78]/) != null) {
configure_module_dirname = configure_module_dirname + "\\src";
} else if (dll.match(/^php5/) != null) {
ERROR("PHP 7.x required for pecl-php-memcache ver 4+. Use pecl-php-meachce ver 3.x for PHP 5.x.");
} else {
ERROR("Cannot determine PHP version from:'" + dll + "'");
}
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");
configure_module_dirname = old_conf_dir;
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-8.2/CREDITS 0000644 0000765 0000024 00000000040 14423450270 013146 0 ustar tomas staff Antony Dovgal, Mikael Johansson
memcache-8.2/LICENSE 0000644 0000765 0000024 00000006210 14423450270 013140 0 ustar tomas staff --------------------------------------------------------------------
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-8.2/example.php 0000644 0000765 0000024 00000000775 14423450270 014311 0 ustar tomas staff set("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-8.2/README 0000644 0000765 0000024 00000013564 14423450270 013025 0 ustar tomas staff This is an official repository for pecl-memcache plugin since 2019.
This repository contains modified pecl-memcache plugin ported to PHP8,
which was originally developed for the need of hosting company in Slovakia (Websupport.sk).
The latest release is 8.2 (released: 2023-04-30) with support for PHP 8.0+ (incl. PHP 8.2).
Please use version 4.0.5.1 (released: 2020-12-19) for PHP 7.x from branch NON_BLOCKING_IO_php7.
See: https://github.com/websupport-sk/pecl-memcache/releases
See also release on PECL: https://pecl.php.net/package/memcache
Feel free to use it and post patches.
Original README before 2019:
memcached 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-8.2/memcache.php 0000644 0000765 0000024 00000070610 14423450270 014413 0 ustar tomas staff |
+----------------------------------------------------------------------+
*/
$VERSION='$Id$';
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!