#!/usr/bin/php -dphar.readonly=1 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_installer.php'; const LEN = 6697; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> '������������� ��a:8:{s:6:"header";s:49:"pharext v3.0.1 (c) Michael Wallner <mike@php.net>";s:7:"version";s:5:"3.0.1";s:4:"name";s:9:"memcached";s:4:"date";s:10:"2015-05-01";s:4:"stub";s:21:"pharext_installer.php";s:7:"license";s:3218:"-------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from <http://www.php.net/software/>". 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 <http://www.php.net>. PHP includes the Zend Engine, freely available at <http://www.zend.com>. ";s:7:"release";s:5:"2.2.0";s:4:"type";s:9:"extension";}���pharext/Cli/Args.phpK��SQCUK��O���������pharext/Cli/Command.php��SQCU�� *۶���������pharext/Command.php��SQCU��td\���������pharext/Exception.phpc��SQCUc��U{���������pharext/ExecCmd.php��SQCU��lʶ���������pharext/Installer.php��SQCU��WJ���������pharext/Openssl/PrivateKey.php��SQCU��&P���������pharext/Packager.phpt!��SQCUt!��;c���������pharext/SourceDir/Basic.php��SQCU��zZ���������pharext/SourceDir/Git.php��SQCU��bc���������pharext/SourceDir/Pecl.php��SQCU�����������pharext/SourceDir.php]��SQCU]��S ���������pharext/Task/Activate.phpp ��SQCUp ��EE���������pharext/Task/Askpass.phpU��SQCUU��*������ ���pharext/Task/BundleGenerator.php}��SQCU}�� `Y���������pharext/Task/Cleanup.php���SQCU���ZͶ���������pharext/Task/Configure.phpT��SQCUT��}���������pharext/Task/Extract.php��SQCU��ն���������pharext/Task/GitClone.php��SQCU��CR(���������pharext/Task/Make.php��SQCU��6 ���������pharext/Task/PaxFixup.php��SQCU��y���������pharext/Task/PeclFixup.php��SQCU��et���������pharext/Task/PharBuild.php��SQCU��D垶���������pharext/Task/PharCompress.phpr��SQCUr�� ���������pharext/Task/PharRename.php��SQCU��[˶���������pharext/Task/PharSign.php��SQCU��ۺi���������pharext/Task/Phpize.php��SQCU�� 2Ѷ���������pharext/Task/StreamFetch.php��SQCU��s\���������pharext/Task.phpw���SQCUw��� IǶ���������pharext/Tempdir.php��SQCU��,���������pharext/Tempfile.php��SQCU��#���������pharext/Tempname.php`��SQCU`��<Np���������pharext/Version.php4���SQCU4���p���������pharext_installer.php���SQCU���pDZ���������pharext_packager.php���SQCU���1���������pharext_package.php2���SQCU2���vSTҶ������ ���package.xmlP��SQCUP��2~���������tests/001.phpt���SQCU���a,P{���������tests/version.phpt(��SQCU(��%-Zƶ���������tests/bug_16084.phpt��SQCU��S))���������tests/bug_16959.phpt��SQCU��Ezc���������tests/bug_17137.phpt��SQCU�����������tests/bug_18639.phpt��SQCU��MҀ���������tests/callback_exception.phptZ��SQCUZ��$F:���������tests/callback_exception_2.phpt��SQCU��Ֆ���������tests/cas.phpt|��SQCU|��Xw!���������tests/cas_multi.phpt��SQCU��¶���������tests/check_if_persistent.phpt"��SQCU"��A���������tests/check_if_pristine.phpt|��SQCU|��yUx���������tests/clone.phpt)��SQCU)��ZV���������tests/compression_types.phpt ��SQCU ��!���������tests/conf_persist.phptl��SQCUl��FS ���������tests/construct.phpt���SQCU���h���������tests/construct_persistent.phpt��SQCU��^&=���������tests/deleted.phpt��SQCU��"CC���������tests/deletemulti.phpt}��SQCU}��k 晶���������tests/deletemultitypes.phpt��SQCU�� r���������tests/expire.phptZ��SQCUZ��<���������tests/flush_buffers.phpt>��SQCU>��d���������tests/getdelayed.phpt��SQCU��aB!���������tests/getserverlist.phptx��SQCUx��l���������tests/gh_21.phpt��SQCU�����������tests/gh_77.phpt9��SQCU9��m ���������tests/gh_90.phptK��SQCUK��LmԶ���������tests/invoke_callback.phpt��SQCU��Mx ���������tests/invoke_callback_2.phpt��SQCU��&BA������ ���tests/invoke_callback_twice.phpt��SQCU��0$���������tests/localserver.phpt_��SQCU_��ŒL����������tests/multi_order.phpt��SQCU�����������tests/no-not-found.phpt ��SQCU ��u'hֶ���������tests/options.phptP��SQCUP��A3k���������tests/pr_75.phpt��SQCU��d6#���������tests/rescode.phpt#��SQCU#��9_Ē������&���tests/session_badconf_emptyprefix.phpt��SQCU��`!������#���tests/session_badconf_locktime.phpt&��SQCU&�� %������!���tests/session_badconf_prefix.phpt��SQCU��������"���tests/session_badconf_servers.phpt��SQCU��6���������tests/session_basic.phpt��SQCU��g޶���������tests/set_large.phptW��SQCUW��Eq���������tests/setoptions.phptT��SQCUT��FOr���������tests/touch_binary.phpt��SQCU��%Ƕ���������tests/types.inc ��SQCU ��7ζ���������tests/types_igbinary.phpt��SQCU��("S���������tests/types_igbinary_multi.phpt��SQCU��"ĕ���������tests/types_json.phpt��SQCU��ˊ,���������tests/types_json_multi.phpt��SQCU��od=���������tests/types_msgpack.phpt��SQCU��et���������tests/types_msgpack_multi.phpt��SQCU�����������tests/types_php.phpty��SQCUy��Ĺ���������tests/types_php_multi.phpt��SQCU��V`���������tests/undefined_set.phpt��SQCU��1���������tests/vbucket.phpt��SQCU��;���������tests/user-flags.phpt=��SQCU=��H���������tests/gh_93.phpt|��SQCU|��j���������tests/add.phptr��SQCUr��~˶���������tests/bad_construct.phpte��SQCUe��/{|���������tests/append.phpt��SQCU��#>���������tests/prepend.phpt��SQCU��ⱶ���������tests/replace.phpt��SQCU��$)\���������tests/getmulti.phpt~��SQCU~��:���������tests/setmulti.phpt��SQCU��9,/P���������tests/cachecallback.phpt��SQCU��}n���������tests/incrdecr.phpt��SQCU��.���������tests/incrdecr_initial.phpt��SQCU�����������tests/incrdecr_invalid_key.phptH��SQCUH��N���������tests/incrdecr_bykey.phpty��SQCUy��{���������tests/invalid_options.phpt��SQCU��=���������tests/keys.phptB��SQCUB��˗���������tests/testdata.res ��SQCU ��b���������tests/config.inc!��SQCU!��$u_���������tests/sasl_basic.phpt��SQCU��d5���������tests/getserverbykey.phpt��SQCU��P}���������README.markdown��SQCU��m���������CREDITS;���SQCU;���w-���������LICENSE ��SQCU ��Q������ ���ChangeLogX��SQCUX�����������memcached-api.php��SQCU��Wk������ ���memcached.ini��SQCU�����������fastlz/LICENSE��SQCU���_������ ���config.m49��SQCU9��mȃ������ ���config.w32��SQCU��*���������php_memcached.cq�SQCUq�G4���������php_memcached.h��SQCU��icƶ���������php_memcached_private.h��SQCU��N���������php_memcached_session.c0��SQCU0��.Q̶���������php_memcached_session.h��SQCU��ְն���������php_libmemcached_compat.h ��SQCU ��ɒ���������php_libmemcached_compat.cq��SQCUq��/`���������php_memcached_server.h��SQCU��&q���������php_memcached_server.cu_��SQCUu_��T���������g_fmt.c��SQCU��(|p���������g_fmt.h��SQCU�����������fastlz/fastlz.cP5��SQCUP5��+ZǶ���������fastlz/fastlz.h ��SQCU ��|l������<?php namespace pharext\Cli; /** * Command line arguments */ class Args implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array|Traversable $spec */ public function __construct($spec = null) { if (is_array($spec) || $spec instanceof Traversable) { $this->compile($spec); } } /** * Compile the original spec * @param array|Traversable $spec * @return pharext\CliArgs self */ public function compile($spec) { foreach ($spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; $this->orig[] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!strlen($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext\Cli; use pharext\Cli\Args as CliArgs; use Phar; if (!function_exists("array_column")) { function array_column(array $array, $col, $idx = null) { $result = []; foreach ($array as $el) { if (isset($idx)) { $result[$el[$idx]] = $el[$col]; } else { $result[] = $el[$col]; } } return $result; } } trait Command { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Retrieve metadata of the currently running phar * @param string $key * @return mixed */ public function metadata($key = null) { $running = new Phar(Phar::running(false)); if ($key === "signature") { $sig = $running->getSignature(); return sprintf("%s signature of %s\n%s", $sig["hash_type"], $this->metadata("name"), chunk_split($sig["hash"], 64, "\n")); } $metadata = $running->getMetadata(); if (isset($key)) { return $metadata[$key]; } return $metadata; } /** * Output pharext vX.Y.Z header */ public function header() { if (!headers_sent()) { /* only display header, if we didn't generate any output yet */ printf("%s\n\n", $this->metadata("header")); } } /** * @inheritdoc * @see \pharext\Command::debug() */ public function debug($fmt) { if ($this->args->verbose) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::warn() */ public function warn($fmt) { if (!$this->args->quiet) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "Warning: $fmt", $arg); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($fmt) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "ERROR: $fmt", $arg); } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Verbosity * @return boolean */ public function verbosity() { if ($this->args->verbose) { return true; } elseif ($this->args->quiet) { return false; } else { return null; } } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Argument error */ const EARGS = 1; /** * Build error */ const EBUILD = 2; /** * Signature error */ const ESIGN = 3; /** * Extract/unpack error */ const EEXTRACT = 4; /** * Install error */ const EINSTALL = 5; /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print debug message * @param string $fmt * @param string ...$args */ public function debug($fmt); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print warning * @param string $fmt * @param string ...$args */ public function warn($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; class Exception extends \Exception { public function __construct($message = null, $code = 0, $previous = null) { if (!isset($message)) { $last_error = error_get_last(); $message = $last_error["message"]; if (!$code) { $code = $last_error["type"]; } } parent::__construct($message, $code, $previous); } } <?php namespace pharext; /** * Execute system command */ class ExecCmd { /** * Sudo command, if the cmd needs escalated privileges * @var string */ private $sudo; /** * Executable of the cmd * @var string */ private $command; /** * Passthrough cmd output * @var bool */ private $verbose; /** * Output of cmd run * @var string */ private $output; /** * Return code of cmd run * @var int */ private $status; /** * @param string $command * @param bool verbose */ public function __construct($command, $verbose = false) { $this->command = $command; $this->verbose = $verbose; } /** * (Re-)set sudo command * @param string $sudo */ public function setSu($sudo = false) { $this->sudo = $sudo; } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param bool $verbose * @return int exit status */ private function suExec($command, $verbose = null) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { $this->status = -1; throw new Exception("Failed to run {$command}"); } $stdout = $pipes[1]; $passwd = 0; $checks = 10; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd < $checks) { $passwd++; if (stristr($data, "password")) { $passwd = $checks + 1; printf("\n%s", $data); continue; } } elseif ($passwd > $checks) { /* new line after pw entry */ printf("\n"); $passwd = $checks; } if ($verbose === null) { print $this->progress($data, 0); } else { if ($verbose) { printf("%s", $data); } $this->output .= $data; } } if ($verbose === null) { $this->progress("", PHP_OUTPUT_HANDLER_FINAL); } return $this->status = proc_close($proc); } /** * Output handler that displays some progress while soaking output * @param string $string * @param int $flags * @return string */ private function progress($string, $flags) { static $counter = 0; static $symbols = ["\\","|","/","-"]; $this->output .= $string; if (false !== strpos($string, "\n")) { ++$counter; } return $flags & PHP_OUTPUT_HANDLER_FINAL ? " \r" : sprintf(" %s\r", $symbols[$counter % 4]); } /** * Run the command * @param array $args * @return \pharext\ExecCmd self * @throws \pharext\Exception */ public function run(array $args = null) { $exec = escapeshellcmd($this->command); if ($args) { $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); } if ($this->sudo) { $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->verbose); } elseif ($this->verbose) { ob_start(function($s) { $this->output .= $s; return $s; }, 1); passthru($exec, $this->status); ob_end_flush(); } elseif ($this->verbose !== false /* !quiet */) { ob_start([$this, "progress"], 1); passthru($exec . " 2>&1", $this->status); ob_end_flush(); } else { exec($exec ." 2>&1", $output, $this->status); $this->output = implode("\n", $output); } if ($this->status) { throw new Exception("Command {$exec} failed ({$this->status})"); } return $this; } /** * Retrieve exit code of cmd run * @return int */ public function getStatus() { return $this->status; } /** * Retrieve output of cmd run * @return string */ public function getOutput() { return $this->output; } } <?php namespace pharext; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use Phar; use SplObjectStorage; /** * The extension install command executed by the extension phar */ class Installer implements Command { use CliCommand; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["n", "common-name", "PHP common program name, e.g. php5 or zts-php", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"], ["i", "ini", "Activate in this php.ini instead of loaded default php.ini", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], [null, "signature", "Show package signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show package license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "name", "Show package name", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "date", "Show package release date", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "release", "Show package release version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } private function extract(Phar $phar) { $temp = (new Task\Extract($phar))->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($temp); return $temp; } private function hooks(SplObjectStorage $phars) { $hook = []; foreach ($phars as $phar) { if (isset($phar["pharext_package.php"])) { $sdir = include $phar["pharext_package.php"]; if ($sdir instanceof SourceDir) { $this->args->compile($sdir->getArgs()); $hook[] = $sdir; } } } return $hook; } private function load() { $list = new SplObjectStorage(); $phar = new Phar(Phar::running(false)); $temp = $this->extract($phar); foreach ($phar as $entry) { $dep_file = $entry->getBaseName(); if (fnmatch("*.ext.phar*", $dep_file)) { $dep_phar = new Phar("$temp/$dep_file"); $list[$dep_phar] = $this->extract($dep_phar); } } /* the actual ext.phar at last */ $list[$phar] = $temp; return $list; } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { try { /* load the phar(s) */ $list = $this->load(); /* installer hooks */ $hook = $this->hooks($list); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EEXTRACT); } /* standard arg stuff */ $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "name", "date", "license", "release", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } try { /* post process hooks */ foreach ($hook as $sdir) { $sdir->setArgs($this->args); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } /* install packages */ try { foreach ($list as $phar) { $this->info("Installing %s ...\n", basename($phar->getPath())); $this->install($list[$phar]); $this->activate($list[$phar]); $this->info("Successfully installed %s!\n", basename($phar->getPath())); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EINSTALL); } } /** * Phpize + trinity */ private function install($temp) { // phpize $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"}); $phpize->run($this->verbosity()); // configure $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"}); $configure->run($this->verbosity()); // make $make = new Task\Make($temp); $make->run($this->verbosity()); // install $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $install = new Task\Make($temp, ["install"], $sudo); $install->run($this->verbosity()); } private function activate($temp) { if ($this->args->ini) { $files = [realpath($this->args->ini)]; } else { $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files()))); $files[] = php_ini_loaded_file(); } $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $type = $this->metadata("type") ?: "extension"; $activate = new Task\Activate($temp, $files, $type, $this->args->prefix, $this->args{"common-name"}, $sudo); if (!$activate->run($this->verbosity())) { $this->info("Extension already activated ...\n"); } } } <?php namespace pharext\Openssl; use pharext\Exception; class PrivateKey { /** * Private key * @var string */ private $key; /** * Public key * @var string */ private $pub; /** * Read a private key * @param string $file * @param string $password * @throws \pharext\Exception */ function __construct($file, $password) { /* there appears to be a bug with refcount handling of this * resource; when the resource is stored as property, it cannot be * "coerced to a private key" on openssl_sign() later in another method */ $key = openssl_pkey_get_private("file://$file", $password); if (!is_resource($key)) { throw new Exception("Could not load private key"); } openssl_pkey_export($key, $this->key); $this->pub = openssl_pkey_get_details($key)["key"]; } /** * Sign the PHAR * @param \Phar $package */ function sign(\Phar $package) { $package->setSignatureAlgorithm(\Phar::OPENSSL, $this->key); } /** * Export the public key to a file * @param string $file * @throws \pharext\Exception */ function exportPublicKey($file) { if (!file_put_contents("$file.tmp", $this->pub) || !rename("$file.tmp", $file)) { throw new Exception; } } } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use pharext\Exception; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-tree` to determine file list", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "pecl", "Use PECL package.xml to determine file list, name and release", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["S", "sign", "Sign the PHAR with a private key", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["E", "zend", "Mark as Zend Extension", CliArgs::OPTIONAL|CliARgs::SINGLE|CliArgs::NOARG], [null, "signature", "Show pharext signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show pharext license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "license", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } try { /* source needs to be evaluated before CliArgs validation, * so e.g. name and version can be overriden and CliArgs * does not complain about missing arguments */ $this->loadSource(); } catch (\Exception $e) { $errs[] = $e->getMessage(); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } printf("\n"); if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } $this->createPackage(); } /** * Download remote source * @param string $source * @return string local source */ private function download($source) { if ($this->args->git) { $task = new Task\GitClone($source); } else { /* print newline only once */ $done = false; $task = new Task\StreamFetch($source, function($bytes_pct) use(&$done) { if (!$done) { $this->info(" %3d%% [%s>%s] \r", floor($bytes_pct*100), str_repeat("=", round(50*$bytes_pct)), str_repeat(" ", round(50*(1-$bytes_pct))) ); if ($bytes_pct == 1) { $done = true; printf("\n"); } } }); } $local = $task->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($local); return $local; } /** * Extract local archive * @param stirng $source * @return string extracted directory */ private function extract($source) { try { $task = new Task\Extract($source); $dest = $task->run($this->verbosity()); } catch (\Exception $e) { if (false === strpos($e->getMessage(), "checksum mismatch")) { throw $e; } $dest = (new Task\PaxFixup($source))->run($this->verbosity()); } $this->cleanup[] = new Task\Cleanup($dest); return $dest; } /** * Localize a possibly remote source * @param string $source * @return string local source directory */ private function localize($source) { if (!stream_is_local($source)) { $source = $this->download($source); $this->cleanup[] = new Task\Cleanup($source); } $source = realpath($source); if (!is_dir($source)) { $source = $this->extract($source); $this->cleanup[] = new Task\Cleanup($source); if (!$this->args->git) { $source = (new Task\PeclFixup($source))->run($this->verbosity()); } } return $source; } /** * Load the source dir * @throws \pharext\Exception */ private function loadSource(){ if ($this->args["source"]) { $source = $this->localize($this->args["source"]); if ($this->args["pecl"]) { $this->source = new SourceDir\Pecl($source); } elseif ($this->args["git"]) { $this->source = new SourceDir\Git($source); } elseif (is_file("$source/pharext_package.php")) { $this->source = include "$source/pharext_package.php"; } else { $this->source = new SourceDir\Basic($source); } if (!$this->source instanceof SourceDir) { throw new Exception("Unknown source dir $source"); } foreach ($this->source->getPackageInfo() as $key => $val) { $this->args->$key = $val; } } } /** * Creates the extension phar */ private function createPackage() { try { $meta = array_merge($this->metadata(), [ "date" => date("Y-m-d"), "name" => $this->args->name, "release" => $this->args->release, "license" => @file_get_contents(current(glob($this->source->getBaseDir()."/LICENSE*"))), "stub" => "pharext_installer.php", "type" => $this->args->zend ? "zend_extension" : "extension", ]); $file = (new Task\PharBuild($this->source, $meta))->run($this->verbosity()); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } try { if ($this->args->sign) { $this->info("Using private key to sign phar ...\n"); $pass = (new Task\Askpass)->run($this->verbosity()); $sign = new Task\PharSign($file, $this->args->sign, $pass); $pkey = $sign->run($this->verbosity()); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::ESIGN); } if ($this->args->gzip) { try { $gzip = (new Task\PharCompress($file, Phar::GZ))->run(); $move = new Task\PharRename($gzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created gzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } if ($this->args->bzip) { try { $bzip = (new Task\PharCompress($file, Phar::BZ2))->run(); $move = new Task\PharRename($bzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created bzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } try { $move = new Task\PharRename($file, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created executable phar %s\n", $name); if (isset($pkey)) { $pkey->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\SourceDir; use FilesystemIterator; use IteratorAggregate; use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; class Basic implements IteratorAggregate, SourceDir { private $path; public function __construct($path) { $this->path = $path; } public function getBaseDir() { return $this->path; } public function getPackageInfo() { return []; } public function getArgs() { return []; } public function setArgs(Args $args) { } public function filter($current, $key, $iterator) { $sub = $current->getSubPath(); if ($sub === ".git" || $sub === ".hg" || $sub === ".svn") { return false; } return true; } public function getIterator() { $rdi = new RecursiveDirectoryIterator($this->path, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rci = new RecursiveCallbackFilterIterator($rdi, [$this, "filter"]); $rii = new RecursiveIteratorIterator($rci); foreach ($rii as $path => $child) { if (!$child->isDir()) { yield $path; } } } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\Cli\Args; use pharext\SourceDir; /** * Extension source directory which is a git repo */ class Git implements \IteratorAggregate, SourceDir { /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * @inheritdoc * @return array */ public function getPackageInfo() { return []; } /** * @inheritdoc * @return array */ public function getArgs() { return []; } /** * @inheritdoc */ public function setArgs(Args $args) { } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-tree -r --name-only HEAD", "r"))) { $path = realpath($this->path); while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { /* there may be symlinks, so no realpath here */ yield "$path/$file"; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\Exception; use pharext\SourceDir; use pharext\Tempfile; /** * A PECL extension source directory containing a v2 package.xml */ class Pecl implements \IteratorAggregate, SourceDir { /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * The package.xml * @var string */ private $file; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { if (is_file("$path/package2.xml")) { $sxe = simplexml_load_file($this->file = "$path/package2.xml"); } elseif (is_file("$path/package.xml")) { $sxe = simplexml_load_file($this->file = "$path/package.xml"); } else { throw new Exception("Missing package.xml in $path"); } $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $this->sxe = $sxe; $this->path = realpath($path); } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Retrieve gathered package info * @return Generator */ public function getPackageInfo() { if (($name = $this->sxe->xpath("/pecl:package/pecl:name"))) { yield "name" => (string) $name[0]; } if (($release = $this->sxe->xpath("/pecl:package/pecl:version/pecl:release"))) { yield "release" => (string) $release[0]; } if ($this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease")) { yield "zend" => true; } } /** * @inheritdoc * @see \pharext\SourceDir::getArgs() */ public function getArgs() { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } } /** * @inheritdoc * @see \pharext\SourceDir::setArgs() */ public function setArgs(Args $args) { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { /* hook */ $temp = tmpfile(); fprintf($temp, "<?php\nreturn new %s(__DIR__);\n", get_class($this)); rewind($temp); yield "pharext_package.php" => $temp; /* deps */ $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package"); foreach ($dependencies as $key => $dep) { if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) { usort($glob, function($a, $b) { return version_compare( substr($a, strpos(".ext.phar", $a)), substr($b, strpos(".ext.phar", $b)) ); }); yield end($glob); } } /* files */ yield realpath($this->file); foreach ($this->sxe->xpath("//pecl:file") as $file) { yield realpath($this->path ."/". $this->dirOf($file) ."/". $file["name"]); } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext; /** * Source directory interface, which should yield file names to package on traversal */ interface SourceDir extends \Traversable { /** * Retrieve the base directory * @return string */ public function getBaseDir(); /** * Retrieve gathered package info * @return array|Traversable */ public function getPackageInfo(); /** * Provide installer command line args * @return array|Traversable */ public function getArgs(); /** * Process installer command line args * @param \pharext\Cli\Args $args */ public function setArgs(Cli\Args $args); } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; use pharext\Tempfile; /** * PHP INI activation */ class Activate implements Task { /** * @var string */ private $cwd; /** * @var array */ private $inis; /** * @var string */ private $type; /** * @var string */ private $php_config; /** * @var string */ private $sudo; /** * @param string $cwd working directory * @param array $inis custom INI or list of loaded/scanned INI files * @param string $type extension or zend_extension * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 * @param string $sudo sudo command * @throws \pharext\Exception */ public function __construct($cwd, array $inis, $type = "extension", $prefix = null, $common_name = "php", $sudo = null) { $this->cwd = $cwd; $this->type = $type; $this->sudo = $sudo; if (!$this->inis = $inis) { throw new Exception("No PHP INIs given"); } $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->php_config = $cmd; } /** * @param bool $verbose * @return boolean false, if extension was already activated */ public function run($verbose = false) { if ($verbose !== false) { printf("Running INI activation ...\n"); } $extension = basename(current(glob("{$this->cwd}/modules/*.so"))); if ($this->type === "zend_extension") { $pattern = preg_quote((new ExecCmd($this->php_config))->run(["--extension-dir"])->getOutput() . "/$extension", "/"); } else { $pattern = preg_quote($extension, "/"); } foreach ($this->inis as $file) { if ($verbose) { printf("Checking %s ...\n", $file); } $temp = new Tempfile("phpini"); foreach (file($file) as $line) { if (preg_match("/^\s*{$this->type}\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) { return false; } fwrite($temp->getStream(), $line); } } /* not found; append to last processed file, which is the main by default */ if ($verbose) { printf("Activating in %s ...\n", $file); } fprintf($temp->getStream(), $this->type . "=%s\n", $extension); $temp->closeStream(); $path = $temp->getPathname(); $stat = stat($file); // owner transfer $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); $cmd = new ExecCmd("chown", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$ugid, $path]); // permission transfer $perm = decoct($stat["mode"] & 0777); $cmd = new ExecCmd("chmod", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$perm, $path]); // rename $cmd = new ExecCmd("mv", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$path, $file]); if ($verbose) { printf("Replaced %s ...\n", $file); } return true; } } <?php namespace pharext\Task; use pharext\Task; /** * Ask password on console */ class Askpass implements Task { /** * @var string */ private $prompt; /** * @param string $prompt */ public function __construct($prompt = "Password:") { $this->prompt = $prompt; } /** * @param bool $verbose * @return string */ public function run($verbose = false) { system("stty -echo"); printf("%s ", $this->prompt); $pass = fgets(STDIN, 1024); printf("\n"); system("stty echo"); if (substr($pass, -1) == "\n") { $pass = substr($pass, 0, -1); } return $pass; } } <?php namespace pharext\Task; use pharext\Task; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * List all library files of pharext to bundle with a phar */ class BundleGenerator implements Task { /** * @param bool $verbose * @return Generator */ public function run($verbose = false) { if ($verbose) { printf("Packaging pharext ... \n"); } $rdi = new RecursiveDirectoryIterator(dirname(dirname(__DIR__))); $rii = new RecursiveIteratorIterator($rdi); for ($rii->rewind(); $rii->valid(); $rii->next()) { if (!$rii->isDot()) { yield $rii->getSubPathname() => $rii->key(); } } } } <?php namespace pharext\Task; use pharext\Task; use FilesystemIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * Recursively cleanup FS entries */ class Cleanup implements Task { /** * @var string */ private $rm; public function __construct($rm) { $this->rm = $rm; } /** * @param bool $verbose */ public function run($verbose = false) { if ($verbose) { printf("Cleaning up %s ...\n", $this->rm); } if ($this->rm instanceof Tempfile) { unset($this->rm); } elseif (is_dir($this->rm)) { $rdi = new RecursiveDirectoryIterator($this->rm, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST); foreach ($rii as $path => $child) { if ($child->isDir()) { rmdir($path); } else { unlink($path); } } rmdir($this->rm); } else { @unlink($this->rm); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Runs extension's configure */ class Configure implements Task { /** * @var array */ private $args; /** * @var string */ private $cwd; /** * @param string $cwd working directory * @param array $args configure args * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 */ public function __construct($cwd, array $args = null, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->args = ["--with-php-config=$cmd"]; if ($args) { $this->args = array_merge($this->args, $args); } } public function run($verbose = false) { if ($verbose !== false) { printf("Running ./configure ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("./configure", $verbose); $cmd->run($this->args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Task; use pharext\Tempdir; use Phar; use PharData; /** * Extract a package archive */ class Extract implements Task { /** * @var Phar(Data) */ private $source; /** * @param mixed $source archive location */ public function __construct($source) { if ($source instanceof Phar || $source instanceof PharData) { $this->source = $source; } else { $this->source = new PharData($source); } } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose) { printf("Extracting %s ...\n", basename($this->source->getPath())); } $dest = new Tempdir("extract"); $this->source->extractTo($dest); return $dest; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Task; use pharext\Tempdir; /** * Clone a git repo */ class GitClone implements Task { /** * @var string */ private $source; /** * @param string $source git repo location */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $local = new Tempdir("gitclone"); $cmd = new ExecCmd("git", $verbose); $cmd->run(["clone", $this->source, $local]); return $local; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Exception; use pharext\Task; /** * Run make in the source dir */ class Make implements Task { /** * @var string */ private $cwd; /** * @var array */ private $args; /** * @var string */ private $sudo; /** * * @param string $cwd working directory * @param array $args make's arguments * @param string $sudo sudo command */ public function __construct($cwd, array $args = null, $sudo = null) { $this->cwd = $cwd; $this->sudo = $sudo; $this->args = $args; } /** * * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running make"); if ($this->args) { foreach ($this->args as $arg) { printf(" %s", $arg); } } printf(" ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("make", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $args = $this->args; if (!$verbose) { $args = array_merge((array) $args, ["-s"]); } $cmd->run($args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; class PaxFixup implements Task { private $source; public function __construct($source) { $this->source = $source; } private function openArchive($source) { $hdr = file_get_contents($source, false, null, 0, 3); if ($hdr === "\x1f\x8b\x08") { $fd = fopen("compress.zlib://$source", "r"); } elseif ($hdr === "BZh") { $fd = fopen("compress.bzip2://$source", "r"); } else { $fd = fopen($source, "r"); } if (!is_resource($fd)) { throw new Exception; } return $fd; } public function run($verbose = false) { if ($verbose !== false) { printf("Fixing up a tarball with global pax header ...\n"); } $temp = new Tempfile("paxfix"); stream_copy_to_stream($this->openArchive($this->source), $temp->getStream(), -1, 1024); $temp->closeStream(); return (new Extract((string) $temp))->run($verbose); } }<?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Fixup package.xml files in an extracted PECL dir */ class PeclFixup implements Task { /** * @var string */ private $source; /** * @param string $source source directory */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return string sanitized source location * @throws \pahrext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Sanitizing PECL dir ...\n"); } $dirs = glob("{$this->source}/*", GLOB_ONLYDIR); $files = array_diff(glob("{$this->source}/*"), $dirs); $check = array_reduce($files, function($r, $v) { return $v && fnmatch("package*.xml", basename($v)); }, true); if (count($dirs) !== 1 || !$check) { throw new Exception("Does not look like an extracted PECL dir: {$this->source}"); } $dest = current($dirs); foreach ($files as $file) { if ($verbose) { printf("Moving %s into %s ...\n", basename($file), basename($dest)); } if (!rename($file, "$dest/" . basename($file))) { throw new Exception; } } return $dest; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\SourceDir; use pharext\Task; use pharext\Tempname; use Phar; /** * Build phar */ class PharBuild implements Task { /** * @var \pharext\SourceDir */ private $source; /** * @var array */ private $meta; /** * @var bool */ private $readonly; /** * @param SourceDir $source extension source directory * @param array $meta phar meta data * @param bool $readonly whether the stub has -dphar.readonly=1 set */ public function __construct(SourceDir $source = null, array $meta = null, $readonly = true) { $this->source = $source; $this->meta = $meta; $this->readonly = $readonly; } /** * @param bool $verbose * @return \pharext\Tempname * @throws \pharext\Exception */ public function run($verbose = false) { /* Phar::compress() and ::convert*() use strtok("."), ugh! * so, be sure to not use any other dots in the filename * except for .phar */ $temp = new Tempname("", "-pharext.phar"); $phar = new Phar($temp); $phar->startBuffering(); if ($this->meta) { $phar->setMetadata($this->meta); if (isset($this->meta["stub"])) { $phar->setDefaultStub($this->meta["stub"]); $phar->setStub("#!/usr/bin/php -dphar.readonly=" . intval($this->readonly) ."\n". $phar->getStub()); } } $phar->buildFromIterator((new Task\BundleGenerator)->run()); if ($this->source) { if ($verbose) { $bdir = $this->source->getBaseDir(); $blen = strlen($bdir); foreach ($this->source as $index => $file) { if (is_resource($file)) { printf("Packaging %s ...\n", $index); $phar[$index] = $file; } else { printf("Packaging %s ...\n", $index = trim(substr($file, $blen), "/")); $phar->addFile($file, $index); } } } else { $phar->buildFromIterator($this->source, $this->source->getBaseDir()); } } $phar->stopBuffering(); if (!chmod($temp, fileperms($temp) | 0111)) { throw new Exception; } return $temp; } }<?php namespace pharext\Task; use pharext\Task; use Phar; /** * Clone a compressed copy of a phar */ class PharCompress implements Task { /** * @var string */ private $file; /** * @var Phar */ private $package; /** * @var int */ private $encoding; /** * @var string */ private $extension; /** * @param string $file path to the original phar * @param int $encoding Phar::GZ or Phar::BZ2 */ public function __construct($file, $encoding) { $this->file = $file; $this->package = new Phar($file); $this->encoding = $encoding; switch ($encoding) { case Phar::GZ: $this->extension = ".gz"; break; case Phar::BZ2: $this->extension = ".bz2"; break; } } /** * @param bool $verbose * @return string */ public function run($verbose = false) { if ($verbose) { printf("Compressing %s ...\n", basename($this->package->getPath())); } $phar = $this->package->compress($this->encoding); $meta = $phar->getMetadata(); if (isset($meta["stub"])) { /* drop shebang */ $phar->setDefaultStub($meta["stub"]); } return $this->file . $this->extension; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Rename the phar archive */ class PharRename implements Task { /** * @var string */ private $phar; /** * @var string */ private $dest; /** * @var string */ private $name; /** * @param string $phar path to phar * @param string $dest destination dir * @param string $name package name */ public function __construct($phar, $dest, $name) { $this->phar = $phar; $this->dest = $dest; $this->name = $name; } /** * @param bool $verbose * @return string path to renamed phar * @throws \pharext\Exception */ public function run($verbose = false) { $extension = substr(strstr($this->phar, "-pharext.phar"), 8); $name = sprintf("%s/%s.ext%s", $this->dest, $this->name, $extension); if ($verbose) { printf("Renaming %s to %s ...\n", basename($this->phar), basename($name)); } if (!rename($this->phar, $name)) { throw new Exception; } return $name; } } <?php namespace pharext\Task; use pharext\Openssl; use pharext\Task; use Phar; /** * Sign the phar with a private key */ class PharSign implements Task { /** * @var Phar */ private $phar; /** * @var \pharext\Openssl\PrivateKey */ private $pkey; /** * * @param mixed $phar phar instance or path to phar * @param string $pkey path to private key * @param string $pass password for the private key */ public function __construct($phar, $pkey, $pass) { if ($phar instanceof Phar || $phar instanceof PharData) { $this->phar = $phar; } else { $this->phar = new Phar($phar); } $this->pkey = new Openssl\PrivateKey($pkey, $pass); } /** * @param bool $verbose * @return \pharext\Openssl\PrivateKey */ public function run($verbose = false) { if ($verbose) { printf("Signing %s ...\n", basename($this->phar->getPath())); } $this->pkey->sign($this->phar); return $this->pkey; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Run phpize in the extension source directory */ class Phpize implements Task { /** * @var string */ private $phpize; /** * * @var string */ private $cwd; /** * @param string $cwd working directory * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP program common name, e.g. php5 */ public function __construct($cwd, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "ize"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->phpize = $cmd; } /** * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running %s ...\n", $this->phpize); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd($this->phpize, $verbose); $cmd->run(); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; /** * Fetch a remote archive */ class StreamFetch implements Task { /** * @var string */ private $source; /** * @var callable */ private $progress; /** * @param string $source remote file location * @param callable $progress progress callback */ public function __construct($source, callable $progress) { $this->source = $source; $this->progress = $progress; } private function createStreamContext() { $progress = $this->progress; /* avoid bytes_max bug of older PHP versions */ $maxbytes = 0; return stream_context_create([],["notification" => function($notification, $severity, $message, $code, $bytes_cur, $bytes_max) use($progress, &$maxbytes) { if ($bytes_max > $maxbytes) { $maxbytes = $bytes_max; } switch ($notification) { case STREAM_NOTIFY_CONNECT: $progress(0); break; case STREAM_NOTIFY_PROGRESS: $progress($maxbytes > 0 ? $bytes_cur/$maxbytes : .5); break; case STREAM_NOTIFY_COMPLETED: /* this is sometimes not generated, why? */ $progress(1); break; } }]); } /** * @param bool $verbose * @return \pharext\Task\Tempfile * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $context = $this->createStreamContext(); if (!$remote = fopen($this->source, "r", false, $context)) { throw new Exception; } $local = new Tempfile("remote"); if (!stream_copy_to_stream($remote, $local->getStream())) { throw new Exception; } $local->closeStream(); /* STREAM_NOTIFY_COMPLETED is not generated, see above */ call_user_func($this->progress, 1); return $local; } } <?php namespace pharext; /** * Simple task interface */ interface Task { public function run($verbose = false); } <?php namespace pharext; /** * Create a temporary directory */ class Tempdir extends \SplFileInfo { /** * @param string $prefix prefix to uniqid() * @throws \pharext\Exception */ public function __construct($prefix) { $temp = new Tempname($prefix); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception("Could not create tempdir: ".error_get_last()["message"]); } parent::__construct($temp); } } <?php namespace pharext; /** * Create a new temporary file */ class Tempfile extends \SplFileInfo { /** * @var resource */ private $handle; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension * @throws \pharext\Exception */ public function __construct($prefix, $suffix = ".tmp") { $tries = 0; $omask = umask(077); do { $path = new Tempname($prefix, $suffix); $this->handle = fopen($path, "x"); } while (!is_resource($this->handle) && $tries++ < 10); umask($omask); if (!is_resource($this->handle)) { throw new Exception("Could not create temporary file"); } parent::__construct($path); } /** * Unlink the file */ public function __destruct() { @unlink($this->getPathname()); } /** * Close the stream */ public function closeStream() { fclose($this->handle); } /** * Retrieve the stream resource * @return resource */ public function getStream() { return $this->handle; } } <?php namespace pharext; use pharext\Exception; /** * A temporary file/directory name */ class Tempname { /** * @var string */ private $name; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension */ public function __construct($prefix, $suffix = null) { $temp = sys_get_temp_dir() . "/pharext-" . posix_getlogin(); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception; } $this->name = $temp ."/". uniqid($prefix) . $suffix; } /** * @return string */ public function __toString() { return (string) $this->name; } } <?php namespace pharext; const VERSION = "3.0.1"; <?php /** * The installer sub-stub for extension phars */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $installer = new pharext\Installer(); $installer->run($argc, $argv); <?php /** * The packager sub-stub for bin/pharext */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $packager = new pharext\Packager(); $packager->run($argc, $argv); <?php return new pharext\SourceDir\Pecl(__DIR__); <?xml version="1.0" encoding="ISO-8859-1"?> <package packagerversion="1.9.4" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> <name>memcached</name> <channel>pecl.php.net</channel> <summary>PHP extension for interfacing with memcached via libmemcached library</summary> <description>This extension uses libmemcached library to provide API for communicating with memcached servers.</description> <lead> <name>Andrei Zmievski</name> <user>andrei</user> <email>andrei@php.net</email> <active>yes</active> </lead> <lead> <name>Mikko Koppanen</name> <user>mkoppanen</user> <email>mkoppanen@php.net</email> <active>yes</active> </lead> <date>2014-04-01</date> <time>10:23:39</time> <version> <release>2.2.0</release> <api>2.2.0</api> </version> <stability> <release>stable</release> <api>stable</api> </stability> <license uri="http://www.php.net/license">PHP</license> <notes> - Added the OPT_SERVER_TIMEOUT_LIMIT behaviour </notes> <contents> <dir name="/"> <file md5sum="ffad5125eeeeaeedfa305a83b62b6932" name="tests/001.phpt" role="test" /> <file md5sum="0f5e4795060732fb47847deed35583c8" name="tests/version.phpt" role="test" /> <file md5sum="0384a8b5ac92f5a4f6e8d020289a9478" name="tests/bug_16084.phpt" role="test" /> <file md5sum="fc52c9ad7ebc2eeb00806acb9c24a6ce" name="tests/bug_16959.phpt" role="test" /> <file md5sum="33e5411875354608c353301319045b7f" name="tests/bug_17137.phpt" role="test" /> <file md5sum="76825971bce0c379955619aca4bd721d" name="tests/bug_18639.phpt" role="test" /> <file md5sum="2c339efc3903961eea563b7aa324058a" name="tests/callback_exception.phpt" role="test" /> <file md5sum="0934cd2b89d78e2b5d1dcdb6bce2c733" name="tests/callback_exception_2.phpt" role="test" /> <file md5sum="56c8f22dab8191f4c5273965207ed4bd" name="tests/cas.phpt" role="test" /> <file md5sum="e509ce004399b26c7cf62f0185575e23" name="tests/cas_multi.phpt" role="test" /> <file md5sum="510bc06e39c486189bcd4adc0f7fcfba" name="tests/check_if_persistent.phpt" role="test" /> <file md5sum="3f6295c86792f140ddbffb09c0ed335e" name="tests/check_if_pristine.phpt" role="test" /> <file md5sum="fda36564ba55d7353c1afa1e83788eaf" name="tests/clone.phpt" role="test" /> <file md5sum="8a7790a0408347eb9c4728d1a54da14e" name="tests/compression_types.phpt" role="test" /> <file md5sum="3b0a72b54a2d489d9054cf553159c70f" name="tests/conf_persist.phpt" role="test" /> <file md5sum="d96726a3c4acce279cda1cba6804051a" name="tests/construct.phpt" role="test" /> <file md5sum="0a2066b54576c429e5cee936df6414d3" name="tests/construct_persistent.phpt" role="test" /> <file md5sum="26e682c3513c35084c577606f633cd73" name="tests/deleted.phpt" role="test" /> <file md5sum="26868bbef66bc747ab05839aca18546a" name="tests/deletemulti.phpt" role="test" /> <file md5sum="16bb6df11e663b1ba5fd0ae3af29e9a2" name="tests/deletemultitypes.phpt" role="test" /> <file md5sum="0c23ccb90df447b3e004e548ff911dfb" name="tests/expire.phpt" role="test" /> <file md5sum="d76e23b83fdbe721bc0cd7296c68b1bf" name="tests/flush_buffers.phpt" role="test" /> <file md5sum="b81f4e5b60cc6d4e8e9ca68fbcef8bf1" name="tests/getdelayed.phpt" role="test" /> <file md5sum="087bd85bd41cb57c838acb388a08f5af" name="tests/getserverlist.phpt" role="test" /> <file md5sum="0ca92ceb34c2e349de929ef1a63eb50c" name="tests/gh_21.phpt" role="test" /> <file md5sum="bc52e5001fb70bccb2bebdd354868841" name="tests/gh_77.phpt" role="test" /> <file md5sum="335d3ae8c73ecb2737682472806c94b8" name="tests/gh_90.phpt" role="test" /> <file md5sum="54780d915d29e944c2f0c3be7c69839a" name="tests/invoke_callback.phpt" role="test" /> <file md5sum="3f632dde09a07dd1c9e3dcfd3bcc5ae2" name="tests/invoke_callback_2.phpt" role="test" /> <file md5sum="ff84ed4c2b7f038b31bc16d06511582d" name="tests/invoke_callback_twice.phpt" role="test" /> <file md5sum="8488b98721d121068ad78677d41e35c9" name="tests/localserver.phpt" role="test" /> <file md5sum="da7d72ace7b9712352c962bc2175c383" name="tests/multi_order.phpt" role="test" /> <file md5sum="8e2362a2d31836f10b8429dd54429bfb" name="tests/no-not-found.phpt" role="test" /> <file md5sum="36545148073bbb06a28a91a6b2a3f128" name="tests/options.phpt" role="test" /> <file md5sum="d0751ba5e5454b02860172a1550bb0a7" name="tests/pr_75.phpt" role="test" /> <file md5sum="e0f7d2a51054795ef0c95f2766ab6950" name="tests/rescode.phpt" role="test" /> <file md5sum="740163bf3288ccce4f806fe52a48db38" name="tests/session_badconf_emptyprefix.phpt" role="test" /> <file md5sum="e8c61afd916037c9545a9ba8ba1ea5f2" name="tests/session_badconf_locktime.phpt" role="test" /> <file md5sum="12c256bead9bde9df3f5cb1d7cb21c15" name="tests/session_badconf_prefix.phpt" role="test" /> <file md5sum="2a4a8671dc6c6a06a458455baced3780" name="tests/session_badconf_servers.phpt" role="test" /> <file md5sum="1af4fe41cc7e359af91b9d5d3b103e30" name="tests/session_basic.phpt" role="test" /> <file md5sum="8da35924bb0de59507a6fb0f2f4ca5b2" name="tests/set_large.phpt" role="test" /> <file md5sum="fe14c7f39bc308ece35c781c24f15b24" name="tests/setoptions.phpt" role="test" /> <file md5sum="d6968f26a314f5b78d15469fc7c1af1e" name="tests/touch_binary.phpt" role="test" /> <file md5sum="2c4f69af7f3d64599c9a77f567b49d9c" name="tests/types.inc" role="test" /> <file md5sum="d771a35fb016f7b1b42c0a3a7029bdd2" name="tests/types_igbinary.phpt" role="test" /> <file md5sum="b70996d9017231dc5573af8d0af4d2c2" name="tests/types_igbinary_multi.phpt" role="test" /> <file md5sum="b3a681be85874706e8b1950c5cde4cdf" name="tests/types_json.phpt" role="test" /> <file md5sum="ad89eb6331a99ddfe4ece8aa76ae753d" name="tests/types_json_multi.phpt" role="test" /> <file md5sum="36dd006194f07fea52fd8b4ff35e9e06" name="tests/types_msgpack.phpt" role="test" /> <file md5sum="4f081965314e1d7385e7c56581460869" name="tests/types_msgpack_multi.phpt" role="test" /> <file md5sum="4fd07f03dc521051601276088bc80b85" name="tests/types_php.phpt" role="test" /> <file md5sum="444bdd5da34856af35025da9c5d9b41a" name="tests/types_php_multi.phpt" role="test" /> <file md5sum="bfb8c75a9e4937be6b05604c01732343" name="tests/undefined_set.phpt" role="test" /> <file md5sum="5c25315a28b4b6eb71b078265111bef1" name="tests/vbucket.phpt" role="test" /> <file md5sum="dc81623e2bde33a860a7bbd3a0dbfff3" name="tests/user-flags.phpt" role="test" /> <file md5sum="8997fb60560d598d6e7e38212fae75fb" name="tests/gh_93.phpt" role="test" /> <file md5sum="6b8db26f01822060c68e2911e99f4242" name="tests/add.phpt" role="test" /> <file md5sum="e5f9f658d4826c1da3544ab1c9d98ba9" name="tests/bad_construct.phpt" role="test" /> <file md5sum="597d3d854148c2935cee5a317cd59237" name="tests/append.phpt" role="test" /> <file md5sum="d9010841a92b0a8bdfe038d1fd804b7a" name="tests/prepend.phpt" role="test" /> <file md5sum="9619b31389d2cc5747d3fa05bddf65f8" name="tests/replace.phpt" role="test" /> <file md5sum="2f2049343b82686aca1a094eb861179a" name="tests/getmulti.phpt" role="test" /> <file md5sum="37562c0d3fc17ae354e1cf71ea44e13f" name="tests/setmulti.phpt" role="test" /> <file md5sum="2d1b754f203a24735f7c053719c5ec8c" name="tests/cachecallback.phpt" role="test" /> <file md5sum="a39ad006935facee848c97c23f003407" name="tests/incrdecr.phpt" role="test" /> <file md5sum="ea444135ab0d06b83ead7597655d346e" name="tests/incrdecr_initial.phpt" role="test" /> <file md5sum="d7563dc4883bddd980ca3fbc40074aaf" name="tests/incrdecr_invalid_key.phpt" role="test" /> <file md5sum="c05a051e593fad3a5ab1ad7f2364b7fa" name="tests/incrdecr_bykey.phpt" role="test" /> <file md5sum="fb8828683359092f892b4fd56476a211" name="tests/invalid_options.phpt" role="test" /> <file md5sum="af57960a02a3ca80061caeded35c68af" name="tests/keys.phpt" role="test" /> <file md5sum="a4058bd7f5993894fb867bafa57097c8" name="tests/testdata.res" role="test" /> <file md5sum="2e53ab40ca172d8d8d4e5b5db0b72088" name="tests/config.inc" role="test" /> <file md5sum="6c0907cd401bc5bc51a9727d35d24493" name="tests/sasl_basic.phpt" role="test" /> <file md5sum="46a91463b6ac1b06d53e3dc17f52612d" name="tests/getserverbykey.phpt" role="test" /> <file md5sum="44adb2dd07cbfff024040efbbc574931" name="README.markdown" role="doc" /> <file md5sum="f15b54956edc11e9bf1c995385b433b4" name="CREDITS" role="doc" /> <file md5sum="cb564efdf78cce8ea6e4b5a4f7c05d97" name="LICENSE" role="doc" /> <file md5sum="0f1a885664ef61e3c4bfa7a33ec377cd" name="ChangeLog" role="doc" /> <file md5sum="10fa240a5e499eb7245809abb98b3c4b" name="memcached-api.php" role="doc" /> <file md5sum="5d1048a41d7e049826eab37a0ae7bc19" name="memcached.ini" role="doc" /> <file md5sum="543b3b1b8571c4b9c02eafee4a6f3fa3" name="fastlz/LICENSE" role="doc" /> <file md5sum="d7187ad75f4dfba1e0cf103c56d237f4" name="config.m4" role="src" /> <file md5sum="cf775e2ff79d560509d887a4c5992196" name="config.w32" role="src" /> <file md5sum="a136ca6390932cb883ad3fde60c86d3d" name="php_memcached.c" role="src" /> <file md5sum="60873a988ab7ff79dc081faf3af71a9d" name="php_memcached.h" role="src" /> <file md5sum="628eddcbeba907f441562c00f06a1649" name="php_memcached_private.h" role="src" /> <file md5sum="5a14ce3e6f73140257f99f8bb353e2bc" name="php_memcached_session.c" role="src" /> <file md5sum="c92bc3e2db00fe51b070b3701a898b01" name="php_memcached_session.h" role="src" /> <file md5sum="068f2dd1b10ee5cc00b8597a58db4584" name="php_libmemcached_compat.h" role="src" /> <file md5sum="77d61372e8502504d211be0482b02757" name="php_libmemcached_compat.c" role="src" /> <file md5sum="3be4bccad38b9630099b30945ea8fbf8" name="php_memcached_server.h" role="src" /> <file md5sum="7e3e312047ee729715d94cbf608ede36" name="php_memcached_server.c" role="src" /> <file md5sum="b340607bcb66007b13583e18d571d6dc" name="g_fmt.c" role="src" /> <file md5sum="94fb8525d4419947cec4653a302be41a" name="g_fmt.h" role="src" /> <file md5sum="89384a08695fc238e9fbc41b0a14b5a7" name="fastlz/fastlz.c" role="src" /> <file md5sum="d49275e3dcc1d23d6ce0041648be8e14" name="fastlz/fastlz.h" role="src" /> </dir> </contents> <dependencies> <required> <php> <min>5.2.0</min> <max>6.0.0</max> <exclude>6.0.0</exclude> </php> <pearinstaller> <min>1.4.0b1</min> </pearinstaller> </required> </dependencies> <providesextension>memcached</providesextension> <extsrcrelease> <configureoption default="no" name="with-libmemcached-dir" prompt="libmemcached directory" /> </extsrcrelease> <changelog> <release> <stability> <release>stable</release> <api>stable</api> </stability> <version> <release>2.2.0</release> <api>2.2.0</api> </version> <date>2014-04-01</date> <notes> - Added the OPT_SERVER_TIMEOUT_LIMIT behaviour </notes> </release> <release> <stability> <release>beta</release> <api>stable</api> </stability> <version> <release>2.2.0b1</release> <api>2.2.0</api> </version> <date>2013-10-28</date> <notes> - Reinstate support for libememcached 0.x series - Added SASL support to session handler - Added Memcached::flushBuffers as per GH #78 - Fixes GH #54: Fixed UDP server adding with newer libmemcached - Fixed PHP bug #65334: (Segfault if uncompress value failed) - Fixes GH #14: get with cas token fails to fetch all results - Fixes GH #69: compiling on CentOS 6.4 with libmemcached 1.0.17 </notes> </release> <release> <stability> <release>beta</release> <api>stable</api> </stability> <version> <release>2.2.0RC1</release> <api>2.2.0</api> </version> <date>2014-03-12</date> <notes> - Fixes incorrect size when compressing serialized objects - Fixes endianess of compressed values </notes> </release> <release> <stability> <release>beta</release> <api>stable</api> </stability> <version> <release>2.2.0b1</release> <api>2.2.0</api> </version> <date>2013-10-28</date> <notes> - Reinstate support for libememcached 0.x series - Added SASL support to session handler - Added Memcached::flushBuffers as per GH #78 - Fixes GH #54: Fixed UDP server adding with newer libmemcached - Fixed PHP bug #65334: (Segfault if uncompress value failed) - Fixes GH #14: get with cas token fails to fetch all results - Fixes GH #69: compiling on CentOS 6.4 with libmemcached 1.0.17 </notes> </release> <release> <stability> <release>stable</release> <api>stable</api> </stability> <version> <release>2.1.0</release> <api>2.1.0</api> </version> <date>2012-08-06</date> <notes> - Drop support for libmemcached 0.x series, now 1.0.x is required - Add support for virtual bucket distribution - Fix compilation against PHP 5.2 </notes> </release> <release> <stability> <release>stable</release> <api>stable</api> </stability> <version> <release>2.0.1</release> <api>2.0.1</api> </version> <date>2012-03-03</date> <notes> - Fix embedded version number to be not -dev </notes> </release> <release> <stability> <release>stable</release> <api>stable</api> </stability> <version> <release>2.0.0</release> <api>2.0.0</api> </version> <date>2012-03-02</date> <notes> - Add touch() and touchByKey() methods - Add resetServerList() and quit() methods - Support binary protocol in sessions - Make it work with libmemcached up to 1.0.4 - Test against PHP 5.4.0 </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>2.0.0b2</release> <api>2.0.0b2</api> </version> <date>2011-06-24</date> <notes> - Add OPT_REMOVE_FAILED_SERVERS option. - Make it work with libmemcached up to 0.49. - Fix a case where invalid session ID could lock the script. - Improve session support: * Add support for libmemcached config string * Add persistence support via PERSISTENT=persistent_id prefix of the save_path - Add 3rd parameter to the __construct() that allows specification of libmemcached configuration string - Fix a possible crash in __construct() when using persistent connections - Add work-around a bug in libmemcached < 0.50 that causes truncation of last character of server key prefix - When using multiple servers implement transparent fail-over - Fix php_memc_cas_impl() implementation when server_key is not being used - Add support for incrementByKey() and decrementByKey() - Make increment/decrement initialize value when it is not available (when using binary protocol) </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>2.0.0b1</release> <api>2.0.0b1</api> </version> <date>2011-03-12</date> <notes> - Change the return value for non-existing keys to be NULL rather than 'false', affects simple get only - Add fastlz library that provides better/faster payload compression - Add configure switch to enable/disable JSON serialization support - Add getAllKeys() method - Add deleteMulti() and deleteMultiByKey() methods - Add isPristine() and isPersistent() methods - Add setOptions() method to set multiple options at once - Add SERIALIZER_JSON_ARRAY type that decodes JSON payloads as arrays instead of objects - Add support for Unix domain socket connections - Add memcached.compression_threshold INI setting - Add memcached.compression_factor INI setting - Add memcached.compression_type INI setting - Implement a few speed optimizations - Many bug fixes and memory leaks plugged - Add several more tests - Add constants for libmemcached 0.37+: * Memcached::OPT_NUMBER_OF_REPLICAS * Memcached::OPT_RANDOMIZE_REPLICA_READ - Add 'on_new' callback to constructor - Add SASL support </notes> </release> <release> <stability> <release>stable</release> <api>stable</api> </stability> <version> <release>1.0.2</release> <api>1.0.2</api> </version> <date>2010-05-03</date> <notes> - Fix build for libmemcached-0.39 (memcached_server_list() issue) </notes> </release> <release> <stability> <release>stable</release> <api>stable</api> </stability> <version> <release>1.0.1</release> <api>1.0.1</api> </version> <date>2010-03-11</date> <notes> - Fix build for libmemcached-0.38. </notes> </release> <release> <stability> <release>stable</release> <api>stable</api> </stability> <version> <release>1.0.0</release> <api>1.0.0</api> </version> <date>2009-07-04</date> <notes> - First stable release. - Add getResultMessage() method. - Fix OPT_RECV_TIMEOUT definition. - Initialize Session lock wait to max execution time (if max execution time is unlimited, default to 30 seconds). </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>0.2.0</release> <api>0.2.0</api> </version> <date>2009-06-04</date> <notes> - Refactor the way payload types are stored in memcached flags to optimize the structure and allow for future expansion. WARNING! You have to flush the cache when upgrading from an older version. - Add JSON serializer support, requires PHP 5.2.10+. - Add HAVE_JSON and HAVE_IGBINARY class constants that indicate whether the respective serializers are available. - Add 'flags' parameter to getMulti() and getMultiByKey(). - Add GET_PRESERVE_ORDER class constant that can be used with abovementioned flags parameter to make the order of the keys in the response match the request. - Fix an issue with retrieving 0-length payloads (FALSE boolean value). - Add several tests. </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>0.1.5</release> <api>0.1.5</api> </version> <date>2009-03-31</date> <notes> - Implement getVersion(). - Add support for preserving boolean value types. - Fix crash when child class does not call constructor. - Fix bug #16084 (Crash when addServers is called with an associative array). - ZTS compilation fixes. </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>0.1.4</release> <api>0.1.4</api> </version> <date>2009-02-13</date> <notes> - Fix compilation against PHP 5.3. - Add support for 'igbinary' serializer (Oleg Grenrus) </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>0.1.3</release> <api>0.1.3</api> </version> <date>2009-02-06</date> <notes> - Bludgeon bug #15896 (Memcached setMulti error) into submission. </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>0.1.2</release> <api>0.1.2</api> </version> <date>2009-02-06</date> <notes> - Fix bug #15896 (Memcached setMulti error). - Check for empty key in getServerByKey(). - Allow passing 'null' for callbacks. - get() with cas token fetching wasn't erroring out properly. - Rename certain parameters in the API to be more clear. - Allow only strings as the append/prepend value. - Remove expiration parameter from append/prepend. </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>0.1.1</release> <api>0.1.1</api> </version> <date>2009-02-02</date> <notes> - Add OPT_LIBKETAMA_COMPATIBLE option. - Implement addServers() method. - Swap internal compressed and serialized flags to be compatible with other clients. </notes> </release> <release> <stability> <release>beta</release> <api>beta</api> </stability> <version> <release>0.1.0</release> <api>0.1.0</api> </version> <date>2009-01-29</date> <notes> - Initial PECL release </notes> </release> </changelog> </package> --TEST-- Check for memcached presence --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php echo "memcached extension is available"; ?> --EXPECT-- memcached extension is available --TEST-- Get version --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); var_dump ($m->getVersion ()); echo "OK" . PHP_EOL; ?> --EXPECTF-- array(1) { ["%s:%d"]=> string(6) "%d.%d.%d" } OK--TEST-- Memcached: Bug #16084 (Crash when addServers is called with an associative array) --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $servers = array ( 0 => array ( 'KEYHERE' => 'localhost', 11211, 3 ), ); $m = new memcached(); var_dump($m->addServers($servers)); var_dump($m->getServerList()); ?> --EXPECT-- bool(true) array(1) { [0]=> array(2) { ["host"]=> string(9) "localhost" ["port"]=> int(11211) } } --TEST-- Memcached: Bug #16959 (getMulti + BINARY_PROTOCOL problem) --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $cache = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true )); $cache->set('key_0', 'value0'); $cache->set('key_0_additional', 'value0_additional'); // -------------- NORMAL echo "NORMAL\n"; $keys = array( 'key_0', 'key_0_additional' ); $values = $cache->getMulti($keys); echo $cache->getResultMessage(), "\n"; echo "Values:\n"; foreach ($values as $k => $v) { var_dump($k); var_dump($v); var_dump($values[$k]); } // --------------- REVERSED KEY ORDER echo "REVERSED KEY ORDER\n"; $keys = array( 'key_0_additional', 'key_0' ); $values = $cache->getMulti($keys); echo $cache->getResultMessage(), "\n"; echo "Values:\n"; foreach ($values as $k => $v) { var_dump($k); var_dump($v); var_dump($values[$k]); } --EXPECT-- NORMAL SUCCESS Values: string(5) "key_0" string(6) "value0" string(6) "value0" string(16) "key_0_additional" string(17) "value0_additional" string(17) "value0_additional" REVERSED KEY ORDER SUCCESS Values: string(16) "key_0_additional" string(17) "value0_additional" string(17) "value0_additional" string(5) "key_0" string(6) "value0" string(6) "value0" --TEST-- Change prefix, pecl bug #17137 --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $memcache = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true, Memcached::OPT_PREFIX_KEY => 'prefix1', )); $memcache2 = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true, Memcached::OPT_PREFIX_KEY => 'prefix2', )); var_dump($memcache->getOption(Memcached::OPT_PREFIX_KEY)); var_dump($memcache->set('test', "val_prefix1", 120)); var_dump($memcache->get('test')); var_dump($memcache2->getOption(Memcached::OPT_PREFIX_KEY)); var_dump($memcache2->set('test', "val_prefix2", 120)); var_dump($memcache2->get('test')); var_dump($memcache->get('test')); --EXPECT-- string(7) "prefix1" bool(true) string(11) "val_prefix1" string(7) "prefix2" bool(true) string(11) "val_prefix2" string(11) "val_prefix1" --TEST-- Memcached::getServerByKey(): Bug pecl#18639 (Segfault in getServerByKey) --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); var_dump($m->set('test', 'test1')); var_dump($m->getServerByKey('1')); --EXPECTF-- bool(true) array(3) { ["host"]=> string(9) "127.0.0.1" ["port"]=> int(11211) ["weight"]=> int(%r[01]%r) } --TEST-- make sure that callback exception behaves correctly --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php function throw_something(Memcached $object, $p_id = null) { throw new Exception("from cb"); } function empty_cb(Memcached $object, $p_id = null) { echo "empty_cb called\n"; } try { $m = new Memcached('test_id', 'throw_something'); echo "fail\n"; } catch (Exception $e) { echo "success\n"; } try { $m = new Memcached('test_id', 'throw_something'); echo "fail\n"; } catch (Exception $e) { echo "success\n"; } try { $m = new Memcached('test_id', 'empty_cb'); $m = new Memcached('test_id', 'empty_cb'); } catch (Exception $e) { echo "fail\n"; } try { } catch (Exception $e) { echo "fail\n"; } echo "OK\n"; ?> --EXPECT-- success success empty_cb called OK --TEST-- Callback initializer throws and dies --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php function init_cb($m, $id) { echo "ran throwing cb\n"; var_dump($m->isPersistent()); throw new RuntimeException('Cb exception'); } function init_cb_die($m, $id) { echo "ran quitting cb\n"; die("quit in cb"); } error_reporting(0); echo "cb with exception\n"; try { $m1 = new Memcached(null, 'init_cb'); } catch (RuntimeException $e) { echo $e->getMessage(), "\n"; } echo "cb persistent with exception\n"; try { $m2 = new Memcached('foo', 'init_cb'); } catch (RuntimeException $e) { echo $e->getMessage(), "\n"; } echo "cb persistent dies\n"; try { $m3 = new Memcached('bar', 'init_cb_die'); } catch (RuntimeException $e) { echo $e->getMessage(), "\n"; } echo "not run\n"; --EXPECT-- cb with exception ran throwing cb bool(false) Cb exception cb persistent with exception ran throwing cb bool(true) Cb exception cb persistent dies ran quitting cb quit in cb --TEST-- Memcached fetch cas & set cas --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $m->delete('cas_test'); $cas_token = null; $m->set('cas_test', 10); $v = $m->get('cas_test', null, $cas_token); if (is_null($cas_token)) { echo "Null cas token for key: cas_test value: 10\n"; return; } $v = $m->cas($cas_token, 'cas_test', 11); if (!$v) { echo "Error setting key: cas_test value: 11 with CAS: $cas_token\n"; return; } $v = $m->get('cas_test'); if ($v !== 11) { echo "Wanted cas_test to be 11, value is: "; var_dump($v); } $v = $m->get('cas_test', null, 2); if ($v != 11) { echo "Failed to get the value with \$cas_token passed by value (2)\n"; return; } $v = $m->get('cas_test', null, null); if ($v != 11) { echo "Failed to get the value with \$cas_token passed by value (null)\n"; return; } $v = $m->get('cas_test', null, $data = array(2, 4)); if ($v != 11 || $data !== array(2, 4)) { echo "Failed to get the value with \$cas_token passed by value (\$data = array(2, 4))\n"; return; } echo "OK\n"; ?> --EXPECT-- OK--TEST-- Memcached multi fetch cas & set cas --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $data = array( 'cas_test_1' => 1, 'cas_test_2' => 2, ); foreach ($data as $key => $v) { $m->delete($key); } $cas_tokens = array(); $m->setMulti($data, 10); $actual = $m->getMulti(array_keys($data), $cas_tokens); foreach ($data as $key => $v) { if (is_null($cas_tokens[$key])) { echo "missing cas token(s)\n"; echo "data: "; var_dump($data); echo "actual data: "; var_dump($actual); echo "cas tokens: "; var_dump($cas_tokens); return; } $v = $m->cas($cas_tokens[$key], $key, 11); if (!$v) { echo "Error setting key: $key value: 11 with CAS: ", $cas_tokens[$key], "\n"; return; } $v = $m->get($key); if ($v !== 11) { echo "Wanted $key to be 11, value is: "; var_dump($v); return; } } if (array_keys($actual) !== array_keys($data)) { echo "missing value(s)\n"; echo "data :"; var_dump($data); echo "actual data: "; var_dump($actual); return; } $actual = $m->getMulti(array_keys($data), 2); if (array_keys($actual) !== array_keys($data)) { echo "Failed to getMulti \$cas_token passed by value (2)\n"; return; } $actual = $m->getMulti(array_keys($data), null); if (array_keys($actual) !== array_keys($data)) { echo "Failed to getMulti \$cas_token passed by value (null)\n"; return; } $actual = $m->getMulti(array_keys($data), $cas_tokens = array(2, 4)); if (array_keys($actual) !== array_keys($data) || $cas_tokens !== array(2, 4)) { echo "Failed to getMulti \$cas_token passed by value (\$cas_tokens = array(2, 4))\n"; return; } echo "OK\n"; ?> --EXPECT-- OK--TEST-- Check if persistent object is persistent --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $m1 = new Memcached('id1'); $m1->setOption(Memcached::OPT_PREFIX_KEY, "foo_"); var_dump($m1->isPersistent()); $m1 = new Memcached('id1'); var_dump($m1->isPersistent()); $m2 = new Memcached('id1'); var_dump($m2->isPersistent()); // this change affects $m1 $m2->setOption(Memcached::OPT_PREFIX_KEY, "bar_"); $m3 = new Memcached('id2'); var_dump($m3->isPersistent()); $m3 = new Memcached(); var_dump($m3->isPersistent()); // objects have the same resource, but they are not the same object. var_dump($m1 === $m2); var_dump($m1->getOption(Memcached::OPT_PREFIX_KEY)); --EXPECT-- bool(true) bool(true) bool(true) bool(true) bool(false) bool(false) string(4) "bar_" --TEST-- Check if persistent object is new or an old persistent one --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $m1 = new Memcached('id1'); $m1->setOption(Memcached::OPT_PREFIX_KEY, "foo_"); var_dump($m1->isPristine()); $m1 = new Memcached('id1'); var_dump($m1->isPristine()); $m2 = new Memcached('id1'); var_dump($m2->isPristine()); // this change affects $m1 $m2->setOption(Memcached::OPT_PREFIX_KEY, "bar_"); $m3 = new Memcached('id2'); var_dump($m3->isPristine()); $m3 = new Memcached(); var_dump($m3->isPristine()); --EXPECT-- bool(true) bool(false) bool(false) bool(true) bool(true) --TEST-- Test cloning --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $s = new stdClass(); $m = new Memcached(); $s = clone $s; $m = clone $m; echo "GOT HERE"; --EXPECTF-- Fatal error: Trying to clone an uncloneable object of class Memcached in %s on line %d--TEST-- Memcached compression test --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $data = file_get_contents(dirname(__FILE__) . '/testdata.res'); function get_compression($name) { switch (strtolower($name)) { case 'zlib': return Memcached::COMPRESSION_ZLIB; case 'fastlz': return Memcached::COMPRESSION_FASTLZ; default: echo "Strange compression type: $name\n"; return 0; } } function fetch_with_compression($m, $key, $value, $set_compression = '', $get_compression = '') { echo "set=[$set_compression] get=[$get_compression]\n"; if (!$set_compression) { $m->setOption(Memcached::OPT_COMPRESSION, false); } else { $m->setOption(Memcached::OPT_COMPRESSION, true); $m->setOption(Memcached::OPT_COMPRESSION_TYPE, get_compression($set_compression)); } $m->set($key, $value, 1800); if (!$get_compression) { $m->setOption(Memcached::OPT_COMPRESSION, true); } else { $m->setOption(Memcached::OPT_COMPRESSION, true); $m->setOption(Memcached::OPT_COMPRESSION_TYPE, get_compression($get_compression)); } $value_back = $m->get($key); var_dump($value === $value_back); } fetch_with_compression($m, 'hello1', $data, 'zlib', 'zlib'); fetch_with_compression($m, 'hello2', $data, 'zlib', 'fastlz'); fetch_with_compression($m, 'hello3', $data, 'fastlz', 'fastlz'); fetch_with_compression($m, 'hello4', $data, 'fastlz', 'zlib'); fetch_with_compression($m, 'hello5', $data, '', 'zlib'); fetch_with_compression($m, 'hello6', $data, '', 'fastlz'); fetch_with_compression($m, 'hello7', $data, 'zlib', ''); fetch_with_compression($m, 'hello8', $data, 'fastlz', ''); fetch_with_compression($m, 'hello9', $data, '', ''); ?> --EXPECT-- set=[zlib] get=[zlib] bool(true) set=[zlib] get=[fastlz] bool(true) set=[fastlz] get=[fastlz] bool(true) set=[fastlz] get=[zlib] bool(true) set=[] get=[zlib] bool(true) set=[] get=[fastlz] bool(true) set=[zlib] get=[] bool(true) set=[fastlz] get=[] bool(true) set=[] get=[] bool(true) --TEST-- Conf settings persist. --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m1 = memc_get_instance (array ( Memcached::OPT_PREFIX_KEY => 'php' ), 'id1'); $m1->set('foo', 'bar'); for ($i = 1000; $i > 0; $i--) { $m1 = new Memcached('id1'); $rv = $m1->get('foo'); if ($rv !== 'bar') { echo "Expected bar got:"; var_dump($rv); die(); } $prefix = $m1->getOption(Memcached::OPT_PREFIX_KEY); if ($prefix !== 'php') { echo "Expected prefix php got:"; var_dump($prefix); die(); } } echo "OK\n"; ?> --EXPECT-- OK--TEST-- Memcached constructor --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $m = new Memcached(); echo get_class($m); ?> --EXPECT-- Memcached --TEST-- persistent memcached connection --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $m1 = new Memcached('id1'); $m1->setOption(Memcached::OPT_PREFIX_KEY, 'php'); var_dump($m1->getOption(Memcached::OPT_PREFIX_KEY)); $m2 = new Memcached('id1'); var_dump($m1->getOption(Memcached::OPT_PREFIX_KEY)); $m3 = new Memcached(); var_dump($m3->getOption(Memcached::OPT_PREFIX_KEY)); ?> --EXPECT-- string(3) "php" string(3) "php" string(0) "" --TEST-- Memcached store & fetch type correctness --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $m->set('eisaleeoo', "foo"); $m->delete('eisaleeoo'); $v = $m->get('eisaleeoo'); if ($v !== Memcached::GET_ERROR_RETURN_VALUE) { echo "Wanted: "; var_dump(Memcached::GET_ERROR_RETURN_VALUE); echo "Got: "; var_dump($v); } echo "OK\n"; ?> --EXPECT-- OK--TEST-- Delete multi --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); function has_all_keys($keys, $array, $check_true = false) { foreach ($keys as $key) { if (!isset($array[$key])) return false; if ($check_true && $array[$key] !== true) return false; } return true; } $data = array( 'foo' => 'foo-data', 'bar' => 'bar-data', 'baz' => 'baz-data', 'lol' => 'lol-data', 'kek' => 'kek-data', ); $keys = array_keys($data); $null = null; $m->setMulti($data, 3600); /* Check that all keys were stored */ var_dump(has_all_keys($keys, $m->getMulti($keys))); /* Check that all keys get deleted */ $deleted = $m->deleteMulti($keys); var_dump(has_all_keys($keys, $deleted, true)); /* Try to get the deleted keys, should give empty array */ var_dump($m->getMulti($keys)); /* ---- same tests for byKey variants ---- */ $m->setMultiByKey("hi", $data, 3600); var_dump(has_all_keys($keys, $m->getMultiByKey('hi', $keys))); /* Check that all keys get deleted */ $deleted = $m->deleteMultiByKey('hi', $keys); var_dump(has_all_keys($keys, $deleted, true)); /* Try to get the deleted keys, should give empty array */ var_dump($m->getMultiByKey('hi', $keys)); /* Test deleting non-existent keys */ $keys = array(); $keys[] = "nothere"; $keys[] = "nothere2"; $retval = $m->deleteMulti($keys); foreach ($retval as $key => $value) { if ($value === Memcached::RES_NOTFOUND) { echo "$key NOT FOUND\n"; } } ?> --EXPECT-- bool(true) bool(true) array(0) { } bool(true) bool(true) array(0) { } nothere NOT FOUND nothere2 NOT FOUND--TEST-- Delete multi key types --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); function dump_types($v, $k) { echo gettype($v) . "\n"; } $keys = array(100, 'str'); array_walk($keys, 'dump_types'); $deleted = $m->deleteMulti($keys); array_walk($keys, 'dump_types'); ?> --EXPECT-- integer string integer string--TEST-- Memcached store, fetch & touch expired key --XFAIL-- https://code.google.com/p/memcached/issues/detail?id=275 --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!method_exists("memcached", "touch")) die ("skip memcached::touch is not available"); ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; function run_expiry_test ($m) { $key = uniqid ('will_expire_'); $set = $m->set($key, "foo", 2); $v = $m->get($key); if (!$set || $v != 'foo') { echo "Error setting key to \"foo\" with 2s expiry.\n"; return; } sleep(1); $res = $m->touch($key, 2); $v = $m->get($key); if(!$res || $v != 'foo') { echo "Error touching key for another 2s expiry.\n"; var_dump($res); var_dump($m->getResultMessage()); var_dump($v); return; } sleep(3); $v = $m->get($key); if ($v !== Memcached::GET_ERROR_RETURN_VALUE) { echo "Wanted:\n"; var_dump(Memcached::GET_ERROR_RETURN_VALUE); echo "from get of expired value. Got:\n"; var_dump($v); return; } echo "All OK" . PHP_EOL; } $m = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true )); echo '-- binary protocol' . PHP_EOL; run_expiry_test ($m); $m = memc_get_instance (); echo '-- text protocol' . PHP_EOL; run_expiry_test ($m); echo "DONE TEST\n"; ?> --EXPECT-- -- binary protocol All OK -- text protocol All OK DONE TEST --TEST-- Test flushing buffers --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (array ( Memcached::OPT_NO_BLOCK => 1, Memcached::OPT_BUFFER_WRITES => 1, )); $key = uniqid ('flush_key_'); var_dump ($m->set($key, 'test_val')); $m2 = memc_get_instance (); var_dump ($m2->get ($key)); var_dump ($m->flushBuffers ()); sleep (1); var_dump ($m2->get ($key)); echo "OK" . PHP_EOL; ?> --EXPECT-- bool(true) bool(false) bool(true) string(8) "test_val" OK--TEST-- Memcached getDelayed callback --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $data = array( 'foo' => 'foo-data', 'bar' => 'bar-data', 'baz' => 'baz-data', 'lol' => 'lol-data', 'kek' => 'kek-data', ); foreach ($data as $k => $v) { $m->set($k, $v, 3600); } function myfunc() { $datas = func_get_args(); if (isset($datas[1])) { unset($datas[1]['cas']); var_dump($datas[1]); } } $m->getDelayed(array_keys($data), false, 'myfunc'); ?> --EXPECT-- array(2) { ["key"]=> string(3) "foo" ["value"]=> string(8) "foo-data" } array(2) { ["key"]=> string(3) "bar" ["value"]=> string(8) "bar-data" } array(2) { ["key"]=> string(3) "baz" ["value"]=> string(8) "baz-data" } array(2) { ["key"]=> string(3) "lol" ["value"]=> string(8) "lol-data" } array(2) { ["key"]=> string(3) "kek" ["value"]=> string(8) "kek-data" } --TEST-- getServerList --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $servers = array ( 0 => array ( 'KEYHERE' => 'localhost', 11211, 3 ), ); $m = new memcached(); var_dump($m->getServerList()); $m->addServers($servers); var_dump($m->getServerList()); $m->addServers($servers); var_dump($m->getServerList()); $m = new memcached(); $m->addServer('127.0.0.1', 11211); var_dump($m->getServerList()); ?> --EXPECTF-- array(0) { } array(1) { [0]=> array(2) { ["host"]=> string(9) "localhost" ["port"]=> int(11211) } } array(2) { [0]=> array(2) { ["host"]=> string(9) "localhost" ["port"]=> int(11211) } [1]=> array(2) { ["host"]=> string(9) "localhost" ["port"]=> int(11211) } } array(1) { [0]=> array(2) { ["host"]=> string(9) "127.0.0.1" ["port"]=> int(11211) } } --TEST-- Test for Github issue 21 --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = new Memcached(); $newServers = array( array(MEMC_SERVER_HOST, MEMC_SERVER_PORT), ); $m->setOption(Memcached::OPT_BINARY_PROTOCOL, true); $m->addServers($newServers); $d = $m->get('foo'); $m->set('counter', 5); $n = $m->decrement('counter'); var_dump($n); $n = $m->decrement('counter', 10); var_dump($n); var_dump($m->get('counter')); $m->set('counter', 'abc'); $n = $m->increment('counter'); var_dump($n); ?> --EXPECT-- int(4) int(0) int(0) bool(false)--TEST-- Test for Github issue #77 --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (Memcached::LIBMEMCACHED_VERSION_HEX < 0x01000016) die ('skip too old libmemcached'); ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $mc = memc_get_instance (); $key = uniqid ("this_does_not_exist_"); $mc->touch($key, 5); var_dump ($mc->getResultCode() == Memcached::RES_NOTFOUND); $mc->set($key, 1, 5); $mc->set($key, 1, 5); var_dump ($mc->getResultCode() == Memcached::RES_SUCCESS); echo "OK\n"; ?> --EXPECT-- bool(true) bool(true) OK --TEST-- Test for GH #90 --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $memcached = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true )); // Create a key for use as a lock. If this key already exists, wait till it doesn't exist. { $key = 'LockKey'; $lockToken = mt_rand(0, pow(2, 32)); //Random value betwen 0 and 2^32 for ownership verification while (true) { $casToken = null; $data = $memcached->get($key, $casToken); if ($memcached->getResultCode() == Memcached::RES_NOTFOUND) { if ($memcached->add($key, $lockToken, 5)) { break; } } elseif ($data === false) { if ($memcached->cas($casToken, $key, $lockToken, 5)) { break; } } //Sleep 10 milliseconds usleep(10 * 1000); } } //Do something here that requires exclusive access to this key //Effectively delete our key lock. { $casToken = null; if ($lockToken == $memcached->get($key, $casToken)) { $memcached->cas($casToken, $key, false, 1); } } //Create 10 keys and then increment them. The first value returned will be wrong. { $keyList = array(); for ($i = 0; $i < 10; $i++) { $keyList[] = $i . '_' . uniqid ('count_value_'); } $valueList = array(); foreach ($keyList as $key) { $valueList[$key] = $memcached->increment($key, 1, 1); } var_dump ($valueList); } --EXPECTF-- array(10) { ["0_%s"]=> int(1) ["1_%s"]=> int(1) ["2_%s"]=> int(1) ["3_%s"]=> int(1) ["4_%s"]=> int(1) ["5_%s"]=> int(1) ["6_%s"]=> int(1) ["7_%s"]=> int(1) ["8_%s"]=> int(1) ["9_%s"]=> int(1) }--TEST-- Test that callback is invoked on new object --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; function my_func(Memcached $obj, $persistent_id = null) { $obj->addServer(MEMC_SERVER_HOST, MEMC_SERVER_PORT); } $m = new Memcached('hi', 'my_func'); var_dump($m->getServerList()); echo "OK\n"; --EXPECTF-- array(1) { [0]=> array(2) { ["host"]=> string(9) "127.0.0.1" ["port"]=> int(11211) } } OK --TEST-- Use callback initializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $arg = 'callback_arg'; $id = 'my_persistent_id'; function init_cb($m, $id) { var_dump(get_class($m)); var_dump($m->isPersistent()); var_dump($id); } function init_cb_fail($m, $id) { echo "configured, should not be called.\n"; } function init_cb_arg($m, $id, $arg) { var_dump($id); var_dump($arg); } function init_nopersist_cb($m, $id) { var_dump($m->isPersistent()); var_dump($id); } class Foo extends Memcached { function __construct($id = null) { parent::__construct($id, array($this, 'init')); } function init($obj, $id, $options) { var_dump($this->isPristine()); var_dump($this->isPersistent()); var_dump($id); } } error_reporting(0); echo "cb call\n"; $m1 = new Memcached('foo1', 'init_cb'); echo "cb not run\n"; $m1 = new Memcached('foo1', 'init_cb_fail'); echo "cb arg without arg\n"; $m1 = new Memcached('foo3', 'init_cb_arg'); echo $php_errormsg, "\n"; echo "cb arg not persistent\n"; $m1 = new Memcached(null, 'init_nopersist_cb'); echo "cb in object\n"; $m1 = new Foo(); echo "cb persistent in object\n"; $m1 = new Foo('baz'); echo "cb second persistent in object\n"; $m1 = new Foo('baz'); --EXPECT-- cb call string(9) "Memcached" bool(true) string(4) "foo1" cb not run cb arg without arg string(4) "foo3" NULL cb arg not persistent bool(false) NULL cb in object bool(true) bool(false) NULL cb persistent in object bool(true) bool(true) string(3) "baz" cb second persistent in object --TEST-- Test that callback is invoked on new object only once --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php function my_func(Memcached $obj, $persistent_id = null) { echo "Invoked for '{$persistent_id}'\n"; } $m1 = new Memcached('foobar', 'my_func'); $m2 = new Memcached('foobar', 'my_func'); echo "OK\n"; --EXPECT-- Invoked for 'foobar' OK --TEST-- Memcached local server test --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $key = 'foobarbazDEADC0DE'; $m->set($key, array( 'foo' => 'bar' ), 360); var_dump($m->get($key)); ?> --EXPECT-- array(1) { ["foo"]=> string(3) "bar" } --TEST-- Memcached GET_PRESERVE_ORDER flag in getMulti --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $m->addServer (MEMC_SERVER_HOST, MEMC_SERVER_PORT); $data = array( 'foo' => 'foo-data', 'bar' => 'bar-data', 'baz' => 'baz-data', 'lol' => 'lol-data', 'kek' => 'kek-data', ); //$m->setMulti($data, 3600); foreach ($data as $k => $v) { $m->set($k, $v, 3600); } $null = null; $keys = array_keys($data); $keys[] = 'zoo'; $got = $m->getMulti($keys, $null, Memcached::GET_PRESERVE_ORDER); foreach ($got as $k => $v) { echo "$k $v\n"; } ?> --EXPECT-- foo foo-data bar bar-data baz baz-data lol lol-data kek kek-data zoo --TEST-- Test that correct return value is returned --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $memcached = new Memcached(); $memcached->addServer('localhost', 5555); // Server should not exist $result = $memcached->get('foo_not_exists'); var_dump ($result === Memcached::GET_ERROR_RETURN_VALUE); $cas = 7; $result = $memcached->get('foo_not_exists', null, $cas); var_dump ($result === Memcached::GET_ERROR_RETURN_VALUE); echo "OK\n"; ?> --EXPECT-- bool(true) bool(true) OK --TEST-- Memcached options --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $m = new Memcached(); $m->setOption(Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP); var_dump($m->getOption(Memcached::OPT_COMPRESSION)); var_dump($m->getOption(Memcached::OPT_SERIALIZER)); var_dump($m->getOption(Memcached::OPT_SOCKET_SEND_SIZE)); $m->setOption(Memcached::OPT_PREFIX_KEY, "\x01"); var_dump($m->getOption(Memcached::OPT_HASH) == Memcached::HASH_DEFAULT); $m->setOption(Memcached::OPT_HASH, Memcached::HASH_MURMUR); var_dump($m->getOption(Memcached::OPT_HASH) == Memcached::HASH_MURMUR); $m->setOption(Memcached::OPT_COMPRESSION_TYPE, Memcached::COMPRESSION_ZLIB); var_dump($m->getOption(Memcached::OPT_COMPRESSION_TYPE) == Memcached::COMPRESSION_ZLIB); $m->setOption(Memcached::OPT_COMPRESSION_TYPE, Memcached::COMPRESSION_FASTLZ); var_dump($m->getOption(Memcached::OPT_COMPRESSION_TYPE) == Memcached::COMPRESSION_FASTLZ); var_dump($m->setOption(Memcached::OPT_COMPRESSION_TYPE, 0)); var_dump($m->getOption(Memcached::OPT_COMPRESSION_TYPE) == Memcached::COMPRESSION_FASTLZ); ?> --EXPECTF-- bool(true) int(1) Warning: Memcached::getOption(): no servers defined in %s on line %d NULL Warning: Memcached::setOption(): bad key provided in %s on line %d bool(true) bool(true) bool(true) bool(true) bool(false) bool(true) --TEST-- Wrong return values for binary protocol --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $client = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true )); $client->set('key1', 'value1'); echo "set result code: ".$client->getResultCode()."\n"; $value = $client->get('key1'); echo "got $value with result code: ".$client->getResultCode()."\n"; var_dump ($client->add('key2', 'value2')); echo "add result code: ".$client->getResultCode()."\n"; echo "OK\n"; ?> --EXPECT-- set result code: 0 got value1 with result code: 0 bool(true) add result code: 0 OK--TEST-- Memcached result codes. --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = new Memcached(); echo $m->getResultMessage(), "\n"; $m->addServer(MEMC_SERVER_HOST, MEMC_SERVER_PORT, 1); echo $m->getResultCode(), "\n"; echo $m->getResultMessage(), "\n"; $m->set('bar_foo', 10); echo $m->getResultMessage(), "\n"; $m->delete('bar_foo'); echo $m->getResultMessage(), "\n"; $m->delete('bar_foo'); echo $m->getResultCode(), "\n"; echo $m->getResultMessage(), "\n"; $m->getMulti(array('asdf_a', 'jkhjkhjkb', 'nbahsdgc')); echo $m->getResultMessage(), "\n"; $code = $m->getResultCode(); $m2 = new Memcached(); $m2->getMulti(array('asdf_a', 'jkhjkhjkb', 'nbahsdgc')); echo $m2->getResultCode(), "\n"; echo $m2->getResultMessage(), "\n"; $m2->addServer('127.0.0.1', 7312, 1); echo $m2->getResultCode(), "\n"; echo $m2->getResultMessage(), "\n"; $m2->delete('bar_foo'); echo $m2->getResultCode(), "\n"; echo $m2->getResultMessage(), "\n"; var_dump($m->getResultCode() == $code); $m = memc_get_instance (array (), 'test1'); $m2 = new Memcached('test1'); $m->delete('moikkamitakuuluu'); echo $m->getResultMessage(), "\n"; $m2->set('minapaasetannih', 10, 1); echo $m->getResultMessage(), "\n"; echo $m2->getResultMessage(), "\n"; $m->delete('bar_foo'); // clearly "NOT FOUND" $m->delete('bar_foo'); $res_m = $m->getResultMessage(); echo $res_m, "\n"; $m2->set('bar_foo', 10); echo $m->getResultMessage(), "\n"; echo $m2->getResultMessage(), "\n"; $m->delete('bar_foo'); echo $m->getResultMessage(), "\n"; ?> --EXPECTF-- SUCCESS %d SUCCESS SUCCESS SUCCESS %d NOT FOUND SUCCESS %d NO SERVERS DEFINED %d SUCCESS %d %rSYSTEM ERROR|WRITE FAILURE|CONNECTION FAILURE%r bool(true) NOT FOUND NOT FOUND SUCCESS NOT FOUND NOT FOUND SUCCESS SUCCESS --TEST-- Session bad configurations, prefix --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_SESSION) print "skip"; ?> --INI-- memcached.sess_locking = on memcached.sess_lock_wait = 150000 memcached.sess_prefix = "memc.sess.key." session.save_handler = memcached --FILE-- <?php include dirname (__FILE__) . '/config.inc'; ini_set ('session.save_path', MEMC_SERVER_HOST . ':' . MEMC_SERVER_PORT); error_reporting(0); function handler($errno, $errstr) { echo "$errstr\n"; } set_error_handler('handler', E_ALL); ini_set('memcached.sess_prefix', " \n"); session_start(); session_write_close(); --EXPECTF-- session_start(): bad memcached key prefix in memcached.sess_prefix --TEST-- Session bad configurations, lock wait time --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; print "skip skip until timeout in memcached set" ?> --INI-- memcached.sess_locking = on memcached.sess_lock_wait = -1 memcached.sess_prefix = "memc.sess.key." session.save_path="127.0.0.1:51312" session.save_handler = memcached --FILE-- <?php error_reporting(0); function handler($errno, $errstr) { echo "$errstr\n"; } set_error_handler('handler', E_ALL); session_start(); session_write_close(); echo "OK\n"; --EXPECTF-- OK--TEST-- Session bad configurations, prefix --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_SESSION) print "skip"; ?> --INI-- memcached.sess_locking = on memcached.sess_lock_wait = 150000 memcached.sess_prefix = "memc.sess.key." session.save_handler = memcached --FILE-- <?php include dirname (__FILE__) . '/config.inc'; ini_set ('session.save_path', MEMC_SERVER_HOST . ':' . MEMC_SERVER_PORT); error_reporting(0); function handler($errno, $errstr) { echo "$errstr\n"; } set_error_handler('handler', E_ALL); ini_set('memcached.sess_prefix', ' sdj jkhasd '); session_start(); session_write_close(); --EXPECTF-- session_start(): bad memcached key prefix in memcached.sess_prefix --TEST-- Session bad configurations, invalid save path (server list) --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_SESSION) print "skip"; ?> --INI-- memcached.sess_locking = on memcached.sess_lock_wait = 150000 memcached.sess_prefix = "memc.sess.key." session.save_handler = memcached --FILE-- <?php include dirname (__FILE__) . '/config.inc'; ini_set ('session.save_path', MEMC_SERVER_HOST . ':' . MEMC_SERVER_PORT); error_reporting(0); function handler($errno, $errstr) { echo "$errstr\n"; } set_error_handler('handler', E_ALL); ini_set('memcached.sess_prefix', 'memc.sess.key.'); ini_set('session.save_path', ''); session_start(); session_write_close(); --EXPECTF-- session_start(): failed to parse session.save_path --TEST-- Session basic open, write, destroy --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_SESSION) print "skip"; ?> --INI-- memcached.sess_locking = on memcached.sess_lock_wait = 150000 memcached.sess_prefix = "memc.sess.key." session.save_handler = memcached --FILE-- <?php include dirname (__FILE__) . '/config.inc'; ini_set ('session.save_path', MEMC_SERVER_HOST . ':' . MEMC_SERVER_PORT); error_reporting(0); session_start(); $_SESSION['foo'] = 1; session_write_close(); $_SESSION = NULL; var_dump($_SESSION); session_start(); var_dump($_SESSION); session_write_close(); session_start(); session_destroy(); session_start(); var_dump($_SESSION); session_write_close(); --EXPECT-- NULL array(1) { ["foo"]=> int(1) } array(0) { } --TEST-- set large data --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $key = 'foobarbazDEADC0DE'; $value = str_repeat("foo bar", 1024 * 1024); $m->set($key, $value, 360); var_dump($m->get($key) === $value); ?> --EXPECT-- bool(true) --TEST-- Set options using setOptions --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php $m = new Memcached(); /* existing options */ var_dump($m->setOptions(array( Memcached::OPT_PREFIX_KEY => 'a_prefix', Memcached::OPT_SERIALIZER => Memcached::SERIALIZER_PHP, Memcached::OPT_COMPRESSION => 0, Memcached::OPT_LIBKETAMA_COMPATIBLE => 1, Memcached::OPT_CONNECT_TIMEOUT => 5000, ))); var_dump($m->getOption(Memcached::OPT_PREFIX_KEY) == 'a_prefix'); var_dump($m->getOption(Memcached::OPT_SERIALIZER) == Memcached::SERIALIZER_PHP); var_dump($m->getOption(Memcached::OPT_COMPRESSION) == 0); var_dump($m->getOption(Memcached::OPT_LIBKETAMA_COMPATIBLE) == 1); echo "test invalid options\n"; var_dump($m->setOptions(array( "asdf" => 123 ))); var_dump($m->setOptions(array( -1 => 123 ))); --EXPECTF-- bool(true) bool(true) bool(true) bool(true) bool(true) test invalid options Warning: Memcached::setOptions(): invalid configuration option in %s on line %d bool(false) Warning: Memcached::setOptions(): error setting memcached option: %s in %s on line %d bool(false) --TEST-- Touch in binary mode --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (Memcached::LIBMEMCACHED_VERSION_HEX < 0x01000016) die ('skip too old libmemcached'); ?> --FILE-- <?php function resolve_to_constant ($code) { $refl = new ReflectionClass ('memcached'); $c = $refl->getConstants (); foreach ($c as $name => $value) { if (strpos ($name, 'RES_') === 0 && $value == $code) return $name; } } function status_print ($op, $mem, $expected) { $code = $mem->getResultcode(); if ($code == $expected) echo "${op} status code as expected" . PHP_EOL; else { $expected = resolve_to_constant ($expected); $code = resolve_to_constant ($code); echo "${op} status code mismatch, expected ${expected} but got ${code}" . PHP_EOL; } } include dirname (__FILE__) . '/config.inc'; $mem = memc_get_instance (array (Memcached::OPT_BINARY_PROTOCOL => true)); $key = uniqid ('touch_t_'); $mem->get($key); status_print ('get', $mem, Memcached::RES_NOTFOUND); $mem->set ($key, 1); status_print ('set', $mem, Memcached::RES_SUCCESS); $mem->get($key); status_print ('get', $mem, Memcached::RES_SUCCESS); $mem->touch ($key, 10); status_print ('touch', $mem, Memcached::RES_SUCCESS); $mem->get($key); status_print ('get', $mem, Memcached::RES_SUCCESS); $mem->get($key); status_print ('get', $mem, Memcached::RES_SUCCESS); echo "OK\n"; ?> --EXPECT-- get status code as expected set status code as expected get status code as expected touch status code as expected get status code as expected get status code as expected OK <?php class testclass { } function memc_types_test ($m, $options) { $data = array( array('boolean_true', true), array('boolean_false', false), array('string', "just a string"), array('string_empty', ""), array('integer_positive_integer', 10), array('integer_negative_integer', -10), array('integer_zero_integer', 0), array('float_positive1', 3.912131), array('float_positive2', 1.2131E+501), array('float_positive2', 1.2131E+52), array('float_negative', -42.123312), array('float_zero', 0.0), array('null', null), array('array_empty', array()), array('array', array(1,2,3,"foo")), array('object_array_empty', (object)array()), array('object_array', (object)array("a" => "1","b" => "2","c" => "3")), array('object_dummy', new testclass()), ); foreach ($data as $key => $value) { $m->delete($key); } foreach ($data as $types) { $key = $types [0]; $value = $types [1]; $m->set($key, $value); $actual = $m->get($key); if ($value !== $actual) { if (is_object($actual)) { if ($options['ignore_object_type']) { $value = (object) (array) $value; if ($value == $actual) continue; } if ($value == $actual && get_class($value) == get_class($actual)) continue; } echo "=== $types[0] ===\n"; echo "Expected: "; var_dump($types[1]); echo "Actual: "; var_dump($actual); } } $m->flush(); if (($actual = $m->get(uniqid ('random_key_'))) !== false) { echo "Expected: null"; echo "Actual: " . gettype($actual); } } function memc_types_test_multi ($m, $options) { $data = array( 'boolean_true' => true, 'boolean_false' => false, 'string' => "just a string", 'string_empty' => "", 'string_large' => str_repeat ('abcdef0123456789', 500), 'integer_positive_integer' => 10, 'integer_negative_integer' => -10, 'integer_zero_integer' => 0, 'float_positive1' => 3.912131, 'float_positive2' => 1.2131E+52, 'float_negative' => -42.123312, 'float_zero' => 0.0, 'null' => null, 'array_empty' => array(), 'array' => array(1,2,3,"foo"), 'object_array_empty' => (object)array(), 'object_array' => (object)array('a' => 1, 'b' => 2, 'c' => 3), 'object_dummy' => new testclass(), ); foreach ($data as $key => $value) { $m->delete($key); } $m->setMulti($data); $actual = $m->getMulti(array_keys($data)); foreach ($data as $key => $value) { if ($value !== $actual[$key]) { if (is_object($value)) { if ($options['ignore_object_type']) { $value = (object) (array) $value; if ($value == $actual[$key]) continue; } if ($value == $actual[$key] && get_class($value) == get_class($actual[$key])) continue; } echo "=== $key ===\n"; echo "Expected: "; var_dump($value); echo "Actual: "; var_dump($actual[$key]); } } } --TEST-- Memcached store & fetch type and value correctness using igbinary serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_IGBINARY) print "skip igbinary not enabled"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test', memc_create_combinations ('igbinary', Memcached::SERIALIZER_IGBINARY) ); ?> --EXPECT-- TEST DONE --TEST-- Memcached multi store & multi fetch type and value correctness using igbinary serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_IGBINARY) print "skip igbinary not enabled"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test_multi', memc_create_combinations ('igbinary', Memcached::SERIALIZER_IGBINARY) ); ?> --EXPECT-- TEST DONE --TEST-- Memcached store & fetch type and value correctness using JSON serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_JSON) print "skip json not enabled"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test', memc_create_combinations ('JSON', Memcached::SERIALIZER_JSON, true) ); ?> --EXPECT-- TEST DONE --TEST-- Memcached multi store & multi fetch type and value correctness using JSON serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_IGBINARY) print "skip json not enabled"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test_multi', memc_create_combinations ('JSON', Memcached::SERIALIZER_JSON, true) ); ?> --EXPECT-- TEST DONE --TEST-- Memcached store & fetch type and value correctness using msgpack serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_MSGPACK) print "skip msgpack not enabled"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test', memc_create_combinations ('msgpack', Memcached::SERIALIZER_MSGPACK, version_compare(phpversion("msgpack"), "0.5.5", "<=")) ); ?> --EXPECT-- TEST DONE --TEST-- Memcached multi store & fetch type and value correctness using msgpack serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; if (!Memcached::HAVE_MSGPACK) print "skip msgpack not enabled"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test_multi', memc_create_combinations ('msgpack', Memcached::SERIALIZER_MSGPACK, version_compare(phpversion("msgpack"), "0.5.5", "<=")) ); ?> --EXPECT-- TEST DONE --TEST-- Memcached store & fetch type and value correctness using PHP serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test', memc_create_combinations ('PHP', Memcached::SERIALIZER_PHP) ); ?> --EXPECT-- TEST DONE --TEST-- Memcached multi store & fetch type and value correctness using PHP serializer --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; include dirname (__FILE__) . '/types.inc'; memc_run_test ('memc_types_test_multi', memc_create_combinations ('PHP', Memcached::SERIALIZER_PHP) ); ?> --EXPECT-- TEST DONE --TEST-- Set with undefined key and value --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $key = 'foobarbazDEADC0DE'; $value = array('foo' => 'bar'); $rv = $m->set($no_key, $value, 360); var_dump($rv); $rv = $m->set($key, $no_value, 360); var_dump($rv); $rv = $m->set($no_key, $no_value, 360); var_dump($rv); $rv = $m->set($key, $value, $no_time); var_dump($rv); ?> --EXPECTF-- Notice: Undefined variable: no_key in %s bool(false) Notice: Undefined variable: no_value in %s bool(true) Notice: Undefined variable: no_key in %s Notice: Undefined variable: no_value in %s bool(false) Notice: Undefined variable: no_time in %s bool(true) --TEST-- Memcached virtual buckets --SKIPIF-- <?php if (!extension_loaded("memcached")) die ("skip"); if (!defined("Memcached::DISTRIBUTION_VIRTUAL_BUCKET")) die ("skip DISTRIBUTION_VIRTUAL_BUCKET not defined"); ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (array ( Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_VIRTUAL_BUCKET )); var_dump ($m->setBucket (array (1, 2, 3), null, 2)); var_dump ($m->setBucket (array (1,2,2), array (1,2,2), 2)); var_dump ($m->setBucket (array ('a', 'b', 'c'), null, 2)); var_dump ($m->setBucket (array (), null, 2)); var_dump ($m->setBucket (array (), array (), -1)); var_dump ($m->setBucket (null, array (), -1)); var_dump ($m->setBucket (array (-1), array (-1), 1)); echo "OK\n"; ?> --EXPECTF-- bool(true) bool(true) bool(true) Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d bool(false) Warning: Memcached::setBucket(): server map cannot be empty in %s on line %d bool(false) Warning: Memcached::setBucket() expects parameter 1 to be array, null given in %s on line %d NULL Warning: Memcached::setBucket(): the map must contain positive integers in %s on line %d bool(false) OK--TEST-- Memcached user flags --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php function check_flags ($flags, $expected_flags) { foreach ($expected_flags as $f) { if (($flags & $f) != $f) { echo "Flag {$f} is not set" . PHP_EOL; return; } } echo "Flags OK" . PHP_EOL; } define ('FLAG_1', 1); define ('FLAG_2', 2); define ('FLAG_4', 4); define ('FLAG_32', 32); define ('FLAG_64', 64); define ('FLAG_TOO_LARGE', pow(2, 16)); $x = 0; include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (array (Memcached::OPT_BINARY_PROTOCOL => true)); $key = uniqid ('udf_test_'); echo "stored with flags" . PHP_EOL; $m->set ($key, '1', 10, FLAG_1 | FLAG_4 | FLAG_64); $udf_flags = 0; $value = $m->get ($key, null, $x, $udf_flags); check_flags ($udf_flags, array (FLAG_1, FLAG_4, FLAG_64)); echo "stored without flags" . PHP_EOL; $m->set ($key, '1'); $value = $m->get ($key, null, $x, $udf_flags); var_dump ($udf_flags == 0); $m->set ($key, '1', 10, FLAG_TOO_LARGE); $m->setOption(Memcached::OPT_COMPRESSION, true); $m->setOption(Memcached::OPT_COMPRESSION_TYPE, Memcached::COMPRESSION_FASTLZ); $m->set ($key, str_repeat ("abcdef1234567890", 200), 10, FLAG_1 | FLAG_4 | FLAG_64); $udf_flags = 0; $value_back = $m->get($key, null, null, $udf_flags); check_flags ($udf_flags, array (FLAG_1, FLAG_4, FLAG_64)); echo "DONE TEST\n"; ?> --EXPECTF-- stored with flags Flags OK stored without flags bool(true) Warning: Memcached::set(): udf_flags will be limited to 65535 in %s on line %d Flags OK DONE TEST--TEST-- Test for Github issue #93 (double and long overflow) --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (array ( Memcached::OPT_COMPRESSION => false )); function testOverflow($m, $value) { $m->delete('overflow'); if (true !== $m->set('overflow', $value)) { echo "Error storing 'overflow' variable\n"; return false; } if (true !== $m->prepend('overflow', str_repeat('0', 128))) { echo "Error prepending key\n"; return false; } $v = @$m->get('overflow'); if ($v !== $value) { // At least it doesn't segfault, so we're happy for now // echo "Error receiving 'overflow' variable\n"; // return false; return true; } return true; } if (!testOverflow($m, 10)) { return; } if (!testOverflow($m, 9.09)) { return; } echo "OK\n"; ?> --EXPECT-- OK--TEST-- Memcached::add() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $m->delete('foo'); var_dump($m->add('foo', 1, 60)); var_dump($m->get('foo')); var_dump($m->add('foo', 2, 60)); var_dump($m->get('foo')); --EXPECT-- bool(true) int(1) bool(false) int(1) --TEST-- Memcached construct with bad arguments --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php error_reporting(0); $m = new Memcached((object)array()); echo $php_errormsg, "\n"; var_dump($m); class extended extends Memcached { public function __construct () { } } error_reporting(E_ALL); $extended = new extended (); var_dump ($extended->setOption (Memcached::OPT_BINARY_PROTOCOL, true)); echo "OK" . PHP_EOL; --EXPECTF-- Memcached::__construct() expects parameter %s NULL Warning: Memcached::setOption(): Memcached constructor was not called in %s on line %d NULL OK--TEST-- Memcached::append() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); error_reporting(0); $m->delete('foo'); $m->setOption(Memcached::OPT_COMPRESSION, true); var_dump($m->append('foo', 'a')); echo $php_errormsg, "\n"; $m->setOption(Memcached::OPT_COMPRESSION, false); $m->delete('foo'); var_dump($m->append('foo', 'a')); var_dump($m->get('foo')); $m->set('foo', 'a'); var_dump($m->append('foo', 'b')); var_dump($m->get('foo')); --EXPECTF-- NULL %s: cannot append/prepend with compression turned on bool(false) bool(false) bool(true) string(2) "ab" --TEST-- Memcached::prepend() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $m->delete('foo'); $m->setOption(Memcached::OPT_COMPRESSION, true); var_dump($m->prepend('foo', 'a')); $m->setOption(Memcached::OPT_COMPRESSION, false); $m->delete('foo'); var_dump($m->prepend('foo', 'a')); var_dump($m->get('foo')); $m->set('foo', 'a'); var_dump($m->prepend('foo', 'b')); var_dump($m->get('foo')); --EXPECTF-- Warning: Memcached::prepend(): cannot append/prepend with compression turned on in %s on line %d NULL bool(false) bool(false) bool(true) string(2) "ba" --TEST-- Memcached::replace() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $m->delete('foo'); var_dump($m->replace('foo', 'bar', 60)); var_dump($m->get('foo')); $m->set('foo', 'kef'); var_dump($m->replace('foo', 'bar', 60)); var_dump($m->get('foo')); --EXPECT-- bool(false) bool(false) bool(true) string(3) "bar" --TEST-- Memcached::getMulti() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); class Foo { function __toString() { return 'a-foo'; } } $data = array(); for ($i = 0; $i < 1000; $i++) { $data['key' . $i] = 'value' . $i; } $data['a-foo'] = 'a-last'; var_dump($m->setMulti($data)); $keys = array_keys($data); $keys['last'] = new Foo(); $v = $m->getMulti($keys); var_dump(is_array($v)); var_dump($m->getResultCode() == Memcached::RES_SUCCESS); if (is_array($v)) { foreach ($v as $key => $value) { if (!isset($data[$key]) or $value !== $data[$key]) { echo "mismatch \$data['$key'] = \n"; var_dump($data[$key]); var_dump($value); } } } else { echo "Result not an array\n"; } var_dump(is_object($keys['last'])); --EXPECT-- bool(true) bool(true) bool(true) bool(true) --TEST-- Memcached::setMulti() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $data['foo'] = 'bar'; $data[PHP_INT_MAX] = 'int-max'; $data[-PHP_INT_MAX] = 'int-min'; $data[-PHP_INT_MAX - 1] = 'int-min'; $data[0] = 'zero'; $data[123] = 'onetwothree'; $data[-123] = 'negonetwothree'; $keys = array_map('strval', array_keys($data)); echo "Data: "; var_dump($data); $m->deleteMulti($keys); echo "set keys: "; var_dump($m->setMulti($data, 10)); echo "get: "; $r = $m->getMulti($keys); var_dump($r); echo "Equal: "; var_dump($r === $data); --EXPECTF-- Data: array(%d) { ["foo"]=> string(3) "bar" [%i]=> string(7) "int-max" [%i]=> string(7) "int-min" [%i]=> string(7) "int-min" [0]=> string(4) "zero" [123]=> string(11) "onetwothree" [-123]=> string(14) "negonetwothree" } set keys: bool(true) get: array(%d) { ["foo"]=> string(3) "bar" [%i]=> string(7) "int-max" [%i]=> string(7) "int-min" [%i]=> string(7) "int-min" [0]=> string(4) "zero" [123]=> string(11) "onetwothree" [-123]=> string(14) "negonetwothree" } Equal: bool(true) --TEST-- Memcached::get() with cache callback --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $runs = 0; $first_key = uniqid ('cache_test_'); $second_key = uniqid ('cache_test_'); $third_key = uniqid ('cache_test_'); var_dump ( $m->get ($first_key, function (Memcached $memc, $key, &$value, &$expiration) { $value = "hello"; $expiration = 10; return true; }) ); var_dump ($m->get ($first_key)); var_dump ( $m->get ($second_key, function (Memcached $memc, $key, &$value, &$expiration) { $value = "hello"; $expiration = 10; return false; }) ); var_dump ($m->get ($second_key)); try { $m->get ($third_key, function (Memcached $memc, $key, &$value, &$expiration) { $value = "hello"; $expiration = 10; throw new Exception ('this is a test'); return true; }); } catch (Exception $e) { echo 'Got exception' . PHP_EOL; } var_dump ($m->get ($third_key)); echo "OK" . PHP_EOL; --EXPECT-- string(5) "hello" string(5) "hello" bool(false) bool(false) Got exception bool(false) OK--TEST-- Memcached::increment() Memcached::decrement() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); echo "Not there\n"; $m->delete('foo'); var_dump($m->increment('foo', 1)); var_dump($m->decrement('foo', 1)); var_dump($m->get('foo')); echo "Normal\n"; $m->set('foo', 1); var_dump($m->get('foo')); $m->increment('foo'); var_dump($m->get('foo')); $m->increment('foo', 2); var_dump($m->get('foo')); $m->decrement('foo'); var_dump($m->get('foo')); $m->decrement('foo', 2); var_dump($m->get('foo')); error_reporting(0); echo "Invalid offset\n"; $m->increment('foo', -1); echo $php_errormsg, "\n"; var_dump($m->get('foo')); $m->decrement('foo', -1); echo $php_errormsg, "\n"; var_dump($m->get('foo')); --EXPECT-- Not there bool(false) bool(false) bool(false) Normal int(1) int(2) int(4) int(3) int(1) Invalid offset Memcached::increment(): offset has to be > 0 int(1) Memcached::decrement(): offset has to be > 0 int(1) --TEST-- Memcached::increment() Memcached::decrement() with initial support --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true )); $m->delete('foo'); var_dump($m->increment('foo', 1, 1)); var_dump($m->increment('foo', 0)); $m->delete('foo'); var_dump($m->increment('foo', 1, 1)); var_dump($m->increment('foo', 1, 1)); var_dump($m->increment('foo', 1, 1)); var_dump($m->decrement('foo', 1, 1)); var_dump($m->decrement('foo', 0)); $m->delete('foo'); $m->deleteByKey('foo', 'foo'); var_dump($m->incrementByKey('foo', 'foo', 1, 1)); var_dump($m->incrementByKey('foo', 'foo', 0)); $m->deleteByKey('foo', 'foo'); var_dump($m->incrementByKey('foo', 'foo', 1, 1)); var_dump($m->incrementByKey('foo', 'foo', 1, 1)); var_dump($m->incrementByKey('foo', 'foo', 1, 1)); var_dump($m->decrementByKey('foo', 'foo', 1, 1)); var_dump($m->decrementByKey('foo', 'foo', 0)); $m->deleteByKey('foo', 'foo'); --EXPECT-- int(1) int(1) int(1) int(2) int(3) int(2) int(2) int(1) int(1) int(1) int(2) int(3) int(2) int(2) --TEST-- Memcached::increment() Memcached::decrement() with invalid key --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); var_dump($m->increment('', 1)); var_dump($m->decrement('', 1)); --EXPECT-- bool(false) bool(false) --TEST-- Memcached::incrementByKey() Memcached::decrementByKey() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); echo "Not there\n"; $m->delete('foo'); var_dump($m->incrementByKey('foo', 'foo', 1)); var_dump($m->decrementByKey('foo', 'foo', 1)); var_dump($m->get('foo')); echo "Normal\n"; $m->set('foo', 1); var_dump($m->get('foo')); $m->incrementByKey('foo', 'foo'); var_dump($m->get('foo')); $m->incrementByKey('foo', 'foo', 2); var_dump($m->get('foo')); $m->decrementByKey('foo', 'foo'); var_dump($m->get('foo')); $m->decrementByKey('foo', 'foo', 2); var_dump($m->get('foo')); error_reporting(0); echo "Invalid offset\n"; $m->incrementByKey('foo', 'foo', -1); echo $php_errormsg, "\n"; var_dump($m->get('foo')); $m->decrementByKey('foo', 'foo', -1); echo $php_errormsg, "\n"; var_dump($m->get('foo')); --EXPECT-- Not there bool(false) bool(false) bool(false) Normal int(1) int(2) int(4) int(3) int(1) Invalid offset Memcached::incrementByKey(): offset has to be > 0 int(1) Memcached::decrementByKey(): offset has to be > 0 int(1) --TEST-- Get version --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_instance (); $m->setOption(500, 23423); var_dump ($m->getResultCode ()); echo "OK" . PHP_EOL; ?> --EXPECTF-- Warning: Memcached::setOption(): error setting memcached option: INVALID ARGUMENTS in %s on line %d int(38) OK--TEST-- Test different kind of keys --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $binary = memc_get_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true, )); $ascii = memc_get_instance (); var_dump ($binary->set ('binary key with spaces', 'this is a test')); var_dump ($binary->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); var_dump ($ascii->set ('ascii key with spaces', 'this is a test')); var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); var_dump ($ascii->set (str_repeat ('1234567890', 512), 'this is a test')); var_dump ($ascii->getResultCode () == Memcached::RES_BAD_KEY_PROVIDED); echo "OK" . PHP_EOL; --EXPECT-- bool(false) bool(true) bool(false) bool(true) bool(false) bool(true) OK Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut malesuada purus vel diam congue condimentum. Vivamus diam erat, commodo eget lacinia venenatis, tempor at nulla. Praesent dapibus aliquam lectus. Aliquam erat volutpat. Praesent congue elementum ipsum, eu bibendum nisi ullamcorper nec. Aenean mattis metus quis libero hendrerit blandit accumsan nisi lacinia. Aenean varius sollicitudin nisl, tempor condimentum turpis congue id. Nunc vulputate purus non nunc dignissim tincidunt. Curabitur adipiscing est in ligula interdum a ultrices nibh ultricies. Aliquam a dapibus lectus. Ut eleifend, dui nec aliquam lobortis, quam est luctus lectus, nec hendrerit augue quam ac odio. Nam condimentum, ligula et rhoncus vulputate, lorem urna adipiscing mauris, non mollis velit arcu non urna. Donec sodales ultrices risus, sed hendrerit libero fringilla vitae. Maecenas magna nisl, vehicula scelerisque hendrerit id, fermentum at lorem. Curabitur eu nisl tincidunt arcu venenatis ultricies. Vivamus velit lorem, hendrerit non imperdiet sit amet, eleifend nec mauris. Donec eget condimentum purus. Cras sed lorem sagittis augue faucibus tincidunt. Pellentesque vitae lorem ac orci dignissim tempor. Maecenas diam elit, pulvinar sed gravida rutrum, ultrices vel mauris. In adipiscing placerat eros imperdiet euismod. Nunc quis ante non lectus dictum porta. Nullam orci felis, tristique ut posuere at, tempor sed eros. Pellentesque accumsan posuere magna eu condimentum. Phasellus adipiscing cursus fringilla. Donec diam ipsum, pharetra quis tempus sit amet, mollis in dolor. Pellentesque volutpat vestibulum nulla quis ultricies. Pellentesque scelerisque erat eu nulla ullamcorper tincidunt. Nullam commodo lobortis lacus, ac scelerisque diam dignissim eget. Cras eu metus sed tellus accumsan rutrum id ac nunc. Duis rutrum ipsum quis sapien dictum tincidunt. Duis gravida augue et sapien laoreet at pretium eros vulputate. Duis pulvinar lectus quis libero ultricies consequat a id felis. Morbi imperdiet venenatis ipsum eget ultricies. Vivamus fermentum urna sed massa tristique euismod. Pellentesque sed justo ut mi tincidunt luctus semper et elit. In hac habitasse platea dictumst. Praesent suscipit, elit quis fermentum luctus, magna dolor volutpat tellus, nec blandit nulla metus a lacus. Duis gravida, ipsum eu posuere congue, massa augue semper sapien, vel bibendum mi odio nec lectus. Vivamus lacinia urna vitae justo tincidunt dictum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc sed libero metus, in imperdiet nibh. Integer sapien mauris, pretium sed sodales at, blandit in lacus. Etiam a nisl semper enim pellentesque posuere vitae quis lectus. Etiam ut libero at tortor molestie feugiat non ut ligula. Suspendisse fermentum ipsum vel mauris aliquet convallis. Fusce velit turpis, sollicitudin sit amet luctus id, porta nec ipsum. Fusce consequat, risus vel congue sollicitudin, orci sem auctor nisl, eu consectetur neque tortor id neque. Nunc in odio velit. Duis vitae lacus elit, eu fringilla nulla. Phasellus non mi tellus, volutpat commodo lorem. Pellentesque vel ligula enim. Morbi suscipit, orci sed gravida convallis, nunc leo eleifend lacus, quis elementum dui mi nec risus. Mauris faucibus arcu scelerisque tellus semper dictum. Suspendisse vel posuere turpis. Curabitur ipsum ligula, auctor ut dignissim vel, scelerisque vel mauris. Vivamus est dolor, bibendum ac vestibulum a, congue id felis. Mauris rutrum pharetra tempus. Sed ornare congue purus, id adipiscing mi tincidunt at. Maecenas blandit, lorem in malesuada adipiscing, dolor arcu suscipit magna, ut mollis leo nibh non orci. Sed non massa ipsum, et lobortis tortor. Aliquam et egestas velit. Mauris ac sem eget elit imperdiet faucibus at non turpis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent vel dui vitae enim imperdiet sodales. Maecenas a tristique mauris. Praesent consectetur risus sit amet lacus dictum ut egestas nibh lacinia. Mauris quam augue, fringilla ac pretium a, consectetur sed risus. Sed ullamcorper eleifend dolor, id tempor arcu sodales at. Vestibulum eget sagittis libero. Cras ornare dui ac ante pretium fringilla. Morbi eu tincidunt felis. Vivamus ultrices diam in eros elementum luctus. Aenean eu neque nibh. Mauris neque est, euismod ut rutrum nec, vehicula at magna. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Fusce sit amet neque ac justo pretium laoreet. Duis non sapien ut eros tristique porttitor vel et lacus. Donec molestie nunc malesuada ante porta ac pretium magna dictum. Nam dolor orci, lacinia egestas ornare eget, viverra et mi. Vivamus convallis lobortis dui, ac sagittis urna mattis sit amet. Duis interdum, est sed dignissim blandit, metus elit scelerisque mauris, sit amet egestas velit arcu quis nunc. <?php if (file_exists (dirname (__FILE__) . '/config.inc.local')) { include dirname (__FILE__) . '/config.inc.local'; } else { define ("MEMC_SERVER_HOST", "127.0.0.1"); define ("MEMC_SERVER_PORT", 11211); //define ("MEMC_SASL_SERVER_HOST", "127.0.0.1"); //define ("MEMC_SASL_SERVER_PORT", 11212); //define ('MEMC_SASL_USER', 'memcached'); //define ('MEMC_SASL_PASS', 'test'); } function memc_create_instance ($host, $port, array $opts = array (), $persistent_id = null) { $memcached = new Memcached($persistent_id); if ($memcached->setOptions ($opts) == false) echo "Failed to set options" . PHP_EOL; $memcached->addServer($host, $port); $memcached->flush (); return $memcached; } function memc_get_instance (array $opts = array (), $persistent_id = null) { return memc_create_instance(MEMC_SERVER_HOST, MEMC_SERVER_PORT, $opts, $persistent_id); } function memc_get_sasl_instance (array $opts = array (), $persistent_id = null) { return memc_create_instance(MEMC_SASL_SERVER_HOST, MEMC_SASL_SERVER_PORT, $opts, $persistent_id); } function memc_run_test ($test_function, $options = array ()) { foreach ($options as $option_set) { $memc = memc_get_instance ($option_set ['options']); $test_function ($memc, $option_set); } echo "TEST DONE" . PHP_EOL; } function memc_create_combinations ($name, $serializer, $ignore_object_type = false) { return array ( array ( 'title' => "$name serializer, ascii protocol", 'options' => array ( Memcached::OPT_SERIALIZER => $serializer ), 'ignore_object_type' => $ignore_object_type ), array ( 'title' => "$name serializer, binary protocol", 'options' => array ( Memcached::OPT_BINARY_PROTOCOL => true, Memcached::OPT_SERIALIZER => $serializer ), 'ignore_object_type' => $ignore_object_type ), ); } --TEST-- Test SASL authentication --SKIPIF-- <?php include dirname (__FILE__) . '/config.inc'; if (!extension_loaded("memcached")) die ("skip"); if (!Memcached::HAVE_SASL) die ("skip no SASL support enabled"); if (!defined ('MEMC_SASL_USER') || empty (MEMC_SASL_USER)) die ("skip MEMC_SASL_USER is not set"); ?> --INI-- memcached.use_sasl = on --FILE-- <?php include dirname (__FILE__) . '/config.inc'; $m = memc_get_sasl_instance (array ( Memcached::OPT_BINARY_PROTOCOL => true )); var_dump ($m->setSaslAuthData (MEMC_SASL_USER, MEMC_SASL_PASS)); $key = uniqid ('sasl_test_'); var_dump ($m->set ($key, 'set using sasl')); var_dump ($m->get ($key)); echo "OK" . PHP_EOL; ?> --EXPECT-- bool(true) bool(true) string(14) "set using sasl" OK --TEST-- Memcached::getServerByKey() --SKIPIF-- <?php if (!extension_loaded("memcached")) print "skip"; ?> --FILE-- <?php error_reporting(0); $m = new Memcached(); var_dump($m->getServerByKey("a")); $m->addServer('localhost', 11211, 1); $m->addServer('localhost', 11212, 1); $m->addServer('localhost', 11213, 1); $m->addServer('localhost', 11214, 1); $m->addServer('localhost', 11215, 1); var_dump($m->getServerByKey("")); echo $m->getResultMessage(), "\n"; var_dump($m->getServerByKey("a")); var_dump($m->getServerByKey("b")); var_dump($m->getServerByKey("c")); var_dump($m->getServerByKey("d")); --EXPECTF-- bool(false) bool(false) A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE array(%d) { ["host"]=> string(%d) "%s" ["port"]=> int(%d) ["weight"]=> int(0) } array(%d) { ["host"]=> string(%d) "%s" ["port"]=> int(%d) ["weight"]=> int(0) } array(%d) { ["host"]=> string(%d) "%s" ["port"]=> int(%d) ["weight"]=> int(0) } array(%d) { ["host"]=> string(%d) "%s" ["port"]=> int(%d) ["weight"]=> int(0) } Build Status ------------ [![Build Status](https://travis-ci.org/php-memcached-dev/php-memcached.png?branch=master)](https://travis-ci.org/php-memcached-dev/php-memcached) Description ----------- This extension uses libmemcached library to provide API for communicating with memcached servers. memcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load. Building -------- $ phpize $ ./configure $ make $ make test Resources --------- * [libmemcached](http://libmemcached.org/libMemcached.html) * [memcached](http://memcached.org/) * [igbinary](https://github.com/phadej/igbinary/) memcached Andrei Zmievski, Oleg Grenrus (igbinary support) -------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from <http://www.php.net/software/>". 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 <http://www.php.net>. PHP includes the Zend Engine, freely available at <http://www.zend.com>. memcached extension changelog Version 2.2.0b1 --------------- * Reinstate support for libememcached 0.x series * Added SASL support to session handler * Added Memcached::flushBuffers as per GH #78 * Fixes GH #54: Fixed UDP server adding with newer libmemcached * Fixed PHP bug #65334: (Segfault if uncompress value failed) * Fixes GH #14: get with cas token fails to fetch all results * Fixes GH #68: memcached 2.1.0 requires libmemcached 1.0.10 * Fixes GH #69: compiling on CentOS 6.4 with libmemcached 1.0.17 * Merged PR #91: memcached.sess_lock_wait and memcached.sess_lock_max_wait * Added session handler settings: - memcached.sess_number_of_replicas - memcached.sess_randomize_replica_read - memcached.sess_remove_failed - memcached.sess_connect_timeout * Added support for memcached protocol handlers * Added Memcached::setBucket for virtual bucket support * Added support for msgpack serialization * Memcached::setSaslAuthData returns correct status on success * Added support for user-defined flags in set and get operations Version 2.1.0 ------------- * Drop support for libmemcached 0.x series, now 1.0.x is required * Add support for virtual bucket distribution * Fix compilation against PHP 5.2 Version 2.0.1 ------------- * Fix embedded version number to be not -dev Version 2.0.0 ------------- * Add touch() and touchByKey() methods * Add resetServerList() and quit() methods * Support binary protocol in sessions * Make it work with libmemcached up to 1.0.4 * Test against PHP 5.4.0 Version 2.0.0b2 --------------- * Add OPT_REMOVE_FAILED_SERVERS option. * Make it work with libmemcached up to 0.49. * Fix a case where invalid session ID could lock the script. * Improve session support: - Add support for libmemcached config string - Add persistence support via PERSISTENT=persistent_id prefix of the save_path * Add 3rd parameter to the __construct() that allows specification of libmemcached configuration string * Fix a possible crash in __construct() when using persistent connections * Add work-around a bug in libmemcached < 0.50 that causes truncation of last character of server key prefix * When using multiple servers implement transparent fail-over * Fix php_memc_cas_impl() implementation when server_key is not being used * Add support for incrementByKey() and decrementByKey() * Make increment/decrement initialize value when it is not available (when using binary protocol) Version 2.0.0b1 --------------- * Add fastlz library that provides better/faster payload compression * Add configure switch to enable/disable JSON serialization support * Add getAllKeys() method * Add deleteMulti() and deleteMultiByKey() methods * Add isPristine() and isPersistent() methods * Add setOptions() method to set multiple options at once * Add SERIALIZER_JSON_ARRAY type that decodes JSON payloads as arrays instead of objects * Add support for Unix domain socket connections * Add memcached.compression_threshold INI setting * Add memcached.compression_factor INI setting * Add memcached.compression_type INI setting * Implement a few speed optimizations * Many bug fixes and memory leaks plugged * Add several more tests * Add constants for libmemcached 0.37+: - Memcached::OPT_NUMBER_OF_REPLICAS - Memcached::OPT_RANDOMIZE_REPLICA_READ * Add 'on_new' callback to constructor * Add SASL support Version 1.0.1 ------------- * Fix JSON API handling to account for PHP 5.2/5.3 version differences. * Add memcached.sess_locking, memcached.sess_lock_wait, and memcached.sess_prefix INI entries. * Add OPT_AUTO_EJECT_HOSTS option. Version 1.0.0 ------------- * First stable release. * Add getResultMessage() method. * Fix OPT_RECV_TIMEOUT definition. * Initialize Session lock wait to max execution time (if max execution time is unlimited, default to 30 seconds). Version 0.2.0 ------------- * Add JSON serializer support, requires PHP 5.2.10+. * Add HAVE_JSON and HAVE_IGBINARY class constants that indicate whether the respective serializers are available. * Add 'flags' parameter to getMulti() and getMultiByKey(). * Add GET_PRESERVE_ORDER class constant that can be used with abovementioned flags parameter to make the order of the keys in the response match the request. * Fix an issue with retrieving 0-length payloads (FALSE boolean value). * Refactor the way payload types are stored in memcached flags to optimize the structure and allow for future expansion. WARNING! You have to flush the cache when upgrading to this version. * Add several tests. Version 0.1.5 ------------- * Implement getVersion(). * Add support for preserving boolean value types. * Fix crash when child class does not call constructor. * Fix bug #16084 (Crash when addServers is called with an associative array). * ZTS compilation fixes. Version 0.1.4 ------------- * Fix compilation against PHP 5.3. * Add support for 'igbinary' serializer (Oleg Grenrus) Version 0.1.3 ------------- * Bludgeon bug #15896 (Memcached setMulti error) into submission. Version 0.1.2 ------------- * Fix bug #15896 (Memcached setMulti error). * Check for empty key in getServerByKey(). * Allow passing 'null' for callbacks. * get() with cas token fetching wasn't erroring out properly. * Rename certain parameters in the API to be more clear. * Allow only strings as the append/prepend value. * Remove expiration parameter from append/prepend. Version 0.1.1 ------------- * Add OPT_LIBKETAMA_COMPATIBLE option. * Implement addServers() method. * Swap internal compressed and serialized flags to be compatible with other clients. Version 0.1.0 ------------- * Initial release <?php /** * Memcached class. */ class Memcached { /** * Libmemcached behavior options. */ const OPT_HASH; const OPT_HASH_DEFAULT; const HASH_MD5; const HASH_CRC; const HASH_FNV1_64; const HASH_FNV1A_64; const HASH_FNV1_32; const HASH_FNV1A_32; const HASH_HSIEH; const HASH_MURMUR; const OPT_DISTRIBUTION; const DISTRIBUTION_MODULA; const DISTRIBUTION_CONSISTENT; const DISTRIBUTION_VIRTUAL_BUCKET; const LIBKETAMA_COMPATIBLE; const OPT_BUFFER_REQUESTS; const OPT_BINARY_PROTOCOL; const OPT_NO_BLOCK; const OPT_TCP_NODELAY; const OPT_SOCKET_SEND_SIZE; const OPT_SOCKET_RECV_SIZE; const OPT_CONNECT_TIMEOUT; const OPT_RETRY_TIMEOUT; const OPT_DEAD_TIMEOUT; const OPT_SND_TIMEOUT; const OPT_RCV_TIMEOUT; const OPT_POLL_TIMEOUT; const OPT_SERVER_FAILURE_LIMIT; const OPT_SERVER_TIMEOUT_LIMIT; const OPT_CACHE_LOOKUPS; const OPT_AUTO_EJECT_HOSTS; const OPT_NUMBER_OF_REPLICAS; const OPT_NOREPLY; const OPT_VERIFY_KEY; const OPT_RANDOMIZE_REPLICA_READS; /** * Class parameters */ const HAVE_JSON; const HAVE_IGBINARY; /** * Class options. */ const OPT_COMPRESSION; const OPT_COMPRESSION_TYPE; const OPT_PREFIX_KEY; /** * Serializer constants */ const SERIALIZER_PHP; const SERIALIZER_IGBINARY; const SERIALIZER_JSON; const SERIALIZER_JSON_ARRAY; /** * Compression types */ const COMPRESSION_TYPE_FASTLZ; const COMPRESSION_TYPE_ZLIB; /** * Flags */ const GET_PRESERVE_ORDER; /** * Return values */ const GET_ERROR_RETURN_VALUE; const RES_PAYLOAD_FAILURE; const RES_SUCCESS; const RES_FAILURE; const RES_HOST_LOOKUP_FAILURE; const RES_UNKNOWN_READ_FAILURE; const RES_PROTOCOL_ERROR; const RES_CLIENT_ERROR; const RES_SERVER_ERROR; const RES_WRITE_FAILURE; const RES_DATA_EXISTS; const RES_NOTSTORED; const RES_NOTFOUND; const RES_PARTIAL_READ; const RES_SOME_ERRORS; const RES_NO_SERVERS; const RES_END; const RES_ERRNO; const RES_BUFFERED; const RES_TIMEOUT; const RES_BAD_KEY_PROVIDED; const RES_STORED; const RES_DELETED; const RES_STAT; const RES_ITEM; const RES_NOT_SUPPORTED; const RES_FETCH_NOTFINISHED; const RES_SERVER_MARKED_DEAD; const RES_UNKNOWN_STAT_KEY; const RES_INVALID_HOST_PROTOCOL; const RES_MEMORY_ALLOCATION_FAILURE; const RES_CONNECTION_SOCKET_CREATE_FAILURE; public function __construct( $persistent_id = '', $on_new_object_cb = null ) {} public function get( $key, $cache_cb = null, &$cas_token = null, &$udf_flags = null ) {} public function getByKey( $server_key, $key, $cache_cb = null, &$cas_token = null, &$udf_flags = null ) {} public function getMulti( array $keys, &$cas_tokens = null, $flags = 0, &$udf_flags = null ) {} public function getMultiByKey( $server_key, array $keys, &$cas_tokens = null, $flags = 0, &$udf_flags = null ) {} public function getDelayed( array $keys, $with_cas = null, $value_cb = null ) {} public function getDelayedByKey( $server_key, array $keys, $with_cas = null, $value_cb = null ) {} public function fetch( ) {} public function fetchAll( ) {} public function set( $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function touch( $key, $expiration = 0 ) {} public function touchbyKey( $key, $expiration = 0 ) {} public function setByKey( $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function setMulti( array $items, $expiration = 0, $udf_flags = 0 ) {} public function setMultiByKey( $server_key, array $items, $expiration = 0, $udf_flags = 0 ) {} public function cas( $token, $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function casByKey( $token, $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function add( $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function addByKey( $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function append( $key, $value ) {} public function appendByKey( $server_key, $key, $value ) {} public function prepend( $key, $value ) {} public function prependByKey( $server_key, $key, $value ) {} public function replace( $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function replaceByKey( $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function delete( $key, $time = 0 ) {} public function deleteByKey( $server_key, $key, $time = 0 ) {} public function deleteMulti( array $keys, $expiration = 0 ) {} public function deleteMultiByKey( $server_key, array $keys, $expiration = 0 ) {} public function increment( $key, $offset = 1, $initial_value = 0, $expiry = 0) {} public function decrement( $key, $offset = 1, $initial_value = 0, $expiry = 0) {} public function getOption( $option ) {} public function setOption( $option, $value ) {} public function setOptions( array $options ) {} public function setBucket( array $host_map, array $forward_map, $replicas ) {} public function addServer( $host, $port, $weight = 0 ) {} public function addServers( array $servers ) {} public function getServerList( ) {} public function getServerByKey( $server_key ) {} public function getLastErrorMessage( ) {} public function getLastErrorCode( ) {} public function getLastErrorErrno( ) {} public function getLastDisconnectedServer( ) {} public function flush( $delay = 0 ) {} public function getStats( ) {} public function getVersion( ) {} public function getResultCode( ) {} public function getResultMessage( ) {} public function isPersistent( ) {} public function isPristine( ) {} public function setSaslAuthData( $username, $password ) {} } class MemcachedException extends Exception { function __construct( $errmsg = "", $errcode = 0 ) {} } [memcached] ; Use session locking ; valid values: On, Off ; the default is On memcached.sess_locking = On ; Session spin lock retry wait time in microseconds. ; Be carefull when setting this value. ; Valid values are integers, where 0 is interpreted as ; the default value. Negative values result in a reduces ; locking to a try lock. ; the default is 150000 memcached.sess_lock_wait = 150000 ; The maximum time, in seconds, to wait for a session lock ; before timing out. ; Setting to 0 results in default behavior, which is to ; use max_execution_time. memcached.sess_lock_max_wait = 0; ; The time, in seconds, before a lock should release itself. ; Setting to 0 results in the default behaviour, which is to ; use the memcached.sess_lock_max_wait setting. If that is ; also 0, max_execution_time will be used. memcached.sess_lock_expire = 0; ; memcached session key prefix ; valid values are strings less than 219 bytes long ; the default value is "memc.sess.key." memcached.sess_prefix = "memc.sess.key." ; memcached session consistent hash mode ; if set to On, consistent hashing (libketama) is used ; for session handling. ; When consistent hashing is used, one can add or remove cache ; node(s) without messing up too much with existing keys ; default is Off memcached.sess_consistent_hash = Off ; Allow failed memcached server to automatically be removed memcached.sess_remove_failed = 1 ; Write data to a number of additional memcached servers ; This is "poor man's HA" as libmemcached calls it. ; If this value is positive and sess_remove_failed is enabled ; when a memcached server fails the session will continue to be available ; from a replica. However, if the failed memcache server ; becomes available again it will read the session from there ; which could have old data or no data at all memcached.sess_number_of_replicas = 0 ; memcached session binary mode ; libmemcached replicas only work if binary mode is enabled memcached.sess_binary = Off ; memcached session replica read randomize memcached.sess_randomize_replica_read = Off ; memcached connect timeout value ; In non-blocking mode this changes the value of the timeout ; during socket connection in milliseconds. Specifying -1 means an infinite timeout. memcached.sess_connect_timeout = 1000 ; Session SASL username ; Both username and password need to be set for SASL to be enabled ; In addition to this memcached.use_sasl needs to be on memcached.sess_sasl_username = NULL ; Session SASL password memcached.sess_sasl_password = NULL ; Set the compression type ; valid values are: fastlz, zlib ; the default is fastlz memcached.compression_type = "fastlz" ; Compression factor ; Store compressed value only if the compression ; factor (saving) exceeds the set limit. ; ; store compressed if: ; plain_len > comp_len * factor ; ; the default value is 1.3 (23% space saving) memcached.compression_factor = "1.3" ; The compression threshold ; ; Do not compress serialized values below this threshold. ; the default is 2000 bytes memcached.compression_threshold = 2000 ; Set the default serializer for new memcached objects. ; valid values are: php, igbinary, json, json_array, msgpack ; ; json - standard php JSON encoding. This serializer ; is fast and compact but only works on UTF-8 ; encoded data and does not fully implement ; serializing. See the JSON extension. ; json_array - as json, but decodes into arrays ; php - the standard php serializer ; igbinary - a binary serializer ; msgpack - a cross-language binary serializer ; ; The default is igbinary if available, then msgpack if available, then php otherwise. memcached.serializer = "igbinary" ; Use SASL authentication for connections ; valid values: On, Off ; the default is Off memcached.use_sasl = Off ; The amount of retries for failed store commands. ; This mechanism allows transparent fail-over to secondary servers when ; set/increment/decrement/setMulti operations fail on the desired server in a multi-server ; environment. ; the default is 2 memcached.store_retry_count = 2 FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dnl dnl $ Id: $ dnl vim:se ts=2 sw=2 et: PHP_ARG_ENABLE(memcached, whether to enable memcached support, [ --enable-memcached Enable memcached support]) PHP_ARG_WITH(libmemcached-dir, for libmemcached, [ --with-libmemcached-dir[=DIR] Set the path to libmemcached install prefix.], yes) PHP_ARG_ENABLE(memcached-session, whether to enable memcached session handler support, [ --disable-memcached-session Disable memcached session handler support], yes, no) PHP_ARG_ENABLE(memcached-igbinary, whether to enable memcached igbinary serializer support, [ --enable-memcached-igbinary Enable memcached igbinary serializer support], no, no) PHP_ARG_ENABLE(memcached-json, whether to enable memcached json serializer support, [ --enable-memcached-json Enable memcached json serializer support], no, no) PHP_ARG_ENABLE(memcached-msgpack, whether to enable memcached msgpack serializer support, [ --enable-memcached-msgpack Enable memcached msgpack serializer support], no, no) PHP_ARG_ENABLE(memcached-sasl, whether to enable memcached sasl support, [ --disable-memcached-sasl Disable memcached sasl support], yes, no) PHP_ARG_ENABLE(memcached-protocol, whether to enable memcached protocol support, [ --enable-memcached-protocol Enable memcached protocoll support], no, no) if test -z "$PHP_ZLIB_DIR"; then PHP_ARG_WITH(zlib-dir, for ZLIB, [ --with-zlib-dir[=DIR] Set the path to ZLIB install prefix.], no) fi if test -z "$PHP_DEBUG"; then AC_ARG_ENABLE(debug, [ --enable-debug compile with debugging symbols],[ PHP_DEBUG=$enableval ],[ PHP_DEBUG=no ]) fi if test "$PHP_MEMCACHED" != "no"; then AC_PATH_PROG(PKG_CONFIG, pkg-config, no) if test "x$PKG_CONFIG" = "xno"; then AC_MSG_RESULT([pkg-config not found]) AC_MSG_ERROR([Please reinstall the pkg-config distribution]) fi dnl # zlib if test "$PHP_ZLIB_DIR" != "no" && test "$PHP_ZLIB_DIR" != "yes"; then if test -f "$PHP_ZLIB_DIR/include/zlib/zlib.h"; then PHP_ZLIB_DIR="$PHP_ZLIB_DIR" PHP_ZLIB_INCDIR="$PHP_ZLIB_DIR/include/zlib" elif test -f "$PHP_ZLIB_DIR/include/zlib.h"; then PHP_ZLIB_DIR="$PHP_ZLIB_DIR" PHP_ZLIB_INCDIR="$PHP_ZLIB_DIR/include" else AC_MSG_ERROR([Can't find ZLIB headers under "$PHP_ZLIB_DIR"]) fi else for i in /usr/local /usr; do if test -f "$i/include/zlib/zlib.h"; then PHP_ZLIB_DIR="$i" PHP_ZLIB_INCDIR="$i/include/zlib" elif test -f "$i/include/zlib.h"; then PHP_ZLIB_DIR="$i" PHP_ZLIB_INCDIR="$i/include" fi done fi AC_MSG_CHECKING([for zlib location]) if test "$PHP_ZLIB_DIR" != "no" && test "$PHP_ZLIB_DIR" != "yes"; then AC_MSG_RESULT([$PHP_ZLIB_DIR]) PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR/$PHP_LIBDIR, MEMCACHED_SHARED_LIBADD) PHP_ADD_INCLUDE($PHP_ZLIB_INCDIR) else AC_MSG_ERROR([memcached support requires ZLIB. Use --with-zlib-dir=<DIR> to specify the prefix where ZLIB headers and library are located]) fi if test "$PHP_MEMCACHED_SESSION" != "no"; then AC_MSG_CHECKING([for session includes]) session_inc_path="" if test -f "$abs_srcdir/include/php/ext/session/php_session.h"; then session_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/session/php_session.h"; then session_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/session/php_session.h"; then session_inc_path="$phpincludedir" else for i in php php4 php5 php6; do if test -f "$prefix/include/$i/ext/session/php_session.h"; then session_inc_path="$prefix/include/$i" fi done fi if test "$session_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_session.h]) else AC_MSG_RESULT([$session_inc_path]) fi fi if test "$PHP_MEMCACHED_JSON" != "no"; then AC_MSG_CHECKING([for json includes]) json_inc_path="" tmp_version=$PHP_VERSION if test -z "$tmp_version"; then if test -z "$PHP_CONFIG"; then AC_MSG_ERROR([php-config not found]) fi PHP_MEMCACHED_VERSION_ORIG=`$PHP_CONFIG --version`; else PHP_MEMCACHED_VERSION_ORIG=$tmp_version fi if test -z $PHP_MEMCACHED_VERSION_ORIG; then AC_MSG_ERROR([failed to detect PHP version, please report]) fi PHP_MEMCACHED_VERSION_MASK=`echo ${PHP_MEMCACHED_VERSION_ORIG} | awk 'BEGIN { FS = "."; } { printf "%d", ($1 * 1000 + $2) * 1000 + $3;}'` if test $PHP_MEMCACHED_VERSION_MASK -ge 5003000; then if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then json_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/json/php_json.h"; then json_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/json/php_json.h"; then json_inc_path="$phpincludedir" else for i in php php4 php5 php6; do if test -f "$prefix/include/$i/ext/json/php_json.h"; then json_inc_path="$prefix/include/$i" fi done fi if test "$json_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_json.h]) else AC_DEFINE(HAVE_JSON_API,1,[Whether JSON API is available]) AC_DEFINE(HAVE_JSON_API_5_3,1,[Whether JSON API for PHP 5.3 is available]) AC_MSG_RESULT([$json_inc_path]) fi elif test $PHP_MEMCACHED_VERSION_MASK -ge 5002009; then dnl Check JSON for PHP 5.2.9+ if test -f "$abs_srcdir/include/php/ext/json/php_json.h"; then json_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/json/php_json.h"; then json_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/json/php_json.h"; then json_inc_path="$phpincludedir" else for i in php php4 php5 php6; do if test -f "$prefix/include/$i/ext/json/php_json.h"; then json_inc_path="$prefix/include/$i" fi done fi if test "$json_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_json.h]) else AC_DEFINE(HAVE_JSON_API,1,[Whether JSON API is available]) AC_DEFINE(HAVE_JSON_API_5_2,1,[Whether JSON API for PHP 5.2 is available]) AC_MSG_RESULT([$json_inc_path]) fi else AC_MSG_RESULT([the PHP version does not support JSON serialization API]) fi fi if test "$PHP_MEMCACHED_IGBINARY" != "no"; then AC_MSG_CHECKING([for igbinary includes]) igbinary_inc_path="" if test -f "$abs_srcdir/include/php/ext/igbinary/igbinary.h"; then igbinary_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/session/igbinary.h"; then igbinary_inc_path="$phpincludedir" elif test -f "$phpincludedir/ext/igbinary/igbinary.h"; then igbinary_inc_path="$phpincludedir" else for i in php php4 php5 php6; do if test -f "$prefix/include/$i/ext/igbinary/igbinary.h"; then igbinary_inc_path="$prefix/include/$i" fi done fi if test "$igbinary_inc_path" = ""; then AC_MSG_ERROR([Cannot find igbinary.h]) else AC_MSG_RESULT([$igbinary_inc_path]) fi fi if test "$PHP_MEMCACHED_MSGPACK" != "no"; then AC_MSG_CHECKING([for msgpack includes]) msgpack_inc_path="" if test -f "$abs_srcdir/include/php/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$abs_srcdir/include/php" elif test -f "$abs_srcdir/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$abs_srcdir" elif test -f "$phpincludedir/ext/session/php_msgpack.h"; then msgpack_inc_path="$phpincludedir" elif test -f "$phpincludedir/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$phpincludedir" else for i in php php4 php5 php6; do if test -f "$prefix/include/$i/ext/msgpack/php_msgpack.h"; then msgpack_inc_path="$prefix/include/$i" fi done fi if test "$msgpack_inc_path" = ""; then AC_MSG_ERROR([Cannot find php_msgpack.h]) else AC_MSG_RESULT([$msgpack_inc_path]) fi fi AC_MSG_CHECKING([for memcached session support]) if test "$PHP_MEMCACHED_SESSION" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_MEMCACHED_SESSION,1,[Whether memcache session handler is enabled]) SESSION_INCLUDES="-I$session_inc_path" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcached, session) ]) else SESSION_INCLUDES="" AC_MSG_RESULT([disabled]) fi AC_MSG_CHECKING([for memcached igbinary support]) if test "$PHP_MEMCACHED_IGBINARY" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_MEMCACHED_IGBINARY,1,[Whether memcache igbinary serializer is enabled]) IGBINARY_INCLUDES="-I$igbinary_inc_path" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcached, igbinary) ]) else IGBINARY_INCLUDES="" AC_MSG_RESULT([disabled]) fi AC_MSG_CHECKING([for memcached msgpack support]) if test "$PHP_MEMCACHED_MSGPACK" != "no"; then AC_MSG_RESULT([enabled]) AC_DEFINE(HAVE_MEMCACHED_MSGPACK,1,[Whether memcache msgpack serializer is enabled]) MSGPACK_INCLUDES="-I$msgpack_inc_path" ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcached, msgpack) ]) else MSGPACK_INCLUDES="" AC_MSG_RESULT([disabled]) fi AC_MSG_CHECKING([for libmemcached location]) export ORIG_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" if test "$PHP_LIBMEMCACHED_DIR" != "no" && test "$PHP_LIBMEMCACHED_DIR" != "yes"; then export PKG_CONFIG_PATH="$PHP_LIBMEMCACHED_DIR/$PHP_LIBDIR/pkgconfig" if test ! -f "$PHP_LIBMEMCACHED_DIR/include/libmemcached/memcached.h"; then AC_MSG_ERROR(Unable to find memcached.h under $PHP_LIBMEMCACHED_DIR) fi else export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/$PHP_LIBDIR/pkgconfig:/usr/$PHP_LIBDIR/pkgconfig:/opt/$PHP_LIBDIR/pkgconfig" fi if ! $PKG_CONFIG --exists libmemcached; then AC_MSG_ERROR([memcached support requires libmemcached. Use --with-libmemcached-dir=<DIR> to specify the prefix where libmemcached headers and library are located]) else PHP_LIBMEMCACHED_VERSION=`$PKG_CONFIG libmemcached --modversion` PHP_LIBMEMCACHED_DIR=`$PKG_CONFIG libmemcached --variable=prefix` AC_MSG_RESULT([found version $PHP_LIBMEMCACHED_VERSION, under $PHP_LIBMEMCACHED_DIR]) PHP_LIBMEMCACHED_LIBS=`$PKG_CONFIG libmemcached --libs` PHP_LIBMEMCACHED_INCLUDES=`$PKG_CONFIG libmemcached --cflags` PHP_EVAL_LIBLINE($PHP_LIBMEMCACHED_LIBS, MEMCACHED_SHARED_LIBADD) PHP_EVAL_INCLINE($PHP_LIBMEMCACHED_INCLUDES) # # Added -lpthread here because AC_TRY_LINK tests on CentOS 6 seem to fail with undefined reference to pthread_once # ORIG_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $INCLUDES" AC_MSG_CHECKING([whether to enable sasl support]) if test "$PHP_MEMCACHED_SASL" != "no"; then AC_MSG_RESULT(yes) AC_CHECK_HEADERS([sasl/sasl.h], [ac_cv_have_memc_sasl_h="yes"], [ac_cv_have_memc_sasl_h="no"]) if test "$ac_cv_have_memc_sasl_h" = "yes"; then AC_CACHE_CHECK([whether libmemcached supports sasl], ac_cv_memc_sasl_support, [ AC_TRY_COMPILE( [ #include <libmemcached/memcached.h> ], [ #if LIBMEMCACHED_WITH_SASL_SUPPORT /* yes */ #else # error "no sasl support" #endif ], [ ac_cv_memc_sasl_support="yes" ], [ ac_cv_memc_sasl_support="no" ] ) ]) if test "$ac_cv_memc_sasl_support" = "yes"; then PHP_CHECK_LIBRARY(sasl2, sasl_client_init, [PHP_ADD_LIBRARY(sasl2, 1, MEMCACHED_SHARED_LIBADD)]) AC_DEFINE(HAVE_MEMCACHED_SASL, 1, [Have SASL support]) else AC_MSG_ERROR([no, libmemcached sasl support is not enabled. Run configure with --disable-memcached-sasl to disable this check]) fi else AC_MSG_ERROR([no, sasl.h is not available. Run configure with --disable-memcached-sasl to disable this check]) fi else AC_MSG_RESULT([no]) fi PHP_MEMCACHED_FILES="php_memcached.c php_libmemcached_compat.c fastlz/fastlz.c g_fmt.c" if test "$PHP_MEMCACHED_SESSION" != "no"; then PHP_MEMCACHED_FILES="${PHP_MEMCACHED_FILES} php_memcached_session.c" fi LIBEVENT_INCLUDES="" AC_MSG_CHECKING([for memcached protocol support]) if test "$PHP_MEMCACHED_PROTOCOL" != "no"; then AC_MSG_RESULT([enabled]) AC_CACHE_CHECK([whether libmemcachedprotocol is usable], ac_cv_have_libmemcachedprotocol, [ AC_TRY_COMPILE( [ #include <libmemcachedprotocol-0.0/handler.h> ], [ memcached_binary_protocol_callback_st s_test_impl; s_test_impl.interface.v1.delete_object = 0; ], [ ac_cv_have_libmemcachedprotocol="yes" ], [ ac_cv_have_libmemcachedprotocol="no" ] ) ]) if test "$ac_cv_have_libmemcachedprotocol" != "yes"; then AC_MSG_ERROR([Cannot enable libmemcached protocol]) fi PHP_ADD_LIBRARY_WITH_PATH(memcachedprotocol, $PHP_LIBMEMCACHED_DIR/$PHP_LIBDIR, MEMCACHED_SHARED_LIBADD) AC_MSG_CHECKING([for libevent]) if $PKG_CONFIG --exists libevent; then PHP_MEMCACHED_LIBEVENT_VERSION=`$PKG_CONFIG libevent --modversion` PHP_MEMCACHED_LIBEVENT_PREFIX=`$PKG_CONFIG libevent --variable=prefix` AC_MSG_RESULT([found version $PHP_MEMCACHED_LIBEVENT_VERSION, under $PHP_MEMCACHED_LIBEVENT_PREFIX]) LIBEVENT_LIBS=`$PKG_CONFIG libevent --libs` LIBEVENT_INCLUDES=`$PKG_CONFIG libevent --cflags` PHP_EVAL_LIBLINE($LIBEVENT_LIBS, MEMCACHED_SHARED_LIBADD) PHP_EVAL_INCLINE($LIBEVENT_INCLUDES) else AC_MSG_ERROR(Unable to find libevent installation) fi PHP_MEMCACHED_FILES="${PHP_MEMCACHED_FILES} php_memcached_server.c" AC_DEFINE(HAVE_MEMCACHED_PROTOCOL,1,[Whether memcached protocol is enabled]) else AC_MSG_RESULT([disabled]) fi CFLAGS="$ORIG_CFLAGS" export PKG_CONFIG_PATH="$ORIG_PKG_CONFIG_PATH" PHP_SUBST(MEMCACHED_SHARED_LIBADD) PHP_NEW_EXTENSION(memcached, $PHP_MEMCACHED_FILES, $ext_shared,,$SESSION_INCLUDES $IGBINARY_INCLUDES $LIBEVENT_INCLUDES $MSGPACK_INCLUDES) PHP_ADD_BUILD_DIR($ext_builddir/fastlz, 1) ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP(memcached, spl, true) ]) fi fi // $ Id: $ // vim:ft=javascript ARG_WITH('memcached', 'libmemcached extension', 'no'); if (PHP_MEMCACHED == "yes") { if (!CHECK_LIB("memcached.lib", "memcached", PHP_MEMCACHED)) { ERROR("memcached: library 'memcached' not found"); } if (!CHECK_HEADER_ADD_INCLUDE("libmemcached/memcached.h", "CFLAGS_MEMCACHED")) { ERROR("memcached: header 'libmemcached/memcached.h' not found"); } EXTENSION("memcached", "memcached.c"); AC_DEFINE("HAVE_MEMCACHED", 1, "memcached support"); AC_DEFINE("MEMCACHED_EXPORTS", 1) } /* +----------------------------------------------------------------------+ | Copyright (c) 2009-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ /* $ Id: $ */ /* TODO * - set LIBKETAMA_COMPATIBLE as the default? * - fix unserialize(serialize($memc)) * - ability to set binary protocol for sessions */ #include "php_memcached.h" #include "php_memcached_private.h" #include "php_memcached_server.h" #include "g_fmt.h" #ifdef HAVE_MEMCACHED_SESSION # include "php_memcached_session.h" #endif #include "fastlz/fastlz.h" #include <zlib.h> #ifdef HAVE_JSON_API # include "ext/json/php_json.h" #endif #ifdef HAVE_MEMCACHED_IGBINARY # include "ext/igbinary/igbinary.h" #endif #ifdef HAVE_MEMCACHED_MSGPACK # include "ext/msgpack/php_msgpack.h" #endif /* * This is needed because PHP 5.3.[01] does not install JSON_parser.h by default. This * constant will move into php_json.h in the future anyway. */ #ifndef JSON_PARSER_DEFAULT_DEPTH #define JSON_PARSER_DEFAULT_DEPTH 512 #endif /**************************************** Custom options ****************************************/ #define MEMC_OPT_COMPRESSION -1001 #define MEMC_OPT_PREFIX_KEY -1002 #define MEMC_OPT_SERIALIZER -1003 #define MEMC_OPT_COMPRESSION_TYPE -1004 #define MEMC_OPT_STORE_RETRY_COUNT -1005 /**************************************** Custom result codes ****************************************/ #define MEMC_RES_PAYLOAD_FAILURE -1001 /**************************************** Payload value flags ****************************************/ #define MEMC_CREATE_MASK(start, n_bits) (((1 << n_bits) - 1) << start) #define MEMC_MASK_TYPE MEMC_CREATE_MASK(0, 4) #define MEMC_MASK_INTERNAL MEMC_CREATE_MASK(4, 12) #define MEMC_MASK_USER MEMC_CREATE_MASK(16, 16) #define MEMC_VAL_GET_TYPE(flags) ((flags) & MEMC_MASK_TYPE) #define MEMC_VAL_SET_TYPE(flags, type) ((flags) |= ((type) & MEMC_MASK_TYPE)) #define MEMC_VAL_IS_STRING 0 #define MEMC_VAL_IS_LONG 1 #define MEMC_VAL_IS_DOUBLE 2 #define MEMC_VAL_IS_BOOL 3 #define MEMC_VAL_IS_SERIALIZED 4 #define MEMC_VAL_IS_IGBINARY 5 #define MEMC_VAL_IS_JSON 6 #define MEMC_VAL_IS_MSGPACK 7 #define MEMC_VAL_COMPRESSED (1<<0) #define MEMC_VAL_COMPRESSION_ZLIB (1<<1) #define MEMC_VAL_COMPRESSION_FASTLZ (1<<2) #define MEMC_VAL_GET_FLAGS(internal_flags) ((internal_flags & MEMC_MASK_INTERNAL) >> 4) #define MEMC_VAL_SET_FLAG(internal_flags, internal_flag) ((internal_flags) |= ((internal_flag << 4) & MEMC_MASK_INTERNAL)) #define MEMC_VAL_HAS_FLAG(internal_flags, internal_flag) ((MEMC_VAL_GET_FLAGS(internal_flags) & internal_flag) == internal_flag) #define MEMC_VAL_DEL_FLAG(internal_flags, internal_flag) internal_flags &= ~((internal_flag << 4) & MEMC_MASK_INTERNAL) /**************************************** User-defined flags ****************************************/ #define MEMC_VAL_GET_USER_FLAGS(flags) ((flags & MEMC_MASK_USER) >> 16) #define MEMC_VAL_SET_USER_FLAGS(flags, udf_flags) ((flags) |= ((udf_flags << 16) & MEMC_MASK_USER)) #define MEMC_VAL_USER_FLAGS_MAX ((1 << 16) - 1) /**************************************** "get" operation flags ****************************************/ #define MEMC_GET_PRESERVE_ORDER (1<<0) /**************************************** Helper macros ****************************************/ #define MEMC_METHOD_INIT_VARS \ zval* object = getThis(); \ php_memc_t* i_obj = NULL; \ struct memc_obj* m_obj = NULL; #define MEMC_METHOD_FETCH_OBJECT \ i_obj = (php_memc_t *) zend_object_store_get_object( object TSRMLS_CC ); \ m_obj = i_obj->obj; \ if (!m_obj) { \ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Memcached constructor was not called"); \ return; \ } #ifndef DVAL_TO_LVAL #ifdef _WIN64 # define DVAL_TO_LVAL(d, l) \ if ((d) > LONG_MAX) { \ (l) = (long)(unsigned long)(__int64) (d); \ } else { \ (l) = (long) (d); \ } #else # define DVAL_TO_LVAL(d, l) \ if ((d) > LONG_MAX) { \ (l) = (unsigned long) (d); \ } else { \ (l) = (long) (d); \ } #endif #endif #define RETURN_FROM_GET RETURN_FALSE #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 3) #define zend_parse_parameters_none() \ zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") #endif /**************************************** Structures and definitions ****************************************/ enum memcached_compression_type { COMPRESSION_TYPE_ZLIB = 1, COMPRESSION_TYPE_FASTLZ = 2, }; typedef struct { zend_object zo; struct memc_obj { memcached_st *memc; zend_bool compression; enum memcached_serializer serializer; enum memcached_compression_type compression_type; #if HAVE_MEMCACHED_SASL zend_bool has_sasl_data; #endif long store_retry_count; } *obj; zend_bool is_persistent; zend_bool is_pristine; int rescode; int memc_errno; } php_memc_t; #ifdef HAVE_MEMCACHED_PROTOCOL typedef struct { zend_object zo; php_memc_proto_handler_t *handler; } php_memc_server_t; #endif enum { MEMC_OP_SET, MEMC_OP_TOUCH, MEMC_OP_ADD, MEMC_OP_REPLACE, MEMC_OP_APPEND, MEMC_OP_PREPEND }; static zend_class_entry *memcached_ce = NULL; static zend_class_entry *memcached_exception_ce = NULL; static zend_object_handlers memcached_object_handlers; #ifdef HAVE_MEMCACHED_PROTOCOL static zend_object_handlers memcached_server_object_handlers; static zend_class_entry *memcached_server_ce = NULL; #endif struct callbackContext { zval *array; zval *entry; memcached_stat_st *stats; /* for use with functions that need stats */ void *return_value; unsigned int i; /* for use with structures mapped against servers */ }; #if HAVE_SPL static zend_class_entry *spl_ce_RuntimeException = NULL; #endif #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3) const zend_fcall_info empty_fcall_info = { 0, NULL, NULL, NULL, NULL, 0, NULL, NULL, 0 }; #undef ZEND_BEGIN_ARG_INFO_EX #define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ static zend_arg_info name[] = { \ { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, #endif ZEND_DECLARE_MODULE_GLOBALS(php_memcached) #ifdef COMPILE_DL_MEMCACHED ZEND_GET_MODULE(memcached) #endif static PHP_INI_MH(OnUpdateCompressionType) { if (!new_value) { MEMC_G(compression_type_real) = COMPRESSION_TYPE_FASTLZ; } else if (!strcmp(new_value, "fastlz")) { MEMC_G(compression_type_real) = COMPRESSION_TYPE_FASTLZ; } else if (!strcmp(new_value, "zlib")) { MEMC_G(compression_type_real) = COMPRESSION_TYPE_ZLIB; } else { return FAILURE; } return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } static PHP_INI_MH(OnUpdateSerializer) { if (!new_value) { MEMC_G(serializer) = SERIALIZER_DEFAULT; } else if (!strcmp(new_value, "php")) { MEMC_G(serializer) = SERIALIZER_PHP; #ifdef HAVE_MEMCACHED_IGBINARY } else if (!strcmp(new_value, "igbinary")) { MEMC_G(serializer) = SERIALIZER_IGBINARY; #endif // IGBINARY #ifdef HAVE_JSON_API } else if (!strcmp(new_value, "json")) { MEMC_G(serializer) = SERIALIZER_JSON; } else if (!strcmp(new_value, "json_array")) { MEMC_G(serializer) = SERIALIZER_JSON_ARRAY; #endif // JSON #ifdef HAVE_MEMCACHED_MSGPACK } else if (!strcmp(new_value, "msgpack")) { MEMC_G(serializer) = SERIALIZER_MSGPACK; #endif // msgpack } else { return FAILURE; } return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* {{{ INI entries */ PHP_INI_BEGIN() #ifdef HAVE_MEMCACHED_SESSION STD_PHP_INI_ENTRY("memcached.sess_locking", "1", PHP_INI_ALL, OnUpdateBool, sess_locking_enabled, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_consistent_hash", "0", PHP_INI_ALL, OnUpdateBool, sess_consistent_hash_enabled, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_binary", "0", PHP_INI_ALL, OnUpdateBool, sess_binary_enabled, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_lock_wait", "150000", PHP_INI_ALL, OnUpdateLongGEZero,sess_lock_wait, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_lock_max_wait", "0", PHP_INI_ALL, OnUpdateLongGEZero, sess_lock_max_wait, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_lock_expire", "0", PHP_INI_ALL, OnUpdateLongGEZero, sess_lock_expire, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_prefix", "memc.sess.key.", PHP_INI_ALL, OnUpdateString, sess_prefix, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_number_of_replicas", "0", PHP_INI_ALL, OnUpdateLongGEZero, sess_number_of_replicas, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_randomize_replica_read", "0", PHP_INI_ALL, OnUpdateBool, sess_randomize_replica_read, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_remove_failed", "0", PHP_INI_ALL, OnUpdateBool, sess_remove_failed_enabled, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_connect_timeout", "1000", PHP_INI_ALL, OnUpdateLong, sess_connect_timeout, zend_php_memcached_globals, php_memcached_globals) #if HAVE_MEMCACHED_SASL STD_PHP_INI_ENTRY("memcached.sess_sasl_username", "", PHP_INI_ALL, OnUpdateString, sess_sasl_username, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.sess_sasl_password", "", PHP_INI_ALL, OnUpdateString, sess_sasl_password, zend_php_memcached_globals, php_memcached_globals) #endif #endif STD_PHP_INI_ENTRY("memcached.compression_type", "fastlz", PHP_INI_ALL, OnUpdateCompressionType, compression_type, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.compression_factor", "1.3", PHP_INI_ALL, OnUpdateReal, compression_factor, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.compression_threshold", "2000", PHP_INI_ALL, OnUpdateLong, compression_threshold, zend_php_memcached_globals, php_memcached_globals) STD_PHP_INI_ENTRY("memcached.serializer", SERIALIZER_DEFAULT_NAME, PHP_INI_ALL, OnUpdateSerializer, serializer_name, zend_php_memcached_globals, php_memcached_globals) #if HAVE_MEMCACHED_SASL STD_PHP_INI_ENTRY("memcached.use_sasl", "0", PHP_INI_SYSTEM, OnUpdateBool, use_sasl, zend_php_memcached_globals, php_memcached_globals) #endif STD_PHP_INI_ENTRY("memcached.store_retry_count", "2", PHP_INI_ALL, OnUpdateLong, store_retry_count, zend_php_memcached_globals, php_memcached_globals) PHP_INI_END() /* }}} */ /**************************************** Forward declarations ****************************************/ static int php_memc_handle_error(php_memc_t *i_obj, memcached_return status TSRMLS_DC); static char *php_memc_zval_to_payload(zval *value, size_t *payload_len, uint32_t *flags, enum memcached_serializer serializer, enum memcached_compression_type compression_type TSRMLS_DC); static int php_memc_zval_from_payload(zval *value, const char *payload, size_t payload_len, uint32_t flags, enum memcached_serializer serializer TSRMLS_DC); static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key); static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key); static memcached_return php_memc_do_cache_callback(zval *memc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, char *key, size_t key_len, zval *value TSRMLS_DC); static int php_memc_do_result_callback(zval *memc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, memcached_result_st *result TSRMLS_DC); static memcached_return php_memc_do_serverlist_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context); static memcached_return php_memc_do_stats_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context); static memcached_return php_memc_do_version_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context); static void php_memc_destroy(struct memc_obj *m_obj, zend_bool persistent TSRMLS_DC); /**************************************** Method implementations ****************************************/ char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TSRMLS_DC) { char *buffer = NULL; if (fci->object_ptr) { spprintf (&buffer, 0, "%s::%s", Z_OBJCE_P (fci->object_ptr)->name, fci_cache->function_handler->common.function_name); } else { if (Z_TYPE_P (fci->function_name) == IS_OBJECT) { spprintf (&buffer, 0, "%s", Z_OBJCE_P (fci->function_name)->name); } else { spprintf (&buffer, 0, "%s", Z_STRVAL_P (fci->function_name)); } } return buffer; } static zend_bool php_memcached_on_new_callback(zval *object, zend_fcall_info *fci, zend_fcall_info_cache *fci_cache, char *persistent_id, int persistent_id_len TSRMLS_DC) { zend_bool retval = 1; zval pid_z; zval *retval_ptr, *pid_z_ptr = &pid_z; zval **params[2]; INIT_ZVAL(pid_z); if (persistent_id) { ZVAL_STRINGL(pid_z_ptr, persistent_id, persistent_id_len, 1); } /* Call the cb */ params[0] = &object; params[1] = &pid_z_ptr; fci->params = params; fci->param_count = 2; fci->retval_ptr_ptr = &retval_ptr; fci->no_separation = 1; if (zend_call_function(fci, fci_cache TSRMLS_CC) == FAILURE) { char *buf = php_memc_printable_func (fci, fci_cache TSRMLS_CC); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to invoke 'on_new' callback %s()", buf); efree (buf); retval = 0; } zval_dtor(pid_z_ptr); if (retval_ptr) { zval_ptr_dtor(&retval_ptr); } return retval; } static int le_memc, le_memc_sess; static int php_memc_list_entry(void) { return le_memc; } /* {{{ Memcached::__construct([string persistent_id[, callback on_new[, string connection_str]]])) Creates a Memcached object, optionally using persistent memcache connection */ static PHP_METHOD(Memcached, __construct) { zval *object = getThis(); php_memc_t *i_obj; struct memc_obj *m_obj = NULL; char *persistent_id = NULL, *conn_str = NULL; int persistent_id_len, conn_str_len; zend_bool is_persistent = 0; char *plist_key = NULL; int plist_key_len = 0; zend_fcall_info fci = {0}; zend_fcall_info_cache fci_cache; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!f!s", &persistent_id, &persistent_id_len, &fci, &fci_cache, &conn_str, &conn_str_len) == FAILURE) { ZVAL_NULL(object); return; } i_obj = (php_memc_t *) zend_object_store_get_object(object TSRMLS_CC); i_obj->is_pristine = 0; if (persistent_id && *persistent_id) { zend_rsrc_list_entry *le = NULL; is_persistent = 1; plist_key_len = spprintf(&plist_key, 0, "memcached:id=%s", persistent_id); plist_key_len += 1; if (zend_hash_find(&EG(persistent_list), plist_key, plist_key_len, (void *)&le) == SUCCESS) { if (le->type == php_memc_list_entry()) { m_obj = (struct memc_obj *) le->ptr; } } i_obj->obj = m_obj; } i_obj->is_persistent = is_persistent; if (!m_obj) { m_obj = pecalloc(1, sizeof(*m_obj), is_persistent); if (m_obj == NULL) { if (plist_key) { efree(plist_key); } php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory: cannot allocate handle"); /* not reached */ } if (conn_str) { m_obj->memc = php_memc_create_str(conn_str, conn_str_len); if (!m_obj->memc) { char error_buffer[1024]; if (plist_key) { efree(plist_key); } #ifdef HAVE_LIBMEMCACHED_CHECK_CONFIGURATION if (libmemcached_check_configuration(conn_str, conn_str_len, error_buffer, sizeof(error_buffer)) != MEMCACHED_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "configuration error %s", error_buffer); } else { php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not allocate libmemcached structure"); } #else php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not allocate libmemcached structure"); #endif /* not reached */ } } else { m_obj->memc = memcached_create(NULL); if (m_obj->memc == NULL) { if (plist_key) { efree(plist_key); } php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not allocate libmemcached structure"); /* not reached */ } } m_obj->serializer = MEMC_G(serializer); m_obj->compression_type = MEMC_G(compression_type_real); m_obj->compression = 1; m_obj->store_retry_count = MEMC_G(store_retry_count); i_obj->obj = m_obj; i_obj->is_pristine = 1; if (fci.size) { /* will be 0 when not available */ if (!php_memcached_on_new_callback(object, &fci, &fci_cache, persistent_id, persistent_id_len TSRMLS_CC) || EG(exception)) { /* error calling or exception thrown from callback */ if (plist_key) { efree(plist_key); } i_obj->obj = NULL; if (is_persistent) { php_memc_destroy(m_obj, is_persistent TSRMLS_CC); } return; } } if (is_persistent) { zend_rsrc_list_entry le; le.type = php_memc_list_entry(); le.ptr = m_obj; if (zend_hash_update(&EG(persistent_list), (char *)plist_key, plist_key_len, (void *)&le, sizeof(le), NULL) == FAILURE) { if (plist_key) { efree(plist_key); } php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not register persistent entry"); /* not reached */ } } } if (plist_key) { efree(plist_key); } } /* }}} */ /* {{{ Memcached::get(string key [, mixed callback [, double &cas_token [, int &udf_flags ] ] ]) Returns a value for the given key or false */ PHP_METHOD(Memcached, get) { php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::getByKey(string server_key, string key [, mixed callback [, double &cas_token [, int &udf_flags ] ] ]) Returns a value for key from the server identified by the server key or false */ PHP_METHOD(Memcached, getByKey) { php_memc_get_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ -- php_memc_get_impl */ static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { char *key = NULL; int key_len = 0; char *server_key = NULL; int server_key_len = 0; const char *payload = NULL; size_t payload_len = 0; uint32_t flags = 0; uint64_t cas = 0; const char* keys[1] = { NULL }; size_t key_lens[1] = { 0 }; zval *cas_token = NULL; zval *udf_flags = NULL; zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|f!zz", &server_key, &server_key_len, &key, &key_len, &fci, &fcc, &cas_token, &udf_flags) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f!zz", &key, &key_len, &fci, &fcc, &cas_token, &udf_flags) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0 || strchr(key, ' ')) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FROM_GET; } keys[0] = key; key_lens[0] = key_len; uint64_t orig_cas_flag; orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); /* * Enable CAS support, but only if it is currently disabled. */ if (cas_token && PZVAL_IS_REF(cas_token) && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); } status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, keys, key_lens, 1); if (cas_token && PZVAL_IS_REF(cas_token) && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FROM_GET; } status = MEMCACHED_SUCCESS; memcached_result_create(m_obj->memc, &result); if (memcached_fetch_result(m_obj->memc, &result, &status) == NULL) { /* This is for historical reasons */ if (status == MEMCACHED_END) status = MEMCACHED_NOTFOUND; /* * If the result wasn't found, and we have the read-through callback, invoke * it to get the value. The CAS token will be 0, because we cannot generate it * ourselves. */ if (cas_token) { ZVAL_DOUBLE(cas_token, 0.0); } if (status == MEMCACHED_NOTFOUND && fci.size != 0) { status = php_memc_do_cache_callback(getThis(), &fci, &fcc, key, key_len, return_value TSRMLS_CC); } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { memcached_result_free(&result); RETURN_FROM_GET; } /* if we have a callback, all processing is done */ if (fci.size != 0) { memcached_result_free(&result); return; } } /* Fetch all remaining results */ memcached_result_st dummy_result; memcached_return dummy_status = MEMCACHED_SUCCESS; memcached_result_create(m_obj->memc, &dummy_result); while (memcached_fetch_result(m_obj->memc, &dummy_result, &dummy_status) != NULL) {} memcached_result_free(&dummy_result); payload = memcached_result_value(&result); payload_len = memcached_result_length(&result); flags = memcached_result_flags(&result); if (cas_token) { cas = memcached_result_cas(&result); } if (php_memc_zval_from_payload(return_value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { memcached_result_free(&result); i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FROM_GET; } if (cas_token) { zval_dtor(cas_token); ZVAL_DOUBLE(cas_token, (double)cas); } if (udf_flags) { zval_dtor(udf_flags); ZVAL_LONG(udf_flags, MEMC_VAL_GET_USER_FLAGS(flags)); } memcached_result_free(&result); } /* }}} */ /* {{{ Memcached::getMulti(array keys [, array &cas_tokens [, array &udf_flags ] ]) Returns values for the given keys or false */ PHP_METHOD(Memcached, getMulti) { php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::getMultiByKey(string server_key, array keys [, array &cas_tokens [, array &udf_flags ] ]) Returns values for the given keys from the server identified by the server key or false */ PHP_METHOD(Memcached, getMultiByKey) { php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ -- php_memc_getMulti_impl */ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { zval *keys = NULL; char *server_key = NULL; int server_key_len = 0; size_t num_keys = 0; zval **entry = NULL; const char *payload = NULL; size_t payload_len = 0; const char **mkeys = NULL; size_t *mkeys_len = NULL; const char *tmp_key = NULL; size_t res_key_len = 0; uint32_t flags; uint64_t cas = 0; zval *cas_tokens = NULL; zval *udf_flags = NULL; uint64_t orig_cas_flag = 0; zval *value; long get_flags = 0; int i = 0; zend_bool preserve_order; memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|zlz", &server_key, &server_key_len, &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|zlz", &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; preserve_order = (get_flags & MEMC_GET_PRESERVE_ORDER); num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); mkeys = safe_emalloc(num_keys, sizeof(*mkeys), 0); mkeys_len = safe_emalloc(num_keys, sizeof(*mkeys_len), 0); array_init(return_value); /* * Create the array of keys for libmemcached. If none of the keys were valid * (strings), set bad key result code and return. */ for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(keys)); zend_hash_get_current_data(Z_ARRVAL_P(keys), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(keys))) { if (Z_TYPE_PP(entry) != IS_STRING) { convert_to_string_ex(entry); } if (Z_TYPE_PP(entry) == IS_STRING && Z_STRLEN_PP(entry) > 0) { mkeys[i] = Z_STRVAL_PP(entry); mkeys_len[i] = Z_STRLEN_PP(entry); if (preserve_order) { add_assoc_null_ex(return_value, mkeys[i], mkeys_len[i] + 1); } i++; } } if (i == 0) { i_obj->rescode = MEMCACHED_NOTFOUND; efree(mkeys); efree(mkeys_len); return; } /* * Enable CAS support, but only if it is currently disabled. */ if (cas_tokens && PZVAL_IS_REF(cas_tokens)) { orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); if (orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); } } status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, mkeys, mkeys_len, i); /* Handle error, but ignore, there might still be some result */ php_memc_handle_error(i_obj, status TSRMLS_CC); /* * Restore the CAS support flag, but only if we had to turn it on. */ if (cas_tokens && PZVAL_IS_REF(cas_tokens) && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } efree(mkeys); efree(mkeys_len); /* * Iterate through the result set and create the result array. The CAS tokens are * returned as doubles, because we cannot store potential 64-bit values in longs. */ if (cas_tokens) { if (PZVAL_IS_REF(cas_tokens)) { /* cas_tokens was passed by reference, we'll create an array for it. */ zval_dtor(cas_tokens); array_init(cas_tokens); } else { /* Not passed by reference, we allow this (eg.: if you specify null to not enable cas but you want to use the udf_flags parameter). We destruct it and set it to null for the peace of mind. */ zval_dtor(cas_tokens); cas_tokens = NULL; } } /* * Iterate through the result set and create the result array. The flags are * returned as longs. */ if (udf_flags) { zval_dtor(udf_flags); array_init(udf_flags); } memcached_result_create(m_obj->memc, &result); while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { char res_key [MEMCACHED_MAX_KEY]; if (status != MEMCACHED_SUCCESS) { status = MEMCACHED_SOME_ERRORS; php_memc_handle_error(i_obj, status TSRMLS_CC); continue; } payload = memcached_result_value(&result); payload_len = memcached_result_length(&result); flags = memcached_result_flags(&result); tmp_key = memcached_result_key_value(&result); res_key_len = memcached_result_key_length(&result); /* * This may be a bug in libmemcached, the key is not null terminated * whe using the binary protocol. */ memcpy (res_key, tmp_key, res_key_len >= MEMCACHED_MAX_KEY ? MEMCACHED_MAX_KEY - 1 : res_key_len); res_key [res_key_len] = '\0'; MAKE_STD_ZVAL(value); if (php_memc_zval_from_payload(value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { zval_ptr_dtor(&value); if (EG(exception)) { status = MEMC_RES_PAYLOAD_FAILURE; php_memc_handle_error(i_obj, status TSRMLS_CC); memcached_quit(m_obj->memc); break; } status = MEMCACHED_SOME_ERRORS; i_obj->rescode = MEMCACHED_SOME_ERRORS; continue; } add_assoc_zval_ex(return_value, res_key, res_key_len+1, value); if (cas_tokens) { cas = memcached_result_cas(&result); add_assoc_double_ex(cas_tokens, res_key, res_key_len+1, (double)cas); } if (udf_flags) { add_assoc_long_ex(udf_flags, res_key, res_key_len+1, MEMC_VAL_GET_USER_FLAGS(flags)); } } memcached_result_free(&result); if (EG(exception)) { /* XXX: cas_tokens should only be set on success, currently we're destructive */ if (cas_tokens) { zval_dtor(cas_tokens); ZVAL_NULL(cas_tokens); } if (udf_flags) { zval_dtor(udf_flags); ZVAL_NULL(udf_flags); } zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ Memcached::getDelayed(array keys [, bool with_cas [, mixed callback ] ]) Sends a request for the given keys and returns immediately */ PHP_METHOD(Memcached, getDelayed) { php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::getDelayedByKey(string server_key, array keys [, bool with_cas [, mixed callback ] ]) Sends a request for the given keys from the server identified by the server key and returns immediately */ PHP_METHOD(Memcached, getDelayedByKey) { php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ -- php_memc_getDelayed_impl */ static void php_memc_getDelayed_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { zval *keys = NULL; char *server_key = NULL; int server_key_len = 0; zend_bool with_cas = 0; size_t num_keys = 0; zval **entry = NULL; const char **mkeys = NULL; size_t *mkeys_len = NULL; uint64_t orig_cas_flag = 0; zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; int i = 0; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|bf!", &server_key, &server_key_len, &keys, &with_cas, &fci, &fcc) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|bf!", &keys, &with_cas, &fci, &fcc) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; /* * Create the array of keys for libmemcached. If none of the keys were valid * (strings), set bad key result code and return. */ num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); mkeys = safe_emalloc(num_keys, sizeof(*mkeys), 0); mkeys_len = safe_emalloc(num_keys, sizeof(*mkeys_len), 0); for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(keys)); zend_hash_get_current_data(Z_ARRVAL_P(keys), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(keys))) { if (Z_TYPE_PP(entry) != IS_STRING) { convert_to_string_ex(entry); } if (Z_TYPE_PP(entry) == IS_STRING && Z_STRLEN_PP(entry) > 0) { mkeys[i] = Z_STRVAL_PP(entry); mkeys_len[i] = Z_STRLEN_PP(entry); i++; } } if (i == 0) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; efree(mkeys); efree(mkeys_len); zval_dtor(return_value); RETURN_FALSE; } /* * Enable CAS support, but only if it is currently disabled. */ if (with_cas) { orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); if (orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); } } /* * Issue the request, but collect results only if the result callback is provided. */ status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, mkeys, mkeys_len, i); /* * Restore the CAS support flag, but only if we had to turn it on. */ if (with_cas && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } efree(mkeys); efree(mkeys_len); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { zval_dtor(return_value); RETURN_FALSE; } if (fci.size != 0) { /* * We have a result callback. Iterate through the result set and invoke the * callback for each one. */ memcached_result_st result; memcached_result_create(m_obj->memc, &result); while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { if (php_memc_do_result_callback(getThis(), &fci, &fcc, &result TSRMLS_CC) < 0) { status = MEMCACHED_FAILURE; break; } } memcached_result_free(&result); /* we successfully retrieved all rows */ if (status == MEMCACHED_END) { status = MEMCACHED_SUCCESS; } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::fetch() Returns the next result from a previous delayed request */ PHP_METHOD(Memcached, fetch) { const char *res_key = NULL; size_t res_key_len = 0; const char *payload = NULL; size_t payload_len = 0; zval *value; uint32_t flags = 0; uint64_t cas = 0; memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; memcached_result_create(m_obj->memc, &result); if ((memcached_fetch_result(m_obj->memc, &result, &status)) == NULL) { php_memc_handle_error(i_obj, status TSRMLS_CC); memcached_result_free(&result); RETURN_FALSE; } payload = memcached_result_value(&result); payload_len = memcached_result_length(&result); flags = memcached_result_flags(&result); res_key = memcached_result_key_value(&result); res_key_len = memcached_result_key_length(&result); cas = memcached_result_cas(&result); MAKE_STD_ZVAL(value); if (php_memc_zval_from_payload(value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { memcached_result_free(&result); zval_ptr_dtor(&value); i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } array_init(return_value); add_assoc_stringl_ex(return_value, ZEND_STRS("key"), res_key, res_key_len, 1); add_assoc_zval_ex(return_value, ZEND_STRS("value"), value); if (cas != 0) { /* XXX: also check against ULLONG_MAX or memc_behavior */ add_assoc_double_ex(return_value, ZEND_STRS("cas"), (double)cas); } if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { add_assoc_long_ex(return_value, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); } memcached_result_free(&result); } /* }}} */ /* {{{ Memcached::fetchAll() Returns all the results from a previous delayed request */ PHP_METHOD(Memcached, fetchAll) { const char *res_key = NULL; size_t res_key_len = 0; const char *payload = NULL; size_t payload_len = 0; zval *value, *entry; uint32_t flags; uint64_t cas = 0; memcached_result_st result; memcached_return status = MEMCACHED_SUCCESS; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; array_init(return_value); memcached_result_create(m_obj->memc, &result); while ((memcached_fetch_result(m_obj->memc, &result, &status)) != NULL) { payload = memcached_result_value(&result); payload_len = memcached_result_length(&result); flags = memcached_result_flags(&result); res_key = memcached_result_key_value(&result); res_key_len = memcached_result_key_length(&result); cas = memcached_result_cas(&result); MAKE_STD_ZVAL(value); if (php_memc_zval_from_payload(value, payload, payload_len, flags, m_obj->serializer TSRMLS_CC) < 0) { memcached_result_free(&result); zval_ptr_dtor(&value); zval_dtor(return_value); i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } MAKE_STD_ZVAL(entry); array_init(entry); add_assoc_stringl_ex(entry, ZEND_STRS("key"), res_key, res_key_len, 1); add_assoc_zval_ex(entry, ZEND_STRS("value"), value); if (cas != 0) { /* XXX: also check against ULLONG_MAX or memc_behavior */ add_assoc_double_ex(entry, ZEND_STRS("cas"), (double)cas); } if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { add_assoc_long_ex(entry, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); } add_next_index_zval(return_value, entry); } memcached_result_free(&result); if (status != MEMCACHED_END && php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ Memcached::set(string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key */ PHP_METHOD(Memcached, set) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 0); } /* }}} */ /* {{{ Memcached::setByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key on the server identified by the server key */ PHP_METHOD(Memcached, setByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_SET, 1); } /* }}} */ #ifdef HAVE_MEMCACHED_TOUCH /* {{{ Memcached::touch(string key, [, int expiration ]) Sets a new expiration for the given key */ PHP_METHOD(Memcached, touch) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 0); } /* }}} */ /* {{{ Memcached::touchbyKey(string key, [, int expiration ]) Sets a new expiration for the given key */ PHP_METHOD(Memcached, touchByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_TOUCH, 1); } /* }}} */ #endif /* {{{ Memcached::setMulti(array items [, int expiration [, int udf_flags ] ]) Sets the keys/values specified in the items array */ PHP_METHOD(Memcached, setMulti) { php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::setMultiByKey(string server_key, array items [, int expiration [, int udf_flags ] ]) Sets the keys/values specified in the items array on the server identified by the given server key */ PHP_METHOD(Memcached, setMultiByKey) { php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ #define PHP_MEMC_FAILOVER_RETRY \ if (!by_key && retry < m_obj->store_retry_count) { \ switch (i_obj->rescode) { \ case MEMCACHED_HOST_LOOKUP_FAILURE: \ case MEMCACHED_CONNECTION_FAILURE: \ case MEMCACHED_CONNECTION_BIND_FAILURE: \ case MEMCACHED_WRITE_FAILURE: \ case MEMCACHED_READ_FAILURE: \ case MEMCACHED_UNKNOWN_READ_FAILURE: \ case MEMCACHED_PROTOCOL_ERROR: \ case MEMCACHED_SERVER_ERROR: \ case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: \ case MEMCACHED_TIMEOUT: \ case MEMCACHED_FAIL_UNIX_SOCKET: \ case MEMCACHED_SERVER_MARKED_DEAD: \ case MEMCACHED_SERVER_TEMPORARILY_DISABLED: \ if (memcached_server_count(m_obj->memc) > 0) { \ retry++; \ i_obj->rescode = 0; \ goto retry; \ } \ break; \ } \ } /* {{{ -- php_memc_setMulti_impl */ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { zval *entries; char *server_key = NULL; int server_key_len = 0; time_t expiration = 0; long udf_flags = 0; zval **entry; char *str_key; uint str_key_len; ulong num_key; char *payload; size_t payload_len; uint32_t flags = 0; uint32_t retry = 0; memcached_return status; char tmp_key[MEMCACHED_MAX_KEY]; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|ll", &server_key, &server_key_len, &entries, &expiration, &udf_flags) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ll", &entries, &expiration, &udf_flags) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; /* * php_memcached uses 16 bits internally to store type, compression and serialization info. * We use 16 upper bits to store user defined flags. */ if (udf_flags > 0) { if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); } } for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(entries)); zend_hash_get_current_data(Z_ARRVAL_P(entries), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(entries))) { int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(entries), &str_key, &str_key_len, &num_key, 0, NULL); if (key_type == HASH_KEY_IS_LONG) { /* Array keys are unsigned, but php integers are signed. * Keys must be converted to signed strings that match * php integers. */ assert(sizeof(tmp_key) >= sizeof(ZEND_TOSTR(LONG_MIN))); str_key_len = sprintf(tmp_key, "%ld", (long)num_key) + 1; str_key = (char *)tmp_key; } else if (key_type != HASH_KEY_IS_STRING) { continue; } flags = 0; if (m_obj->compression) { MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); } if (udf_flags > 0) { MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); } payload = php_memc_zval_to_payload(*entry, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); if (payload == NULL) { i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } retry: if (!by_key) { status = memcached_set(m_obj->memc, str_key, str_key_len-1, payload, payload_len, expiration, flags); } else { status = memcached_set_by_key(m_obj->memc, server_key, server_key_len, str_key, str_key_len-1, payload, payload_len, expiration, flags); } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { PHP_MEMC_FAILOVER_RETRY efree(payload); RETURN_FALSE; } efree(payload); } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::add(string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key, failing if the key already exists */ PHP_METHOD(Memcached, add) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 0); } /* }}} */ /* {{{ Memcached::addByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key on the server identified by the sever key, failing if the key already exists */ PHP_METHOD(Memcached, addByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_ADD, 1); } /* }}} */ /* {{{ Memcached::append(string key, mixed value) Appends the value to existing one for the key */ PHP_METHOD(Memcached, append) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 0); } /* }}} */ /* {{{ Memcached::appendByKey(string server_key, string key, mixed value) Appends the value to existing one for the key on the server identified by the server key */ PHP_METHOD(Memcached, appendByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_APPEND, 1); } /* }}} */ /* {{{ Memcached::prepend(string key, mixed value) Prepends the value to existing one for the key */ PHP_METHOD(Memcached, prepend) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 0); } /* }}} */ /* {{{ Memcached::prependByKey(string server_key, string key, mixed value) Prepends the value to existing one for the key on the server identified by the server key */ PHP_METHOD(Memcached, prependByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_PREPEND, 1); } /* }}} */ /* {{{ Memcached::replace(string key, mixed value [, int expiration [, int udf_flags ] ]) Replaces the value for the given key, failing if the key doesn't exist */ PHP_METHOD(Memcached, replace) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 0); } /* }}} */ /* {{{ Memcached::replaceByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) Replaces the value for the given key on the server identified by the server key, failing if the key doesn't exist */ PHP_METHOD(Memcached, replaceByKey) { php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_REPLACE, 1); } /* }}} */ /* {{{ -- php_memc_store_impl */ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key) { char *key = NULL; int key_len = 0; char *server_key = NULL; int server_key_len = 0; char *s_value = NULL; int s_value_len = 0; zval s_zvalue; zval *value; long expiration = 0; long udf_flags = 0; char *payload = NULL; size_t payload_len; uint32_t flags = 0; uint32_t retry = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &server_key, &server_key_len, &key, &key_len, &s_value, &s_value_len) == FAILURE) { return; } INIT_ZVAL(s_zvalue); value = &s_zvalue; ZVAL_STRINGL(value, s_value, s_value_len, 0); } else if (op == MEMC_OP_TOUCH) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &server_key, &server_key_len, &key, &key_len, &expiration) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|ll", &server_key, &server_key_len, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { return; } } } else { if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &s_value, &s_value_len) == FAILURE) { return; } INIT_ZVAL(s_zvalue); value = &s_zvalue; ZVAL_STRINGL(value, s_value, s_value_len, 0); } else if (op == MEMC_OP_TOUCH) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, &expiration) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|ll", &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { return; } } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0 || strchr(key, ' ')) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } if (m_obj->compression) { /* * When compression is enabled, we cannot do appends/prepends because that would * corrupt the compressed values. It is up to the user to fetch the value, * append/prepend new data, and store it again. */ if (op == MEMC_OP_APPEND || op == MEMC_OP_PREPEND) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot append/prepend with compression turned on"); return; } MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); } /* * php_memcached uses 16 bits internally to store type, compression and serialization info. * We use 16 upper bits to store user defined flags. */ if (udf_flags > 0) { if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); } MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); } if (op == MEMC_OP_TOUCH) { #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000016 if (memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "using touch command with binary protocol is not recommended with libmemcached versions below 1.0.16"); } #endif } else { payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); if (payload == NULL) { i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } } retry: switch (op) { case MEMC_OP_SET: if (!server_key) { status = memcached_set(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); } else { status = memcached_set_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags); } break; #ifdef HAVE_MEMCACHED_TOUCH case MEMC_OP_TOUCH: if (!server_key) { status = memcached_touch(m_obj->memc, key, key_len, expiration); } else { status = memcached_touch_by_key(m_obj->memc, server_key, server_key_len, key, key_len, expiration); } break; #endif case MEMC_OP_ADD: if (!server_key) { status = memcached_add(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); } else { status = memcached_add_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags); } break; case MEMC_OP_REPLACE: if (!server_key) { status = memcached_replace(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); } else { status = memcached_replace_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags); } break; case MEMC_OP_APPEND: if (!server_key) { status = memcached_append(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); } else { status = memcached_append_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags); } break; case MEMC_OP_PREPEND: if (!server_key) { status = memcached_prepend(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); } else { status = memcached_prepend_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags); } break; default: /* not reached */ status = 0; assert(0); break; } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { PHP_MEMC_FAILOVER_RETRY RETVAL_FALSE; } else { RETVAL_TRUE; } if (payload) { efree(payload); } } /* }}} */ /* {{{ -- php_memc_cas_impl */ static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { double cas_d; uint64_t cas; char *key = NULL; int key_len = 0; char *server_key = NULL; int server_key_len = 0; zval *value; time_t expiration = 0; long udf_flags = 0; char *payload; size_t payload_len; uint32_t flags = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dssz|ll", &cas_d, &server_key, &server_key_len, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dsz|ll", &cas_d, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0 || strchr(key, ' ')) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } DVAL_TO_LVAL(cas_d, cas); if (m_obj->compression) { MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); } /* * php_memcached uses 16 bits internally to store type, compression and serialization info. * We use 16 upper bits to store user defined flags. */ if (udf_flags > 0) { if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); } MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); } payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); if (payload == NULL) { i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; RETURN_FALSE; } if (by_key) { status = memcached_cas_by_key(m_obj->memc, server_key, server_key_len, key, key_len, payload, payload_len, expiration, flags, cas); } else { status = memcached_cas(m_obj->memc, key, key_len, payload, payload_len, expiration, flags, cas); } efree(payload); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */ PHP_METHOD(Memcached, cas) { php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key on the server identified by the server_key, failing if the cas_token doesn't match the one in memcache */ PHP_METHOD(Memcached, casByKey) { php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ Memcached::delete(string key [, int time ]) Deletes the given key */ PHP_METHOD(Memcached, delete) { php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::deleteMulti(array keys [, int time ]) Deletes the given keys */ PHP_METHOD(Memcached, deleteMulti) { php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } /* }}} */ /* {{{ Memcached::deleteByKey(string server_key, string key [, int time ]) Deletes the given key from the server identified by the server key */ PHP_METHOD(Memcached, deleteByKey) { php_memc_delete_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ Memcached::deleteMultiByKey(array keys [, int time ]) Deletes the given key from the server identified by the server key */ PHP_METHOD(Memcached, deleteMultiByKey) { php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); } /* }}} */ /* {{{ -- php_memc_delete_impl */ static void php_memc_delete_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { char *key = NULL; int key_len = 0; char *server_key = NULL; int server_key_len = 0; time_t expiration = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &server_key, &server_key_len, &key, &key_len, &expiration) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, &expiration) == FAILURE) { return; } server_key = key; server_key_len = key_len; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0 || strchr(key, ' ')) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } status = memcached_delete_by_key(m_obj->memc, server_key, server_key_len, key, key_len, expiration); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ -- php_memc_deleteMulti_impl */ static void php_memc_deleteMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) { zval *entries; char *server_key = NULL; int server_key_len = 0; time_t expiration = 0; zval **entry; memcached_return status; MEMC_METHOD_INIT_VARS; if (by_key) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|l", &server_key, &server_key_len, &entries, &expiration) == FAILURE) { return; } } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|l", &entries, &expiration) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; array_init(return_value); for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(entries)); zend_hash_get_current_data(Z_ARRVAL_P(entries), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(entries))) { if (Z_TYPE_PP(entry) != IS_STRING) { convert_to_string_ex(entry); } if (Z_STRLEN_PP(entry) == 0) { continue; } if (!by_key) { server_key = Z_STRVAL_PP(entry); server_key_len = Z_STRLEN_PP(entry); } status = memcached_delete_by_key(m_obj->memc, server_key, server_key_len, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry), expiration); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { add_assoc_long(return_value, Z_STRVAL_PP(entry), status); } else { add_assoc_bool(return_value, Z_STRVAL_PP(entry), 1); } } return; } /* }}} */ /* {{{ -- php_memc_incdec_impl */ static void php_memc_incdec_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key, zend_bool incr) { char *key, *server_key; int key_len, server_key_len; long offset = 1; uint64_t value, initial = 0; time_t expiry = 0; memcached_return status; int n_args = ZEND_NUM_ARGS(); uint32_t retry = 0; MEMC_METHOD_INIT_VARS; if (!by_key) { if (zend_parse_parameters(n_args TSRMLS_CC, "s|lll", &key, &key_len, &offset, &initial, &expiry) == FAILURE) { return; } } else { if (zend_parse_parameters(n_args TSRMLS_CC, "ss|lll", &server_key, &server_key_len, &key, &key_len, &offset, &initial, &expiry) == FAILURE) { return; } } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (key_len == 0 || strchr(key, ' ')) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } if (offset < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset has to be > 0"); RETURN_FALSE; } retry: if ((!by_key && n_args < 3) || (by_key && n_args < 4)) { if (by_key) { if (incr) { status = memcached_increment_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, &value); } else { status = memcached_decrement_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, &value); } } else { if (incr) { status = memcached_increment(m_obj->memc, key, key_len, (unsigned int)offset, &value); } else { status = memcached_decrement(m_obj->memc, key, key_len, (unsigned int)offset, &value); } } } else { if (!memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Initial value is only supported with binary protocol"); RETURN_FALSE; } if (by_key) { if (incr) { status = memcached_increment_with_initial_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, initial, expiry, &value); } else { status = memcached_decrement_with_initial_by_key(m_obj->memc, server_key, server_key_len, key, key_len, (unsigned int)offset, initial, expiry, &value); } } else { if (incr) { status = memcached_increment_with_initial(m_obj->memc, key, key_len, (unsigned int)offset, initial, expiry, &value); } else { status = memcached_decrement_with_initial(m_obj->memc, key, key_len, (unsigned int)offset, initial, expiry, &value); } } } if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { PHP_MEMC_FAILOVER_RETRY RETURN_FALSE; } RETURN_LONG((long)value); } /* }}} */ /* {{{ Memcached::increment(string key [, int delta [, initial_value [, expiry time ] ] ]) Increments the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, increment) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1); } /* }}} */ /* {{{ Memcached::decrement(string key [, int delta [, initial_value [, expiry time ] ] ]) Decrements the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, decrement) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); } /* }}} */ /* {{{ Memcached::decrementByKey(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ]) Decrements by server the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, decrementByKey) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0); } /* }}} */ /* {{{ Memcached::increment(string server_key, string key [, int delta [, initial_value [, expiry time ] ] ]) Increments by server the value for the given key by delta, defaulting to 1 */ PHP_METHOD(Memcached, incrementByKey) { php_memc_incdec_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1); } /* }}} */ /* {{{ Memcached::addServer(string hostname, int port [, int weight ]) Adds the given memcache server to the list */ PHP_METHOD(Memcached, addServer) { char *host; int host_len; long port, weight = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", &host, &host_len, &port, &weight) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x01000002 if (host[0] == '/') { /* unix domain socket */ status = memcached_server_add_unix_socket_with_weight(m_obj->memc, host, weight); } else if (memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_USE_UDP)) { status = memcached_server_add_udp_with_weight(m_obj->memc, host, port, weight); } else { status = memcached_server_add_with_weight(m_obj->memc, host, port, weight); } #else status = memcached_server_add_with_weight(m_obj->memc, host, port, weight); #endif if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::addServers(array servers) Adds the given memcache servers to the server list */ PHP_METHOD(Memcached, addServers) { zval *servers; zval **entry; zval **z_host, **z_port, **z_weight = NULL; uint32_t weight = 0; int entry_size, i = 0; memcached_server_st *list = NULL; memcached_return status; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &servers) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(servers)), i = 0; zend_hash_get_current_data(Z_ARRVAL_P(servers), (void **)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(servers)), i++) { if (Z_TYPE_PP(entry) != IS_ARRAY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "server list entry #%d is not an array", i+1); continue; } entry_size = zend_hash_num_elements(Z_ARRVAL_PP(entry)); if (entry_size > 1) { zend_hash_internal_pointer_reset(Z_ARRVAL_PP(entry)); /* Check that we have a host */ if (zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void **)&z_host) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get server host for entry #%d", i+1); continue; } /* Check that we have a port */ if (zend_hash_move_forward(Z_ARRVAL_PP(entry)) == FAILURE || zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void **)&z_port) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get server port for entry #%d", i+1); continue; } convert_to_string_ex(z_host); convert_to_long_ex(z_port); weight = 0; if (entry_size > 2) { /* Try to get weight */ if (zend_hash_move_forward(Z_ARRVAL_PP(entry)) == FAILURE || zend_hash_get_current_data(Z_ARRVAL_PP(entry), (void **)&z_weight) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not get server weight for entry #%d", i+1); } convert_to_long_ex(z_weight); weight = Z_LVAL_PP(z_weight); } list = memcached_server_list_append_with_weight(list, Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port), weight, &status); if (php_memc_handle_error(i_obj, status TSRMLS_CC) == 0) { continue; } } /* catch-all for all errors */ php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not add entry #%d to the server list", i+1); } status = memcached_server_push(m_obj->memc, list); memcached_server_list_free(list); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::getServerList() Returns the list of the memcache servers in use */ PHP_METHOD(Memcached, getServerList) { struct callbackContext context = {0}; memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; callbacks[0] = php_memc_do_serverlist_callback; array_init(return_value); context.return_value = return_value; memcached_server_cursor(m_obj->memc, callbacks, &context, 1); } /* }}} */ /* {{{ Memcached::getServerByKey(string server_key) Returns the server identified by the given server key */ PHP_METHOD(Memcached, getServerByKey) { char *server_key; int server_key_len; php_memcached_instance_st server_instance; memcached_return error; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &server_key, &server_key_len) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; if (server_key_len == 0 || strchr(server_key, ' ')) { i_obj->rescode = MEMCACHED_BAD_KEY_PROVIDED; RETURN_FALSE; } server_instance = memcached_server_by_key(m_obj->memc, server_key, server_key_len, &error); if (server_instance == NULL) { php_memc_handle_error(i_obj, error TSRMLS_CC); RETURN_FALSE; } array_init(return_value); add_assoc_string(return_value, "host", (char*) memcached_server_name(server_instance), 1); add_assoc_long(return_value, "port", memcached_server_port(server_instance)); add_assoc_long(return_value, "weight", 0); } /* }}} */ /* {{{ Memcached::resetServerList() Reset the server list in use */ PHP_METHOD(Memcached, resetServerList) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; memcached_servers_reset(m_obj->memc); RETURN_TRUE; } /* }}} */ /* {{{ Memcached::quit() Close any open connections */ PHP_METHOD(Memcached, quit) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; memcached_quit(m_obj->memc); RETURN_TRUE; } /* }}} */ /* {{{ Memcached::flushBuffers() Flush and senf buffered commands */ PHP_METHOD(Memcached, flushBuffers) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_BOOL(memcached_flush_buffers(m_obj->memc) == MEMCACHED_SUCCESS); } /* }}} */ #ifdef HAVE_LIBMEMCACHED_CHECK_CONFIGURATION /* {{{ Memcached::getLastErrorMessage() Returns the last error message that occurred */ PHP_METHOD(Memcached, getLastErrorMessage) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_STRING(memcached_last_error_message(m_obj->memc), 1); } /* }}} */ /* {{{ Memcached::getLastErrorCode() Returns the last error code that occurred */ PHP_METHOD(Memcached, getLastErrorCode) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_LONG(memcached_last_error(m_obj->memc)); } /* }}} */ /* {{{ Memcached::getLastErrorErrno() Returns the last error errno that occurred */ PHP_METHOD(Memcached, getLastErrorErrno) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_LONG(memcached_last_error_errno(m_obj->memc)); } /* }}} */ #endif /* {{{ Memcached::getLastDisconnectedServer() Returns the last disconnected server Was added in 0.34 according to libmemcached's Changelog */ PHP_METHOD(Memcached, getLastDisconnectedServer) { php_memcached_instance_st server_instance; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; server_instance = memcached_server_get_last_disconnect(m_obj->memc); if (server_instance == NULL) { RETURN_FALSE; } array_init(return_value); add_assoc_string(return_value, "host", (char*) memcached_server_name(server_instance), 1); add_assoc_long(return_value, "port", memcached_server_port(server_instance)); } /* }}} */ /* {{{ Memcached::getStats() Returns statistics for the memcache servers */ PHP_METHOD(Memcached, getStats) { memcached_stat_st *stats; memcached_return status; struct callbackContext context = {0}; memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; if (memcached_server_count(m_obj->memc) == 0) { array_init(return_value); return; } stats = memcached_stat(m_obj->memc, NULL, &status); php_memc_handle_error(i_obj, status TSRMLS_CC); if (stats == NULL) { RETURN_FALSE; } else if (status != MEMCACHED_SUCCESS && status != MEMCACHED_SOME_ERRORS) { memcached_stat_free(m_obj->memc, stats); RETURN_FALSE; } array_init(return_value); callbacks[0] = php_memc_do_stats_callback; context.i = 0; context.stats = stats; context.return_value = return_value; memcached_server_cursor(m_obj->memc, callbacks, &context, 1); memcached_stat_free(m_obj->memc, stats); } /* }}} */ /* {{{ Memcached::getVersion() Returns the version of each memcached server in the pool */ PHP_METHOD(Memcached, getVersion) { memcached_return status = MEMCACHED_SUCCESS; struct callbackContext context = {0}; memcached_server_function callbacks[1]; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; array_init(return_value); status = memcached_version(m_obj->memc); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { zval_dtor(return_value); RETURN_FALSE; } callbacks[0] = php_memc_do_version_callback; context.return_value = return_value; memcached_server_cursor(m_obj->memc, callbacks, &context, 1); } /* }}} */ /* {{{ Memcached::getAllKeys() Returns the keys stored on all the servers */ static memcached_return php_memc_dump_func_callback(const memcached_st *ptr __attribute__((unused)), \ const char *key, size_t key_length, void *context) { zval *ctx = (zval*) context; add_next_index_string(ctx, (char*) key, 1); return MEMCACHED_SUCCESS; } PHP_METHOD(Memcached, getAllKeys) { memcached_return rc; memcached_dump_func callback[1]; MEMC_METHOD_INIT_VARS; callback[0] = php_memc_dump_func_callback; MEMC_METHOD_FETCH_OBJECT; array_init(return_value); rc = memcached_dump(m_obj->memc, callback, return_value, 1); if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ Memcached::flush([ int delay ]) Flushes the data on all the servers */ static PHP_METHOD(Memcached, flush) { time_t delay = 0; memcached_return status; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &delay) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; status = memcached_flush(m_obj->memc, delay); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ Memcached::getOption(int option) Returns the value for the given option constant */ static PHP_METHOD(Memcached, getOption) { long option; uint64_t result; memcached_behavior flag; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &option) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; switch (option) { case MEMC_OPT_COMPRESSION_TYPE: RETURN_LONG(m_obj->compression_type); case MEMC_OPT_COMPRESSION: RETURN_BOOL(m_obj->compression); case MEMC_OPT_PREFIX_KEY: { memcached_return retval; char *result; result = memcached_callback_get(m_obj->memc, MEMCACHED_CALLBACK_PREFIX_KEY, &retval); if (retval == MEMCACHED_SUCCESS && result) { #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX == 0x00049000 RETURN_STRINGL(result, strlen(result) - 1, 1); #else RETURN_STRING(result, 1); #endif } else { RETURN_EMPTY_STRING(); } } case MEMC_OPT_SERIALIZER: RETURN_LONG((long)m_obj->serializer); break; case MEMC_OPT_STORE_RETRY_COUNT: RETURN_LONG((long)m_obj->store_retry_count); break; case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: if (memcached_server_count(m_obj->memc) == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "no servers defined"); return; } default: /* * Assume that it's a libmemcached behavior option. */ flag = (memcached_behavior) option; result = memcached_behavior_get(m_obj->memc, flag); RETURN_LONG((long)result); } } /* }}} */ static int php_memc_set_option(php_memc_t *i_obj, long option, zval *value TSRMLS_DC) { memcached_return rc = MEMCACHED_FAILURE; memcached_behavior flag; struct memc_obj *m_obj = i_obj->obj; switch (option) { case MEMC_OPT_COMPRESSION: convert_to_long(value); m_obj->compression = Z_LVAL_P(value) ? 1 : 0; break; case MEMC_OPT_COMPRESSION_TYPE: convert_to_long(value); if (Z_LVAL_P(value) == COMPRESSION_TYPE_FASTLZ || Z_LVAL_P(value) == COMPRESSION_TYPE_ZLIB) { m_obj->compression_type = Z_LVAL_P(value); } else { /* invalid compression type */ i_obj->rescode = MEMCACHED_INVALID_ARGUMENTS; return 0; } break; case MEMC_OPT_PREFIX_KEY: { char *key; #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX == 0x00049000 char tmp[MEMCACHED_PREFIX_KEY_MAX_SIZE - 1]; #endif convert_to_string(value); if (Z_STRLEN_P(value) == 0) { key = NULL; } else { /* work-around a bug in libmemcached in version 0.49 that truncates the trailing character of the key prefix, to avoid the issue we pad it with a '0' */ #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX == 0x00049000 snprintf(tmp, sizeof(tmp), "%s0", Z_STRVAL_P(value)); key = tmp; #else key = Z_STRVAL_P(value); #endif } if (memcached_callback_set(m_obj->memc, MEMCACHED_CALLBACK_PREFIX_KEY, key) == MEMCACHED_BAD_KEY_PROVIDED) { i_obj->rescode = MEMCACHED_INVALID_ARGUMENTS; php_error_docref(NULL TSRMLS_CC, E_WARNING, "bad key provided"); return 0; } } break; case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: flag = (memcached_behavior) option; convert_to_long(value); rc = memcached_behavior_set(m_obj->memc, flag, (uint64_t) Z_LVAL_P(value)); if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "error setting memcached option: %s", memcached_strerror (m_obj->memc, rc)); return 0; } /* * This is necessary because libmemcached does not reset hash/distribution * options on false case, like it does for MEMCACHED_BEHAVIOR_KETAMA * (non-weighted) case. We have to clean up ourselves. */ if (!Z_LVAL_P(value)) { #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX > 0x00037000 (void)memcached_behavior_set_key_hash(m_obj->memc, MEMCACHED_HASH_DEFAULT); (void)memcached_behavior_set_distribution_hash(m_obj->memc, MEMCACHED_HASH_DEFAULT); (void)memcached_behavior_set_distribution(m_obj->memc, MEMCACHED_DISTRIBUTION_MODULA); #else m_obj->memc->hash = 0; m_obj->memc->distribution = 0; #endif } break; case MEMC_OPT_SERIALIZER: { convert_to_long(value); /* igbinary serializer */ #ifdef HAVE_MEMCACHED_IGBINARY if (Z_LVAL_P(value) == SERIALIZER_IGBINARY) { m_obj->serializer = SERIALIZER_IGBINARY; } else #endif #ifdef HAVE_JSON_API if (Z_LVAL_P(value) == SERIALIZER_JSON) { m_obj->serializer = SERIALIZER_JSON; } else if (Z_LVAL_P(value) == SERIALIZER_JSON_ARRAY) { m_obj->serializer = SERIALIZER_JSON_ARRAY; } else #endif /* msgpack serializer */ #ifdef HAVE_MEMCACHED_MSGPACK if (Z_LVAL_P(value) == SERIALIZER_MSGPACK) { m_obj->serializer = SERIALIZER_MSGPACK; } else #endif /* php serializer */ if (Z_LVAL_P(value) == SERIALIZER_PHP) { m_obj->serializer = SERIALIZER_PHP; } else { m_obj->serializer = SERIALIZER_PHP; i_obj->rescode = MEMCACHED_INVALID_ARGUMENTS; php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid serializer provided"); return 0; } break; } case MEMC_OPT_STORE_RETRY_COUNT: convert_to_long(value); m_obj->store_retry_count = Z_LVAL_P(value); break; default: /* * Assume that it's a libmemcached behavior option. */ if (option < 0) { rc = MEMCACHED_INVALID_ARGUMENTS; } else { flag = (memcached_behavior) option; convert_to_long(value); if (flag < MEMCACHED_BEHAVIOR_MAX) { rc = memcached_behavior_set(m_obj->memc, flag, (uint64_t) Z_LVAL_P(value)); } else { rc = MEMCACHED_INVALID_ARGUMENTS; } } if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "error setting memcached option: %s", memcached_strerror (m_obj->memc, rc)); return 0; } break; } return 1; } static uint32_t *s_zval_to_uint32_array (zval *input, size_t *num_elements TSRMLS_DC) { zval **ppzval; uint32_t *retval; size_t i = 0; *num_elements = zend_hash_num_elements(Z_ARRVAL_P(input)); if (!*num_elements) { return NULL; } retval = ecalloc(*num_elements, sizeof(uint32_t)); for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(input)); zend_hash_get_current_data(Z_ARRVAL_P(input), (void **) &ppzval) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(input)), i++) { long value = 0; if (Z_TYPE_PP(ppzval) == IS_LONG) { value = Z_LVAL_PP(ppzval); } else { zval tmp_zval, *tmp_pzval; tmp_zval = **ppzval; zval_copy_ctor(&tmp_zval); tmp_pzval = &tmp_zval; convert_to_long(tmp_pzval); value = (Z_LVAL_P(tmp_pzval) > 0) ? Z_LVAL_P(tmp_pzval) : 0; zval_dtor(tmp_pzval); } if (value < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "the map must contain positive integers"); efree (retval); *num_elements = 0; return NULL; } retval [i] = (uint32_t) value; } return retval; } /* {{{ Memcached::setBucket(array host_map, array forward_map, integer replicas) Sets the memcached virtual buckets */ PHP_METHOD(Memcached, setBucket) { zval *zserver_map; zval *zforward_map = NULL; long replicas = 0; zend_bool retval = 1; uint32_t *server_map = NULL, *forward_map = NULL; size_t server_map_len = 0, forward_map_len = 0; memcached_return rc; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa!l", &zserver_map, &zforward_map, &replicas) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; if (zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "server map cannot be empty"); RETURN_FALSE; } if (zforward_map && zend_hash_num_elements (Z_ARRVAL_P(zserver_map)) != zend_hash_num_elements (Z_ARRVAL_P(zforward_map))) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "forward_map length must match the server_map length"); RETURN_FALSE; } if (replicas < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "replicas must be larger than zero"); RETURN_FALSE; } server_map = s_zval_to_uint32_array (zserver_map, &server_map_len TSRMLS_CC); if (!server_map) { RETURN_FALSE; } if (zforward_map) { forward_map = s_zval_to_uint32_array (zforward_map, &forward_map_len TSRMLS_CC); if (!forward_map) { efree (server_map); RETURN_FALSE; } } rc = memcached_bucket_set (m_obj->memc, server_map, forward_map, (uint32_t) server_map_len, replicas); if (php_memc_handle_error(i_obj, rc TSRMLS_CC) < 0) { retval = 0;; } efree(server_map); if (forward_map) { efree(forward_map); } RETURN_BOOL(retval); } /* }}} */ /* {{{ Memcached::setOptions(array options) Sets the value for the given option constant */ static PHP_METHOD(Memcached, setOptions) { zval *options; zend_bool ok = 1; uint key_len; char *key; ulong key_index; zval **value; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &options) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(options)); zend_hash_get_current_data(Z_ARRVAL_P(options), (void *) &value) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(options))) { if (zend_hash_get_current_key_ex(Z_ARRVAL_P(options), &key, &key_len, &key_index, 0, NULL) == HASH_KEY_IS_LONG) { zval copy = **value; zval_copy_ctor(©); INIT_PZVAL(©); if (!php_memc_set_option(i_obj, (long) key_index, © TSRMLS_CC)) { ok = 0; } zval_dtor(©); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid configuration option"); ok = 0; } } RETURN_BOOL(ok); } /* }}} */ /* {{{ Memcached::setOption(int option, mixed value) Sets the value for the given option constant */ static PHP_METHOD(Memcached, setOption) { long option; zval *value; MEMC_METHOD_INIT_VARS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz/", &option, &value) == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_BOOL(php_memc_set_option(i_obj, option, value TSRMLS_CC)); } /* }}} */ #ifdef HAVE_MEMCACHED_SASL /* {{{ Memcached::setSaslAuthData(string user, string pass) Sets sasl credentials */ static PHP_METHOD(Memcached, setSaslAuthData) { MEMC_METHOD_INIT_VARS; memcached_return status; char *user, *pass; int user_len, pass_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &user, &user_len, &pass, &pass_len) == FAILURE) { return; } if (!MEMC_G(use_sasl)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "SASL support (memcached.use_sasl) isn't enabled in php.ini"); RETURN_FALSE; } MEMC_METHOD_FETCH_OBJECT; if (!memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "SASL is only supported with binary protocol"); RETURN_FALSE; } m_obj->has_sasl_data = 1; status = memcached_set_sasl_auth_data(m_obj->memc, user, pass); if (php_memc_handle_error(i_obj, status TSRMLS_CC) < 0) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ #endif /* HAVE_MEMCACHED_SASL */ /* {{{ Memcached::getResultCode() Returns the result code from the last operation */ static PHP_METHOD(Memcached, getResultCode) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_LONG(i_obj->rescode); } /* }}} */ /* {{{ Memcached::getResultMessage() Returns the result message from the last operation */ static PHP_METHOD(Memcached, getResultMessage) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; switch (i_obj->rescode) { case MEMC_RES_PAYLOAD_FAILURE: RETURN_STRING("PAYLOAD FAILURE", 1); break; case MEMCACHED_ERRNO: case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: case MEMCACHED_UNKNOWN_READ_FAILURE: if (i_obj->memc_errno) { char *str; int str_len; str_len = spprintf(&str, 0, "%s: %s", memcached_strerror(m_obj->memc, (memcached_return)i_obj->rescode), strerror(i_obj->memc_errno)); RETURN_STRINGL(str, str_len, 0); } /* Fall through */ default: RETURN_STRING(memcached_strerror(m_obj->memc, (memcached_return)i_obj->rescode), 1); break; } } /* }}} */ /* {{{ Memcached::isPersistent() Returns the true if instance uses a persistent connection */ static PHP_METHOD(Memcached, isPersistent) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_BOOL(i_obj->is_persistent); } /* }}} */ /* {{{ Memcached::isPristine() Returns the true if instance is recently created */ static PHP_METHOD(Memcached, isPristine) { MEMC_METHOD_INIT_VARS; if (zend_parse_parameters_none() == FAILURE) { return; } MEMC_METHOD_FETCH_OBJECT; RETURN_BOOL(i_obj->is_pristine); } /* }}} */ /**************************************** Internal support code ****************************************/ /* {{{ constructor/destructor */ static void php_memc_destroy(struct memc_obj *m_obj, zend_bool persistent TSRMLS_DC) { #if HAVE_MEMCACHED_SASL if (m_obj->has_sasl_data) { memcached_destroy_sasl_auth_data(m_obj->memc); } #endif if (m_obj->memc) { memcached_free(m_obj->memc); } pefree(m_obj, persistent); } static void php_memc_free_storage(php_memc_t *i_obj TSRMLS_DC) { zend_object_std_dtor(&i_obj->zo TSRMLS_CC); if (i_obj->obj && !i_obj->is_persistent) { php_memc_destroy(i_obj->obj, 0 TSRMLS_CC); } i_obj->obj = NULL; efree(i_obj); } zend_object_value php_memc_new(zend_class_entry *ce TSRMLS_DC) { zend_object_value retval; php_memc_t *i_obj; i_obj = ecalloc(1, sizeof(*i_obj)); zend_object_std_init( &i_obj->zo, ce TSRMLS_CC ); #if PHP_VERSION_ID >= 50400 object_properties_init( (zend_object *) i_obj, ce); #else { zval *tmp; zend_hash_copy(i_obj->zo.properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); } #endif retval.handle = zend_objects_store_put(i_obj, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)php_memc_free_storage, NULL TSRMLS_CC); retval.handlers = &memcached_object_handlers; return retval; } #ifdef HAVE_MEMCACHED_PROTOCOL static void php_memc_server_free_storage(php_memc_server_t *intern TSRMLS_DC) { zend_object_std_dtor(&intern->zo TSRMLS_CC); efree (intern); } zend_object_value php_memc_server_new(zend_class_entry *ce TSRMLS_DC) { zend_object_value retval; php_memc_server_t *intern; zval *tmp; intern = ecalloc(1, sizeof(php_memc_server_t)); zend_object_std_init (&intern->zo, ce TSRMLS_CC); #if PHP_VERSION_ID >= 50400 object_properties_init( (zend_object *) intern, ce); #else zend_hash_copy(intern->zo.properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); #endif intern->handler = php_memc_proto_handler_new (); retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t)php_memc_server_free_storage, NULL TSRMLS_CC); retval.handlers = &memcached_server_object_handlers; return retval; } #endif ZEND_RSRC_DTOR_FUNC(php_memc_dtor) { if (rsrc->ptr) { struct memc_obj *m_obj = (struct memc_obj *)rsrc->ptr; php_memc_destroy(m_obj, 1 TSRMLS_CC); rsrc->ptr = NULL; } } ZEND_RSRC_DTOR_FUNC(php_memc_sess_dtor) { if (rsrc->ptr) { memcached_sess *memc_sess = (memcached_sess *)rsrc->ptr; memcached_free(memc_sess->memc_sess); pefree(rsrc->ptr, 1); rsrc->ptr = NULL; } } /* }}} */ /* {{{ internal API functions */ static memcached_return php_memc_do_serverlist_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) { struct callbackContext* context = (struct callbackContext*) in_context; zval *array; MAKE_STD_ZVAL(array); array_init(array); add_assoc_string(array, "host", (char*) memcached_server_name(instance), 1); add_assoc_long(array, "port", memcached_server_port(instance)); /* * API does not allow to get at this field. add_assoc_long(array, "weight", instance->weight); */ add_next_index_zval(context->return_value, array); return MEMCACHED_SUCCESS; } static memcached_return php_memc_do_stats_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) { char *hostport = NULL; int hostport_len; struct callbackContext* context = (struct callbackContext*) in_context; zval *entry; hostport_len = spprintf(&hostport, 0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance)); MAKE_STD_ZVAL(entry); array_init(entry); add_assoc_long(entry, "pid", context->stats[context->i].pid); add_assoc_long(entry, "uptime", context->stats[context->i].uptime); add_assoc_long(entry, "threads", context->stats[context->i].threads); add_assoc_long(entry, "time", context->stats[context->i].time); add_assoc_long(entry, "pointer_size", context->stats[context->i].pointer_size); add_assoc_long(entry, "rusage_user_seconds", context->stats[context->i].rusage_user_seconds); add_assoc_long(entry, "rusage_user_microseconds", context->stats[context->i].rusage_user_microseconds); add_assoc_long(entry, "rusage_system_seconds", context->stats[context->i].rusage_system_seconds); add_assoc_long(entry, "rusage_system_microseconds", context->stats[context->i].rusage_system_microseconds); add_assoc_long(entry, "curr_items", context->stats[context->i].curr_items); add_assoc_long(entry, "total_items", context->stats[context->i].total_items); add_assoc_long(entry, "limit_maxbytes", context->stats[context->i].limit_maxbytes); add_assoc_long(entry, "curr_connections", context->stats[context->i].curr_connections); add_assoc_long(entry, "total_connections", context->stats[context->i].total_connections); add_assoc_long(entry, "connection_structures", context->stats[context->i].connection_structures); add_assoc_long(entry, "bytes", context->stats[context->i].bytes); add_assoc_long(entry, "cmd_get", context->stats[context->i].cmd_get); add_assoc_long(entry, "cmd_set", context->stats[context->i].cmd_set); add_assoc_long(entry, "get_hits", context->stats[context->i].get_hits); add_assoc_long(entry, "get_misses", context->stats[context->i].get_misses); add_assoc_long(entry, "evictions", context->stats[context->i].evictions); add_assoc_long(entry, "bytes_read", context->stats[context->i].bytes_read); add_assoc_long(entry, "bytes_written", context->stats[context->i].bytes_written); add_assoc_stringl(entry, "version", context->stats[context->i].version, strlen(context->stats[context->i].version), 1); add_assoc_zval_ex(context->return_value, hostport, hostport_len+1, entry); efree(hostport); /* Increment the server count in our context structure. Failure to do so will cause only the stats for the last server to get displayed. */ context->i++; return MEMCACHED_SUCCESS; } static memcached_return php_memc_do_version_callback(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context) { char *hostport = NULL; char version[16]; int hostport_len, version_len; struct callbackContext* context = (struct callbackContext*) in_context; hostport_len = spprintf(&hostport, 0, "%s:%d", memcached_server_name(instance), memcached_server_port(instance)); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000009 version_len = snprintf(version, sizeof(version), "%d.%d.%d", memcached_server_major_version(instance), memcached_server_minor_version(instance), memcached_server_micro_version(instance)); #else version_len = snprintf(version, sizeof(version), "%d.%d.%d", instance->major_version, instance->minor_version, instance->micro_version); #endif add_assoc_stringl_ex(context->return_value, hostport, hostport_len+1, version, version_len, 1); efree(hostport); return MEMCACHED_SUCCESS; } static int php_memc_handle_error(php_memc_t *i_obj, memcached_return status TSRMLS_DC) { int result = 0; switch (status) { case MEMCACHED_SUCCESS: case MEMCACHED_STORED: case MEMCACHED_DELETED: case MEMCACHED_STAT: result = 0; i_obj->memc_errno = 0; break; case MEMCACHED_END: case MEMCACHED_BUFFERED: i_obj->rescode = status; i_obj->memc_errno = 0; result = 0; break; case MEMCACHED_SOME_ERRORS: i_obj->rescode = status; #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 i_obj->memc_errno = memcached_last_error_errno(i_obj->obj->memc); #else i_obj->memc_errno = i_obj->obj->memc->cached_errno; /* Hnngghgh! */ #endif result = 0; break; default: i_obj->rescode = status; #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 i_obj->memc_errno = memcached_last_error_errno(i_obj->obj->memc); #else i_obj->memc_errno = i_obj->obj->memc->cached_errno; /* Hnngghgh! */ #endif result = -1; break; } return result; } static char *s_compress_value (enum memcached_compression_type compression_type, const char *payload, size_t *payload_len, uint32_t *flags TSRMLS_DC) { /* status */ zend_bool compress_status = 0; /* Additional 5% for the data */ size_t buffer_size = (size_t) (((double) *payload_len * 1.05) + 1.0); char *buffer = emalloc(sizeof(uint32_t) + buffer_size); /* Store compressed size here */ size_t compressed_size = 0; uint32_t plen = *payload_len; /* Copy the uin32_t at the beginning */ memcpy(buffer, &plen, sizeof(uint32_t)); buffer += sizeof(uint32_t); switch (compression_type) { case COMPRESSION_TYPE_FASTLZ: compress_status = ((compressed_size = fastlz_compress(payload, *payload_len, buffer)) > 0); MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_FASTLZ); break; case COMPRESSION_TYPE_ZLIB: /* ZLIB returns the compressed size in this buffer */ compressed_size = buffer_size; compress_status = (compress((Bytef *)buffer, &compressed_size, (Bytef *)payload, *payload_len) == Z_OK); MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_ZLIB); break; default: compress_status = 0; break; } buffer -= sizeof(uint32_t); *payload_len = compressed_size + sizeof(uint32_t); if (!compress_status) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not compress value"); MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); efree (buffer); *payload_len = 0; return NULL; } else if (*payload_len > (compressed_size * MEMC_G(compression_factor))) { MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); efree (buffer); *payload_len = 0; return NULL; } return buffer; } static zend_bool s_serialize_value (enum memcached_serializer serializer, zval *value, smart_str *buf, uint32_t *flags TSRMLS_DC) { switch (serializer) { /* Igbinary serialization */ #ifdef HAVE_MEMCACHED_IGBINARY case SERIALIZER_IGBINARY: if (igbinary_serialize((uint8_t **) &buf->c, &buf->len, value TSRMLS_CC) != 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not serialize value with igbinary"); return 0; } MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_IGBINARY); break; #endif /* JSON serialization */ #ifdef HAVE_JSON_API case SERIALIZER_JSON: case SERIALIZER_JSON_ARRAY: { #if HAVE_JSON_API_5_2 php_json_encode(buf, value TSRMLS_CC); #elif HAVE_JSON_API_5_3 php_json_encode(buf, value, 0 TSRMLS_CC); /* options */ #endif MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_JSON); } break; #endif /* msgpack serialization */ #ifdef HAVE_MEMCACHED_MSGPACK case SERIALIZER_MSGPACK: php_msgpack_serialize(buf, value TSRMLS_CC); if (!buf->c) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not serialize value with msgpack"); return 0; } MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_MSGPACK); break; #endif /* PHP serialization */ default: { php_serialize_data_t var_hash; PHP_VAR_SERIALIZE_INIT(var_hash); php_var_serialize(buf, &value, &var_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(var_hash); if (!buf->c) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not serialize value"); return 0; } MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_SERIALIZED); } break; } /* Check for exceptions caused by serializers */ if (EG(exception) && buf->len) { return 0; } return 1; } static char *php_memc_zval_to_payload(zval *value, size_t *payload_len, uint32_t *flags, enum memcached_serializer serializer, enum memcached_compression_type compression_type TSRMLS_DC) { const char *pl; size_t pl_len = 0; char *payload = NULL; smart_str buf = {0}; char tmp[40] = {0}; switch (Z_TYPE_P(value)) { case IS_STRING: pl = Z_STRVAL_P(value); pl_len = Z_STRLEN_P(value); MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_STRING); break; case IS_LONG: pl_len = sprintf(tmp, "%ld", Z_LVAL_P(value)); pl = tmp; MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_LONG); break; case IS_DOUBLE: php_memcached_g_fmt(tmp, Z_DVAL_P(value)); pl = tmp; pl_len = strlen(tmp); MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_DOUBLE); break; case IS_BOOL: if (Z_BVAL_P(value)) { pl_len = 1; tmp[0] = '1'; tmp[1] = '\0'; } else { pl_len = 0; tmp[0] = '\0'; } pl = tmp; MEMC_VAL_SET_TYPE(*flags, MEMC_VAL_IS_BOOL); break; default: if (!s_serialize_value (serializer, value, &buf, flags TSRMLS_CC)) { smart_str_free (&buf); return NULL; } pl = buf.c; pl_len = buf.len; break; } /* turn off compression for values below the threshold */ if (MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED) && pl_len < MEMC_G(compression_threshold)) { MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); } /* If we have compression flag, compress the value */ if (MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED)) { /* status */ *payload_len = pl_len; payload = s_compress_value (compression_type, pl, payload_len, flags TSRMLS_CC); } /* If compression failed or value is below threshold we just use plain value */ if (!payload || !MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED)) { *payload_len = (uint32_t) pl_len; payload = estrndup(pl, pl_len); } if (buf.len) { smart_str_free(&buf); } return payload; } static char *s_decompress_value (const char *payload, size_t *payload_len, uint32_t flags TSRMLS_DC) { char *buffer = NULL; uint32_t len; unsigned long length; zend_bool decompress_status = 0; /* Stored with newer memcached extension? */ if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ) || MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB)) { /* This is copied from Ilia's patch */ memcpy(&len, payload, sizeof(uint32_t)); buffer = emalloc(len + 1); *payload_len -= sizeof(uint32_t); payload += sizeof(uint32_t); length = len; if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ)) { decompress_status = ((length = fastlz_decompress(payload, *payload_len, buffer, len)) > 0); } else if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB)) { decompress_status = (uncompress((Bytef *)buffer, &length, (Bytef *)payload, *payload_len) == Z_OK); } } /* Fall back to 'old style decompression' */ if (!decompress_status) { unsigned int factor = 1, maxfactor = 16; int status; do { length = (unsigned long)*payload_len * (1 << factor++); buffer = erealloc(buffer, length + 1); memset(buffer, 0, length + 1); status = uncompress((Bytef *)buffer, (uLongf *)&length, (const Bytef *)payload, *payload_len); } while ((status==Z_BUF_ERROR) && (factor < maxfactor)); if (status == Z_OK) { decompress_status = 1; } } if (!decompress_status) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not decompress value"); efree(buffer); return NULL; } buffer [length] = '\0'; *payload_len = length; return buffer; } static zend_bool s_unserialize_value (enum memcached_serializer serializer, int val_type, zval *value, const char *payload, size_t payload_len TSRMLS_DC) { switch (val_type) { case MEMC_VAL_IS_SERIALIZED: { const char *payload_tmp = payload; php_unserialize_data_t var_hash; PHP_VAR_UNSERIALIZE_INIT(var_hash); if (!php_var_unserialize(&value, (const unsigned char **)&payload_tmp, (const unsigned char *)payload_tmp + payload_len, &var_hash TSRMLS_CC)) { ZVAL_FALSE(value); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value"); return 0; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); } break; case MEMC_VAL_IS_IGBINARY: #ifdef HAVE_MEMCACHED_IGBINARY if (igbinary_unserialize((uint8_t *)payload, payload_len, &value TSRMLS_CC)) { ZVAL_FALSE(value); php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value with igbinary"); return 0; } #else ZVAL_FALSE(value); php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no igbinary support"); return 0; #endif break; case MEMC_VAL_IS_JSON: #ifdef HAVE_JSON_API # if HAVE_JSON_API_5_2 php_json_decode(value, payload, payload_len, (serializer == SERIALIZER_JSON_ARRAY) TSRMLS_CC); # elif HAVE_JSON_API_5_3 php_json_decode(value, payload, payload_len, (serializer == SERIALIZER_JSON_ARRAY), JSON_PARSER_DEFAULT_DEPTH TSRMLS_CC); # endif #else ZVAL_FALSE(value); php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no json support"); return 0; #endif break; case MEMC_VAL_IS_MSGPACK: #ifdef HAVE_MEMCACHED_MSGPACK php_msgpack_unserialize(value, payload, payload_len TSRMLS_CC); #else ZVAL_FALSE(value); php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not unserialize value, no msgpack support"); return 0; #endif break; } return 1; } /* The caller MUST free the payload */ static int php_memc_zval_from_payload(zval *value, const char *payload_in, size_t payload_len, uint32_t flags, enum memcached_serializer serializer TSRMLS_DC) { /* A NULL payload is completely valid if length is 0, it is simply empty. */ int retval = 0; zend_bool payload_emalloc = 0; char *pl = NULL; if (payload_in == NULL && payload_len > 0) { ZVAL_FALSE(value); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not handle non-existing value of length %zu", payload_len); return -1; } else if (payload_in == NULL) { if (MEMC_VAL_GET_TYPE(flags) == MEMC_VAL_IS_BOOL) { ZVAL_FALSE(value); } else { ZVAL_EMPTY_STRING(value); } return 0; } if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSED)) { char *datas = s_decompress_value (payload_in, &payload_len, flags TSRMLS_CC); if (!datas) { ZVAL_FALSE(value); return -1; } pl = datas; payload_emalloc = 1; } else { pl = (char *) payload_in; } switch (MEMC_VAL_GET_TYPE(flags)) { case MEMC_VAL_IS_STRING: if (payload_emalloc) { ZVAL_STRINGL(value, pl, payload_len, 0); payload_emalloc = 0; } else { ZVAL_STRINGL(value, pl, payload_len, 1); } break; case MEMC_VAL_IS_LONG: { long lval; char conv_buf [128]; if (payload_len >= 128) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not read long value, too big"); retval = -1; } else { memcpy (conv_buf, pl, payload_len); conv_buf [payload_len] = '\0'; lval = strtol(conv_buf, NULL, 10); ZVAL_LONG(value, lval); } } break; case MEMC_VAL_IS_DOUBLE: { char conv_buf [128]; if (payload_len >= 128) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not read double value, too big"); retval = -1; } else { memcpy (conv_buf, pl, payload_len); conv_buf [payload_len] = '\0'; if (payload_len == 8 && memcmp(conv_buf, "Infinity", 8) == 0) { ZVAL_DOUBLE(value, php_get_inf()); } else if (payload_len == 9 && memcmp(conv_buf, "-Infinity", 9) == 0) { ZVAL_DOUBLE(value, -php_get_inf()); } else if (payload_len == 3 && memcmp(conv_buf, "NaN", 3) == 0) { ZVAL_DOUBLE(value, php_get_nan()); } else { ZVAL_DOUBLE(value, zend_strtod(conv_buf, NULL)); } } } break; case MEMC_VAL_IS_BOOL: ZVAL_BOOL(value, payload_len > 0 && pl[0] == '1'); break; case MEMC_VAL_IS_SERIALIZED: case MEMC_VAL_IS_IGBINARY: case MEMC_VAL_IS_JSON: case MEMC_VAL_IS_MSGPACK: if (!s_unserialize_value (serializer, MEMC_VAL_GET_TYPE(flags), value, pl, payload_len TSRMLS_CC)) { retval = -1; } break; default: ZVAL_FALSE(value); php_error_docref(NULL TSRMLS_CC, E_WARNING, "unknown payload type"); retval = -1; break; } if (payload_emalloc) { efree(pl); } return retval; } PHP_MEMCACHED_API zend_class_entry *php_memc_get_ce(void) { return memcached_ce; } PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception(void) { return memcached_exception_ce; } PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception_base(int root TSRMLS_DC) { #if HAVE_SPL if (!root) { if (!spl_ce_RuntimeException) { zend_class_entry **pce; if (zend_hash_find(CG(class_table), "runtimeexception", sizeof("RuntimeException"), (void **) &pce) == SUCCESS) { spl_ce_RuntimeException = *pce; return *pce; } } else { return spl_ce_RuntimeException; } } #endif #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) return zend_exception_get_default(); #else return zend_exception_get_default(TSRMLS_C); #endif } static memcached_return php_memc_do_cache_callback(zval *zmemc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, char *key, size_t key_len, zval *value TSRMLS_DC) { char *payload = NULL; size_t payload_len = 0; zval **params[4]; zval *retval; zval *z_key; zval *z_expiration; uint32_t flags = 0; memcached_return rc; php_memc_t* i_obj; memcached_return status = MEMCACHED_SUCCESS; int result; MAKE_STD_ZVAL(z_key); MAKE_STD_ZVAL(z_expiration); ZVAL_STRINGL(z_key, key, key_len, 1); ZVAL_NULL(value); ZVAL_LONG(z_expiration, 0); params[0] = &zmemc_obj; params[1] = &z_key; params[2] = &value; params[3] = &z_expiration; fci->retval_ptr_ptr = &retval; fci->params = params; fci->param_count = sizeof(params) / sizeof(params[0]); result = zend_call_function(fci, fcc TSRMLS_CC); if (result == SUCCESS && retval) { i_obj = (php_memc_t *) zend_object_store_get_object(zmemc_obj TSRMLS_CC); struct memc_obj *m_obj = i_obj->obj; if (zend_is_true(retval)) { time_t expiration; if (Z_TYPE_P(z_expiration) != IS_LONG) { convert_to_long(z_expiration); } expiration = Z_LVAL_P(z_expiration); payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); if (payload == NULL) { status = (memcached_return)MEMC_RES_PAYLOAD_FAILURE; } else { rc = memcached_set(m_obj->memc, key, key_len, payload, payload_len, expiration, flags); if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) { status = rc; } efree(payload); } } else { status = MEMCACHED_NOTFOUND; zval_dtor(value); ZVAL_NULL(value); } } else { if (result == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not invoke cache callback"); } status = MEMCACHED_FAILURE; zval_dtor(value); ZVAL_NULL(value); } if (retval) { zval_ptr_dtor(&retval); } zval_ptr_dtor(&z_key); zval_ptr_dtor(&z_expiration); return status; } static int php_memc_do_result_callback(zval *zmemc_obj, zend_fcall_info *fci, zend_fcall_info_cache *fcc, memcached_result_st *result TSRMLS_DC) { const char *res_key = NULL; size_t res_key_len = 0; const char *payload = NULL; size_t payload_len = 0; zval *value, *retval = NULL; uint64_t cas = 0; zval **params[2]; zval *z_result; uint32_t flags = 0; int rc = 0; php_memc_t *i_obj = NULL; params[0] = &zmemc_obj; params[1] = &z_result; fci->retval_ptr_ptr = &retval; fci->params = params; fci->param_count = 2; payload = memcached_result_value(result); payload_len = memcached_result_length(result); flags = memcached_result_flags(result); res_key = memcached_result_key_value(result); res_key_len = memcached_result_key_length(result); cas = memcached_result_cas(result); MAKE_STD_ZVAL(value); i_obj = (php_memc_t *) zend_object_store_get_object(zmemc_obj TSRMLS_CC); if (php_memc_zval_from_payload(value, payload, payload_len, flags, i_obj->obj->serializer TSRMLS_CC) < 0) { zval_ptr_dtor(&value); i_obj->rescode = MEMC_RES_PAYLOAD_FAILURE; return -1; } MAKE_STD_ZVAL(z_result); array_init(z_result); add_assoc_stringl_ex(z_result, ZEND_STRS("key"), res_key, res_key_len, 1); add_assoc_zval_ex(z_result, ZEND_STRS("value"), value); if (cas != 0) { add_assoc_double_ex(z_result, ZEND_STRS("cas"), (double)cas); } if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { add_assoc_long_ex(z_result, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); } if (zend_call_function(fci, fcc TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not invoke result callback"); rc = -1; } if (retval) { zval_ptr_dtor(&retval); } zval_ptr_dtor(&z_result); return rc; } /* }}} */ #ifdef HAVE_MEMCACHED_PROTOCOL static void s_destroy_cb (zend_fcall_info *fci) { if (fci->size > 0) { zval_ptr_dtor(&fci->function_name); if (fci->object_ptr != NULL) { zval_ptr_dtor(&fci->object_ptr); } } } static PHP_METHOD(MemcachedServer, run) { int i; zend_bool rc; char *address; int address_len; php_memc_server_t *intern; intern = (php_memc_server_t *) zend_object_store_get_object(getThis() TSRMLS_CC); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &address, &address_len) == FAILURE) { return; } rc = php_memc_proto_handler_run (intern->handler, address); for (i = MEMC_SERVER_ON_MIN + 1; i < MEMC_SERVER_ON_MAX; i++) { s_destroy_cb (&MEMC_G(server.callbacks) [i].fci); } RETURN_BOOL(rc); } static PHP_METHOD(MemcachedServer, on) { long event; zend_fcall_info fci; zend_fcall_info_cache fci_cache; zend_bool rc = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lf!", &event, &fci, &fci_cache) == FAILURE) { return; } if (event <= MEMC_SERVER_ON_MIN || event >= MEMC_SERVER_ON_MAX) { RETURN_FALSE; } if (fci.size > 0) { s_destroy_cb (&MEMC_G(server.callbacks) [event].fci); MEMC_G(server.callbacks) [event].fci = fci; MEMC_G(server.callbacks) [event].fci_cache = fci_cache; Z_ADDREF_P (fci.function_name); if (fci.object_ptr) { Z_ADDREF_P (fci.object_ptr); } } RETURN_BOOL(rc); } #endif /* {{{ methods arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0) ZEND_ARG_INFO(0, persistent_id) ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getResultCode, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getResultMessage, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_get, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, cache_cb) ZEND_ARG_INFO(2, cas_token) ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, cache_cb) ZEND_ARG_INFO(2, cas_token) ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getMulti, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(2, cas_tokens) ZEND_ARG_INFO(0, flags) ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getMultiByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(2, cas_tokens) ZEND_ARG_INFO(0, flags) ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayed, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(0, with_cas) ZEND_ARG_INFO(0, value_cb) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayedByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_ARRAY_INFO(0, keys, 0) ZEND_ARG_INFO(0, with_cas) ZEND_ARG_INFO(0, value_cb) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_fetch, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_fetchAll, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_touch, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_touchByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setMulti, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, items, 0) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setMultiByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_ARRAY_INFO(0, items, 0) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_add, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_addByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_replace, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_replaceByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_append, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_appendByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_prepend, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_prependByKey, 0, 0, 3) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_cas, 0, 0, 3) ZEND_ARG_INFO(0, cas_token) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4) ZEND_ARG_INFO(0, cas_token) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_delete, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMulti, 0, 0, 1) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteMultiByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, keys) ZEND_ARG_INFO(0, time) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_increment, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, initial_value) ZEND_ARG_INFO(0, expiry) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_decrement, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, initial_value) ZEND_ARG_INFO(0, expiry) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_incrementByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, initial_value) ZEND_ARG_INFO(0, expiry) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_decrementByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, offset) ZEND_ARG_INFO(0, initial_value) ZEND_ARG_INFO(0, expiry) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_flush, 0, 0, 0) ZEND_ARG_INFO(0, delay) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_addServer, 0, 0, 2) ZEND_ARG_INFO(0, host) ZEND_ARG_INFO(0, port) ZEND_ARG_INFO(0, weight) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_addServers, 0) ZEND_ARG_ARRAY_INFO(0, servers, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getServerList, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_resetServerList, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_quit, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_flushBuffers, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getServerByKey, 0) ZEND_ARG_INFO(0, server_key) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getLastErrorMessage, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getLastErrorCode, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getLastErrorErrno, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getLastDisconnectedServer, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getOption, 0) ZEND_ARG_INFO(0, option) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_setSaslAuthData, 0) ZEND_ARG_INFO(0, username) ZEND_ARG_INFO(0, password) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_setOption, 0) ZEND_ARG_INFO(0, option) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_setOptions, 0) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_setBucket, 3) ZEND_ARG_INFO(0, host_map) ZEND_ARG_INFO(0, forward_map) ZEND_ARG_INFO(0, replicas) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getStats, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getVersion, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_isPersistent, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_isPristine, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_getAllKeys, 0) ZEND_END_ARG_INFO() /* }}} */ /* {{{ memcached_class_methods */ #define MEMC_ME(name, args) PHP_ME(Memcached, name, args, ZEND_ACC_PUBLIC) static zend_function_entry memcached_class_methods[] = { MEMC_ME(__construct, arginfo___construct) MEMC_ME(getResultCode, arginfo_getResultCode) MEMC_ME(getResultMessage, arginfo_getResultMessage) MEMC_ME(get, arginfo_get) MEMC_ME(getByKey, arginfo_getByKey) MEMC_ME(getMulti, arginfo_getMulti) MEMC_ME(getMultiByKey, arginfo_getMultiByKey) MEMC_ME(getDelayed, arginfo_getDelayed) MEMC_ME(getDelayedByKey, arginfo_getDelayedByKey) MEMC_ME(fetch, arginfo_fetch) MEMC_ME(fetchAll, arginfo_fetchAll) MEMC_ME(set, arginfo_set) MEMC_ME(setByKey, arginfo_setByKey) #ifdef HAVE_MEMCACHED_TOUCH MEMC_ME(touch, arginfo_touch) MEMC_ME(touchByKey, arginfo_touchByKey) #endif MEMC_ME(setMulti, arginfo_setMulti) MEMC_ME(setMultiByKey, arginfo_setMultiByKey) MEMC_ME(cas, arginfo_cas) MEMC_ME(casByKey, arginfo_casByKey) MEMC_ME(add, arginfo_add) MEMC_ME(addByKey, arginfo_addByKey) MEMC_ME(append, arginfo_append) MEMC_ME(appendByKey, arginfo_appendByKey) MEMC_ME(prepend, arginfo_prepend) MEMC_ME(prependByKey, arginfo_prependByKey) MEMC_ME(replace, arginfo_replace) MEMC_ME(replaceByKey, arginfo_replaceByKey) MEMC_ME(delete, arginfo_delete) MEMC_ME(deleteMulti, arginfo_deleteMulti) MEMC_ME(deleteByKey, arginfo_deleteByKey) MEMC_ME(deleteMultiByKey, arginfo_deleteMultiByKey) MEMC_ME(increment, arginfo_increment) MEMC_ME(decrement, arginfo_decrement) MEMC_ME(incrementByKey, arginfo_incrementByKey) MEMC_ME(decrementByKey, arginfo_decrementByKey) MEMC_ME(addServer, arginfo_addServer) MEMC_ME(addServers, arginfo_addServers) MEMC_ME(getServerList, arginfo_getServerList) MEMC_ME(getServerByKey, arginfo_getServerByKey) MEMC_ME(resetServerList, arginfo_resetServerList) MEMC_ME(quit, arginfo_quit) MEMC_ME(flushBuffers, arginfo_flushBuffers) #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 MEMC_ME(getLastErrorMessage, arginfo_getLastErrorMessage) MEMC_ME(getLastErrorCode, arginfo_getLastErrorCode) MEMC_ME(getLastErrorErrno, arginfo_getLastErrorErrno) #endif MEMC_ME(getLastDisconnectedServer, arginfo_getLastDisconnectedServer) MEMC_ME(getStats, arginfo_getStats) MEMC_ME(getVersion, arginfo_getVersion) MEMC_ME(getAllKeys, arginfo_getAllKeys) MEMC_ME(flush, arginfo_flush) MEMC_ME(getOption, arginfo_getOption) MEMC_ME(setOption, arginfo_setOption) MEMC_ME(setOptions, arginfo_setOptions) MEMC_ME(setBucket, arginfo_setBucket) #ifdef HAVE_MEMCACHED_SASL MEMC_ME(setSaslAuthData, arginfo_setSaslAuthData) #endif MEMC_ME(isPersistent, arginfo_isPersistent) MEMC_ME(isPristine, arginfo_isPristine) { NULL, NULL, NULL } }; #undef MEMC_ME /* }}} */ #ifdef HAVE_MEMCACHED_PROTOCOL /* {{{ */ #define MEMC_SE_ME(name, args) PHP_ME(MemcachedServer, name, args, ZEND_ACC_PUBLIC) static zend_function_entry memcached_server_class_methods[] = { MEMC_SE_ME(run, NULL) MEMC_SE_ME(on, NULL) { NULL, NULL, NULL } }; #undef MEMC_SE_ME /* }}} */ #endif /* {{{ memcached_module_entry */ #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep memcached_deps[] = { #ifdef HAVE_MEMCACHED_SESSION ZEND_MOD_REQUIRED("session") #endif #ifdef HAVE_MEMCACHED_IGBINARY ZEND_MOD_REQUIRED("igbinary") #endif #ifdef HAVE_MEMCACHED_MSGPACK ZEND_MOD_REQUIRED("msgpack") #endif #ifdef HAVE_SPL ZEND_MOD_REQUIRED("spl") #endif {NULL, NULL, NULL} }; #endif static PHP_GINIT_FUNCTION(php_memcached) { #ifdef HAVE_MEMCACHED_SESSION php_memcached_globals->sess_locking_enabled = 1; php_memcached_globals->sess_binary_enabled = 1; php_memcached_globals->sess_consistent_hash_enabled = 0; php_memcached_globals->sess_number_of_replicas = 0; php_memcached_globals->sess_remove_failed_enabled = 0; php_memcached_globals->sess_prefix = NULL; php_memcached_globals->sess_lock_wait = 0; php_memcached_globals->sess_lock_max_wait = 0; php_memcached_globals->sess_lock_expire = 0; php_memcached_globals->sess_locked = 0; php_memcached_globals->sess_lock_key = NULL; php_memcached_globals->sess_lock_key_len = 0; php_memcached_globals->sess_randomize_replica_read = 0; php_memcached_globals->sess_connect_timeout = 1000; #if HAVE_MEMCACHED_SASL php_memcached_globals->sess_sasl_username = NULL; php_memcached_globals->sess_sasl_password = NULL; php_memcached_globals->sess_sasl_data = 0; #endif #endif php_memcached_globals->serializer_name = NULL; php_memcached_globals->serializer = SERIALIZER_DEFAULT; php_memcached_globals->compression_type = NULL; php_memcached_globals->compression_type_real = COMPRESSION_TYPE_FASTLZ; php_memcached_globals->compression_factor = 1.30; #if HAVE_MEMCACHED_SASL php_memcached_globals->use_sasl = 0; #endif php_memcached_globals->store_retry_count = 2; } zend_module_entry memcached_module_entry = { STANDARD_MODULE_HEADER, "memcached", NULL, PHP_MINIT(memcached), PHP_MSHUTDOWN(memcached), NULL, NULL, PHP_MINFO(memcached), PHP_MEMCACHED_VERSION, PHP_MODULE_GLOBALS(php_memcached), PHP_GINIT(php_memcached), NULL, NULL, STANDARD_MODULE_PROPERTIES_EX }; /* }}} */ /* {{{ php_memc_register_constants */ static void php_memc_register_constants(INIT_FUNC_ARGS) { #define REGISTER_MEMC_CLASS_CONST_LONG(name, value) zend_declare_class_constant_long(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value TSRMLS_CC) #define REGISTER_MEMC_CLASS_CONST_BOOL(name, value) zend_declare_class_constant_bool(php_memc_get_ce() , ZEND_STRS( #name ) - 1, value TSRMLS_CC) #define REGISTER_MEMC_CLASS_CONST_NULL(name) zend_declare_class_constant_null(php_memc_get_ce() , ZEND_STRS( #name ) - 1 TSRMLS_CC) /* * Class options */ REGISTER_MEMC_CLASS_CONST_LONG(LIBMEMCACHED_VERSION_HEX, LIBMEMCACHED_VERSION_HEX); REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION, MEMC_OPT_COMPRESSION); REGISTER_MEMC_CLASS_CONST_LONG(OPT_COMPRESSION_TYPE, MEMC_OPT_COMPRESSION_TYPE); REGISTER_MEMC_CLASS_CONST_LONG(OPT_PREFIX_KEY, MEMC_OPT_PREFIX_KEY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERIALIZER, MEMC_OPT_SERIALIZER); REGISTER_MEMC_CLASS_CONST_LONG(OPT_STORE_RETRY_COUNT, MEMC_OPT_STORE_RETRY_COUNT); /* * Indicate whether igbinary serializer is available */ #ifdef HAVE_MEMCACHED_IGBINARY REGISTER_MEMC_CLASS_CONST_LONG(HAVE_IGBINARY, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_IGBINARY, 0); #endif /* * Indicate whether json serializer is available */ #ifdef HAVE_JSON_API REGISTER_MEMC_CLASS_CONST_LONG(HAVE_JSON, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_JSON, 0); #endif /* * Indicate whether msgpack serializer is available */ #ifdef HAVE_MEMCACHED_MSGPACK REGISTER_MEMC_CLASS_CONST_LONG(HAVE_MSGPACK, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_MSGPACK, 0); #endif #ifdef HAVE_MEMCACHED_SESSION REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SESSION, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SESSION, 0); #endif #ifdef HAVE_MEMCACHED_SASL REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SASL, 1); #else REGISTER_MEMC_CLASS_CONST_LONG(HAVE_SASL, 0); #endif /* * libmemcached behavior options */ REGISTER_MEMC_CLASS_CONST_LONG(OPT_HASH, MEMCACHED_BEHAVIOR_HASH); REGISTER_MEMC_CLASS_CONST_LONG(HASH_DEFAULT, MEMCACHED_HASH_DEFAULT); REGISTER_MEMC_CLASS_CONST_LONG(HASH_MD5, MEMCACHED_HASH_MD5); REGISTER_MEMC_CLASS_CONST_LONG(HASH_CRC, MEMCACHED_HASH_CRC); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1_64, MEMCACHED_HASH_FNV1_64); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1A_64, MEMCACHED_HASH_FNV1A_64); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1_32, MEMCACHED_HASH_FNV1_32); REGISTER_MEMC_CLASS_CONST_LONG(HASH_FNV1A_32, MEMCACHED_HASH_FNV1A_32); REGISTER_MEMC_CLASS_CONST_LONG(HASH_HSIEH, MEMCACHED_HASH_HSIEH); REGISTER_MEMC_CLASS_CONST_LONG(HASH_MURMUR, MEMCACHED_HASH_MURMUR); REGISTER_MEMC_CLASS_CONST_LONG(OPT_DISTRIBUTION, MEMCACHED_BEHAVIOR_DISTRIBUTION); REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_MODULA, MEMCACHED_DISTRIBUTION_MODULA); REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_CONSISTENT, MEMCACHED_DISTRIBUTION_CONSISTENT); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 REGISTER_MEMC_CLASS_CONST_LONG(DISTRIBUTION_VIRTUAL_BUCKET, MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET); #endif REGISTER_MEMC_CLASS_CONST_LONG(OPT_LIBKETAMA_COMPATIBLE, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED); REGISTER_MEMC_CLASS_CONST_LONG(OPT_LIBKETAMA_HASH, MEMCACHED_BEHAVIOR_KETAMA_HASH); REGISTER_MEMC_CLASS_CONST_LONG(OPT_TCP_KEEPALIVE, MEMCACHED_BEHAVIOR_TCP_KEEPALIVE); REGISTER_MEMC_CLASS_CONST_LONG(OPT_BUFFER_WRITES, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_BINARY_PROTOCOL, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL); REGISTER_MEMC_CLASS_CONST_LONG(OPT_NO_BLOCK, MEMCACHED_BEHAVIOR_NO_BLOCK); REGISTER_MEMC_CLASS_CONST_LONG(OPT_TCP_NODELAY, MEMCACHED_BEHAVIOR_TCP_NODELAY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SOCKET_SEND_SIZE, MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SOCKET_RECV_SIZE, MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE); REGISTER_MEMC_CLASS_CONST_LONG(OPT_CONNECT_TIMEOUT, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_RETRY_TIMEOUT, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000003 REGISTER_MEMC_CLASS_CONST_LONG(OPT_DEAD_TIMEOUT, MEMCACHED_BEHAVIOR_DEAD_TIMEOUT); #endif REGISTER_MEMC_CLASS_CONST_LONG(OPT_SEND_TIMEOUT, MEMCACHED_BEHAVIOR_SND_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_RECV_TIMEOUT, MEMCACHED_BEHAVIOR_RCV_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_POLL_TIMEOUT, MEMCACHED_BEHAVIOR_POLL_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_CACHE_LOOKUPS, MEMCACHED_BEHAVIOR_CACHE_LOOKUPS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERVER_FAILURE_LIMIT, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT); REGISTER_MEMC_CLASS_CONST_LONG(OPT_AUTO_EJECT_HOSTS, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_HASH_WITH_PREFIX_KEY, MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_NOREPLY, MEMCACHED_BEHAVIOR_NOREPLY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_SORT_HOSTS, MEMCACHED_BEHAVIOR_SORT_HOSTS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_VERIFY_KEY, MEMCACHED_BEHAVIOR_VERIFY_KEY); REGISTER_MEMC_CLASS_CONST_LONG(OPT_USE_UDP, MEMCACHED_BEHAVIOR_USE_UDP); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00037000 REGISTER_MEMC_CLASS_CONST_LONG(OPT_NUMBER_OF_REPLICAS, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS); REGISTER_MEMC_CLASS_CONST_LONG(OPT_RANDOMIZE_REPLICA_READ, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ); #endif #ifdef HAVE_MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS REGISTER_MEMC_CLASS_CONST_LONG(OPT_REMOVE_FAILED_SERVERS, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS); #endif #ifdef HAVE_MEMCACHED_BEHAVIOR_SERVER_TIMEOUT_LIMIT REGISTER_MEMC_CLASS_CONST_LONG(OPT_SERVER_TIMEOUT_LIMIT, MEMCACHED_BEHAVIOR_SERVER_TIMEOUT_LIMIT); #endif /* * libmemcached result codes */ REGISTER_MEMC_CLASS_CONST_LONG(RES_SUCCESS, MEMCACHED_SUCCESS); REGISTER_MEMC_CLASS_CONST_LONG(RES_FAILURE, MEMCACHED_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_HOST_LOOKUP_FAILURE, MEMCACHED_HOST_LOOKUP_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_READ_FAILURE, MEMCACHED_UNKNOWN_READ_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_PROTOCOL_ERROR, MEMCACHED_PROTOCOL_ERROR); REGISTER_MEMC_CLASS_CONST_LONG(RES_CLIENT_ERROR, MEMCACHED_CLIENT_ERROR); REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_ERROR, MEMCACHED_SERVER_ERROR); REGISTER_MEMC_CLASS_CONST_LONG(RES_WRITE_FAILURE, MEMCACHED_WRITE_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_DATA_EXISTS, MEMCACHED_DATA_EXISTS); REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTSTORED, MEMCACHED_NOTSTORED); REGISTER_MEMC_CLASS_CONST_LONG(RES_NOTFOUND, MEMCACHED_NOTFOUND); REGISTER_MEMC_CLASS_CONST_LONG(RES_PARTIAL_READ, MEMCACHED_PARTIAL_READ); REGISTER_MEMC_CLASS_CONST_LONG(RES_SOME_ERRORS, MEMCACHED_SOME_ERRORS); REGISTER_MEMC_CLASS_CONST_LONG(RES_NO_SERVERS, MEMCACHED_NO_SERVERS); REGISTER_MEMC_CLASS_CONST_LONG(RES_END, MEMCACHED_END); REGISTER_MEMC_CLASS_CONST_LONG(RES_ERRNO, MEMCACHED_ERRNO); REGISTER_MEMC_CLASS_CONST_LONG(RES_BUFFERED, MEMCACHED_BUFFERED); REGISTER_MEMC_CLASS_CONST_LONG(RES_TIMEOUT, MEMCACHED_TIMEOUT); REGISTER_MEMC_CLASS_CONST_LONG(RES_BAD_KEY_PROVIDED, MEMCACHED_BAD_KEY_PROVIDED); REGISTER_MEMC_CLASS_CONST_LONG(RES_STORED, MEMCACHED_STORED); REGISTER_MEMC_CLASS_CONST_LONG(RES_DELETED, MEMCACHED_DELETED); REGISTER_MEMC_CLASS_CONST_LONG(RES_STAT, MEMCACHED_STAT); REGISTER_MEMC_CLASS_CONST_LONG(RES_ITEM, MEMCACHED_ITEM); REGISTER_MEMC_CLASS_CONST_LONG(RES_NOT_SUPPORTED, MEMCACHED_NOT_SUPPORTED); REGISTER_MEMC_CLASS_CONST_LONG(RES_FETCH_NOTFINISHED, MEMCACHED_FETCH_NOTFINISHED); REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_MARKED_DEAD, MEMCACHED_SERVER_MARKED_DEAD); REGISTER_MEMC_CLASS_CONST_LONG(RES_UNKNOWN_STAT_KEY, MEMCACHED_UNKNOWN_STAT_KEY); REGISTER_MEMC_CLASS_CONST_LONG(RES_INVALID_HOST_PROTOCOL, MEMCACHED_INVALID_HOST_PROTOCOL); REGISTER_MEMC_CLASS_CONST_LONG(RES_MEMORY_ALLOCATION_FAILURE, MEMCACHED_MEMORY_ALLOCATION_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_CONNECTION_SOCKET_CREATE_FAILURE, MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_BAD_KEY_PROVIDED, MEMCACHED_BAD_KEY_PROVIDED); REGISTER_MEMC_CLASS_CONST_LONG(RES_E2BIG, MEMCACHED_E2BIG); REGISTER_MEMC_CLASS_CONST_LONG(RES_KEY_TOO_BIG, MEMCACHED_KEY_TOO_BIG); REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_TEMPORARILY_DISABLED, MEMCACHED_SERVER_TEMPORARILY_DISABLED); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000008 REGISTER_MEMC_CLASS_CONST_LONG(RES_SERVER_MEMORY_ALLOCATION_FAILURE, MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE); #endif #if HAVE_MEMCACHED_SASL REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_PROBLEM, MEMCACHED_AUTH_PROBLEM); REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_FAILURE, MEMCACHED_AUTH_FAILURE); REGISTER_MEMC_CLASS_CONST_LONG(RES_AUTH_CONTINUE, MEMCACHED_AUTH_CONTINUE); #endif /* * Our result codes. */ REGISTER_MEMC_CLASS_CONST_LONG(RES_PAYLOAD_FAILURE, MEMC_RES_PAYLOAD_FAILURE); /* * Serializer types. */ REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_PHP, SERIALIZER_PHP); REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_IGBINARY, SERIALIZER_IGBINARY); REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_JSON, SERIALIZER_JSON); REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_JSON_ARRAY, SERIALIZER_JSON_ARRAY); REGISTER_MEMC_CLASS_CONST_LONG(SERIALIZER_MSGPACK, SERIALIZER_MSGPACK); /* * Compression types */ REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_FASTLZ, COMPRESSION_TYPE_FASTLZ); REGISTER_MEMC_CLASS_CONST_LONG(COMPRESSION_ZLIB, COMPRESSION_TYPE_ZLIB); /* * Flags. */ REGISTER_MEMC_CLASS_CONST_LONG(GET_PRESERVE_ORDER, MEMC_GET_PRESERVE_ORDER); #ifdef HAVE_MEMCACHED_PROTOCOL /* * Server callbacks */ REGISTER_MEMC_CLASS_CONST_LONG(ON_CONNECT, MEMC_SERVER_ON_CONNECT); REGISTER_MEMC_CLASS_CONST_LONG(ON_ADD, MEMC_SERVER_ON_ADD); REGISTER_MEMC_CLASS_CONST_LONG(ON_APPEND, MEMC_SERVER_ON_APPEND); REGISTER_MEMC_CLASS_CONST_LONG(ON_DECREMENT, MEMC_SERVER_ON_DECREMENT); REGISTER_MEMC_CLASS_CONST_LONG(ON_DELETE, MEMC_SERVER_ON_DELETE); REGISTER_MEMC_CLASS_CONST_LONG(ON_FLUSH, MEMC_SERVER_ON_FLUSH); REGISTER_MEMC_CLASS_CONST_LONG(ON_GET, MEMC_SERVER_ON_GET); REGISTER_MEMC_CLASS_CONST_LONG(ON_INCREMENT, MEMC_SERVER_ON_INCREMENT); REGISTER_MEMC_CLASS_CONST_LONG(ON_NOOP, MEMC_SERVER_ON_NOOP); REGISTER_MEMC_CLASS_CONST_LONG(ON_PREPEND, MEMC_SERVER_ON_PREPEND); REGISTER_MEMC_CLASS_CONST_LONG(ON_QUIT, MEMC_SERVER_ON_QUIT); REGISTER_MEMC_CLASS_CONST_LONG(ON_REPLACE, MEMC_SERVER_ON_REPLACE); REGISTER_MEMC_CLASS_CONST_LONG(ON_SET, MEMC_SERVER_ON_SET); REGISTER_MEMC_CLASS_CONST_LONG(ON_STAT, MEMC_SERVER_ON_STAT); REGISTER_MEMC_CLASS_CONST_LONG(ON_VERSION, MEMC_SERVER_ON_VERSION); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_SUCCESS, PROTOCOL_BINARY_RESPONSE_SUCCESS); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_KEY_ENOENT, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_KEY_EEXISTS, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_E2BIG, PROTOCOL_BINARY_RESPONSE_E2BIG); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_EINVAL, PROTOCOL_BINARY_RESPONSE_EINVAL); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_NOT_STORED, PROTOCOL_BINARY_RESPONSE_NOT_STORED); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_DELTA_BADVAL, PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_NOT_MY_VBUCKET, PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_AUTH_ERROR, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_AUTH_CONTINUE, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_UNKNOWN_COMMAND, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_ENOMEM, PROTOCOL_BINARY_RESPONSE_ENOMEM); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_NOT_SUPPORTED, PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_EINTERNAL, PROTOCOL_BINARY_RESPONSE_EINTERNAL); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_EBUSY, PROTOCOL_BINARY_RESPONSE_EBUSY); REGISTER_MEMC_CLASS_CONST_LONG(RESPONSE_ETMPFAIL, PROTOCOL_BINARY_RESPONSE_ETMPFAIL); #endif #undef REGISTER_MEMC_CLASS_CONST_LONG /* * Return value from simple get errors */ REGISTER_MEMC_CLASS_CONST_BOOL(GET_ERROR_RETURN_VALUE, 0); } /* }}} */ int php_memc_sess_list_entry(void) { return le_memc_sess; } /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(memcached) { zend_class_entry ce; memcpy(&memcached_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); memcached_object_handlers.clone_obj = NULL; le_memc = zend_register_list_destructors_ex(NULL, php_memc_dtor, "Memcached persistent connection", module_number); le_memc_sess = zend_register_list_destructors_ex(NULL, php_memc_sess_dtor, "Memcached Sessions persistent connection", module_number); INIT_CLASS_ENTRY(ce, "Memcached", memcached_class_methods); memcached_ce = zend_register_internal_class(&ce TSRMLS_CC); memcached_ce->create_object = php_memc_new; #ifdef HAVE_MEMCACHED_PROTOCOL memcpy(&memcached_server_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); memcached_server_object_handlers.clone_obj = NULL; INIT_CLASS_ENTRY(ce, "MemcachedServer", memcached_server_class_methods); memcached_server_ce = zend_register_internal_class(&ce TSRMLS_CC); memcached_server_ce->create_object = php_memc_server_new; #endif INIT_CLASS_ENTRY(ce, "MemcachedException", NULL); memcached_exception_ce = zend_register_internal_class_ex(&ce, php_memc_get_exception_base(0 TSRMLS_CC), NULL TSRMLS_CC); /* TODO * possibly declare custom exception property here */ php_memc_register_constants(INIT_FUNC_ARGS_PASSTHRU); #ifdef HAVE_MEMCACHED_SESSION php_session_register_module(ps_memcached_ptr); #endif REGISTER_INI_ENTRIES(); #if HAVE_MEMCACHED_SASL if (MEMC_G(use_sasl)) { if (sasl_client_init(NULL) != SASL_OK) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize SASL library"); return FAILURE; } } #endif return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(memcached) { #if HAVE_MEMCACHED_SASL if (MEMC_G(use_sasl)) { sasl_done(); } #endif UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(memcached) { php_info_print_table_start(); php_info_print_table_header(2, "memcached support", "enabled"); php_info_print_table_row(2, "Version", PHP_MEMCACHED_VERSION); php_info_print_table_row(2, "libmemcached version", memcached_lib_version()); #if HAVE_MEMCACHED_SASL php_info_print_table_row(2, "SASL support", "yes"); #else php_info_print_table_row(2, "SASL support", "no"); #endif #ifdef HAVE_MEMCACHED_SESSION php_info_print_table_row(2, "Session support", "yes"); #else php_info_print_table_row(2, "Session support ", "no"); #endif #ifdef HAVE_MEMCACHED_IGBINARY php_info_print_table_row(2, "igbinary support", "yes"); #else php_info_print_table_row(2, "igbinary support", "no"); #endif #ifdef HAVE_JSON_API php_info_print_table_row(2, "json support", "yes"); #else php_info_print_table_row(2, "json support", "no"); #endif #ifdef HAVE_MEMCACHED_MSGPACK php_info_print_table_row(2, "msgpack support", "yes"); #else php_info_print_table_row(2, "msgpack support", "no"); #endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim: noet sw=4 ts=4 fdm=marker: */ /* +----------------------------------------------------------------------+ | Copyright (c) 2009 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ /* $ Id: $ */ #ifndef PHP_MEMCACHED_H #define PHP_MEMCACHED_H #include "php.h" #include "main/php_config.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #define PHP_MEMCACHED_VERSION "2.2.0" #if defined(PHP_WIN32) && defined(MEMCACHED_EXPORTS) #define PHP_MEMCACHED_API __declspec(dllexport) #else #define PHP_MEMCACHED_API PHPAPI #endif PHP_MEMCACHED_API zend_class_entry *php_memc_get_ce(void); PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception(void); PHP_MEMCACHED_API zend_class_entry *php_memc_get_exception_base(int root TSRMLS_DC); extern zend_module_entry memcached_module_entry; #define phpext_memcached_ptr &memcached_module_entry #endif /* PHP_MEMCACHED_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ /* +----------------------------------------------------------------------+ | Copyright (c) 2009 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ /* $ Id: $ */ #ifndef PHP_MEMCACHED_PRIVATE_H #define PHP_MEMCACHED_PRIVATE_H #include "main/php_config.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "php_libmemcached_compat.h" #include <stdlib.h> #include <string.h> #include <php.h> #include <php_main.h> #ifdef ZTS # include "TSRM.h" #endif #include <php_ini.h> #include <SAPI.h> #include <ext/standard/info.h> #include <zend_extensions.h> #include <zend_exceptions.h> #include <ext/standard/php_smart_str.h> #include <ext/standard/php_var.h> #include <ext/standard/basic_functions.h> #ifdef PHP_WIN32 # include "win32/php_stdint.h" #else /* Used to store the size of the block */ # if defined(HAVE_INTTYPES_H) # include <inttypes.h> # elif defined(HAVE_STDINT_H) # include <stdint.h> # endif #endif #ifndef HAVE_INT32_T # if SIZEOF_INT == 4 typedef int int32_t; # elif SIZEOF_LONG == 4 typedef long int int32_t; # endif #endif #ifndef HAVE_UINT32_T # if SIZEOF_INT == 4 typedef unsigned int uint32_t; # elif SIZEOF_LONG == 4 typedef unsigned long int uint32_t; # endif #endif /**************************************** Structures and definitions ****************************************/ enum memcached_serializer { SERIALIZER_PHP = 1, SERIALIZER_IGBINARY = 2, SERIALIZER_JSON = 3, SERIALIZER_JSON_ARRAY = 4, SERIALIZER_MSGPACK = 5, }; #ifdef HAVE_MEMCACHED_IGBINARY #define SERIALIZER_DEFAULT SERIALIZER_IGBINARY #define SERIALIZER_DEFAULT_NAME "igbinary" #elif HAVE_MEMCACHED_MSGPACK #define SERIALIZER_DEFAULT SERIALIZER_MSGPACK #define SERIALIZER_DEFAULT_NAME "msgpack" #else #define SERIALIZER_DEFAULT SERIALIZER_PHP #define SERIALIZER_DEFAULT_NAME "php" #endif /* HAVE_MEMCACHED_IGBINARY / HAVE_MEMCACHED_MSGPACK */ #if LIBMEMCACHED_WITH_SASL_SUPPORT # if defined(HAVE_SASL_SASL_H) # include <sasl/sasl.h> # define HAVE_MEMCACHED_SASL 1 # endif #endif #ifdef HAVE_MEMCACHED_PROTOCOL typedef enum { MEMC_SERVER_ON_MIN = -1, MEMC_SERVER_ON_CONNECT = 0, MEMC_SERVER_ON_ADD = 1, MEMC_SERVER_ON_APPEND = 2, MEMC_SERVER_ON_DECREMENT = 3, MEMC_SERVER_ON_DELETE = 4, MEMC_SERVER_ON_FLUSH = 5, MEMC_SERVER_ON_GET = 6, MEMC_SERVER_ON_INCREMENT = 7, MEMC_SERVER_ON_NOOP = 8, MEMC_SERVER_ON_PREPEND = 9, MEMC_SERVER_ON_QUIT = 10, MEMC_SERVER_ON_REPLACE = 11, MEMC_SERVER_ON_SET = 12, MEMC_SERVER_ON_STAT = 13, MEMC_SERVER_ON_VERSION = 14, MEMC_SERVER_ON_MAX } php_memc_event_t; typedef struct { zend_fcall_info fci; zend_fcall_info_cache fci_cache; } php_memc_server_cb_t; #endif ZEND_BEGIN_MODULE_GLOBALS(php_memcached) #ifdef HAVE_MEMCACHED_SESSION zend_bool sess_locking_enabled; long sess_lock_wait; long sess_lock_max_wait; long sess_lock_expire; char* sess_prefix; zend_bool sess_locked; char* sess_lock_key; int sess_lock_key_len; int sess_number_of_replicas; zend_bool sess_randomize_replica_read; zend_bool sess_remove_failed_enabled; long sess_connect_timeout; zend_bool sess_consistent_hash_enabled; zend_bool sess_binary_enabled; #if HAVE_MEMCACHED_SASL char *sess_sasl_username; char *sess_sasl_password; zend_bool sess_sasl_data; #endif #endif char *serializer_name; enum memcached_serializer serializer; char *compression_type; int compression_type_real; int compression_threshold; double compression_factor; #if HAVE_MEMCACHED_SASL zend_bool use_sasl; #endif #ifdef HAVE_MEMCACHED_PROTOCOL struct { php_memc_server_cb_t callbacks [MEMC_SERVER_ON_MAX]; } server; #endif long store_retry_count; ZEND_END_MODULE_GLOBALS(php_memcached) PHP_RINIT_FUNCTION(memcached); PHP_RSHUTDOWN_FUNCTION(memcached); PHP_MINIT_FUNCTION(memcached); PHP_MSHUTDOWN_FUNCTION(memcached); PHP_MINFO_FUNCTION(memcached); #ifdef ZTS #define MEMC_G(v) TSRMG(php_memcached_globals_id, zend_php_memcached_globals *, v) #else #define MEMC_G(v) (php_memcached_globals.v) #endif typedef struct { memcached_st *memc_sess; zend_bool is_persistent; } memcached_sess; int php_memc_sess_list_entry(void); char *php_memc_printable_func (zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TSRMLS_DC); #endif /* PHP_MEMCACHED_PRIVATE_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 */ /* +----------------------------------------------------------------------+ | Copyright (c) 2009-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ #include "php_memcached.h" #include "php_memcached_private.h" #include "php_memcached_session.h" extern ZEND_DECLARE_MODULE_GLOBALS(php_memcached) #define MEMC_SESS_DEFAULT_LOCK_WAIT 150000 #define MEMC_SESS_LOCK_EXPIRATION 30 ps_module ps_mod_memcached = { PS_MOD(memcached) }; static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC) { char *lock_key = NULL; int lock_key_len = 0; unsigned long attempts; long write_retry_attempts = 0; long lock_maxwait = MEMC_G(sess_lock_max_wait); long lock_wait = MEMC_G(sess_lock_wait); long lock_expire = MEMC_G(sess_lock_expire); time_t expiration; memcached_return status; /* set max timeout for session_start = max_execution_time. (c) Andrei Darashenka, Richter & Poweleit GmbH */ if (lock_maxwait <= 0) { lock_maxwait = zend_ini_long(ZEND_STRS("max_execution_time"), 0); if (lock_maxwait <= 0) { lock_maxwait = MEMC_SESS_LOCK_EXPIRATION; } } if (lock_wait == 0) { lock_wait = MEMC_SESS_DEFAULT_LOCK_WAIT; } if (lock_expire <= 0) { lock_expire = lock_maxwait; } expiration = time(NULL) + lock_expire + 1; attempts = (unsigned long)((1000000.0 / lock_wait) * lock_maxwait); /* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server */ if (MEMC_G(sess_remove_failed_enabled)) { write_retry_attempts = MEMC_G(sess_number_of_replicas) * ( memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1); } lock_key_len = spprintf(&lock_key, 0, "lock.%s", key); do { status = memcached_add(memc, lock_key, lock_key_len, "1", sizeof("1")-1, expiration, 0); if (status == MEMCACHED_SUCCESS) { MEMC_G(sess_locked) = 1; MEMC_G(sess_lock_key) = lock_key; MEMC_G(sess_lock_key_len) = lock_key_len; return 0; } else if (status != MEMCACHED_NOTSTORED && status != MEMCACHED_DATA_EXISTS) { if (write_retry_attempts > 0) { write_retry_attempts--; continue; } php_error_docref(NULL TSRMLS_CC, E_WARNING, "Write of lock failed"); break; } if (lock_wait > 0) { usleep(lock_wait); } } while(--attempts > 0); efree(lock_key); return -1; } static void php_memc_sess_unlock(memcached_st *memc TSRMLS_DC) { if (MEMC_G(sess_locked)) { memcached_delete(memc, MEMC_G(sess_lock_key), MEMC_G(sess_lock_key_len), 0); MEMC_G(sess_locked) = 0; efree(MEMC_G(sess_lock_key)); MEMC_G(sess_lock_key_len) = 0; } } PS_OPEN_FUNC(memcached) { memcached_sess *memc_sess = PS_GET_MOD_DATA(); memcached_return status; char *p, *plist_key = NULL; int plist_key_len = 0; if (!strncmp((char *)save_path, "PERSISTENT=", sizeof("PERSISTENT=") - 1)) { zend_rsrc_list_entry *le = NULL; char *e; p = (char *)save_path + sizeof("PERSISTENT=") - 1; if (!*p) { error: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid persistent id for session storage"); return FAILURE; } if ((e = strchr(p, ' '))) { plist_key_len = spprintf(&plist_key, 0, "memcached_sessions:id=%.*s", (int)(e - p), p); } else { goto error; } plist_key_len++; if (zend_hash_find(&EG(persistent_list), plist_key, plist_key_len, (void *)&le) == SUCCESS) { if (le->type == php_memc_sess_list_entry()) { memc_sess = (memcached_sess *) le->ptr; PS_SET_MOD_DATA(memc_sess); return SUCCESS; } } p = e + 1; memc_sess = pecalloc(sizeof(*memc_sess), 1, 1); memc_sess->is_persistent = 1; } else { p = (char *)save_path; memc_sess = ecalloc(sizeof(*memc_sess), 1); memc_sess->is_persistent = 0; } if (!strstr(p, "--SERVER")) { memcached_server_st *servers = memcached_servers_parse(p); if (servers) { memc_sess->memc_sess = memcached_create(NULL); if (memc_sess->memc_sess) { if (MEMC_G(sess_consistent_hash_enabled)) { if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, (uint64_t) 1) == MEMCACHED_FAILURE) { PS_SET_MOD_DATA(NULL); if (plist_key) { efree(plist_key); } memcached_free(memc_sess->memc_sess); php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to enable memcached consistent hashing"); return FAILURE; } } status = memcached_server_push(memc_sess->memc_sess, servers); memcached_server_list_free(servers); if (MEMC_G(sess_prefix) && MEMC_G(sess_prefix)[0] != 0 && memcached_callback_set(memc_sess->memc_sess, MEMCACHED_CALLBACK_PREFIX_KEY, MEMC_G(sess_prefix)) != MEMCACHED_SUCCESS) { PS_SET_MOD_DATA(NULL); if (plist_key) { efree(plist_key); } memcached_free(memc_sess->memc_sess); php_error_docref(NULL TSRMLS_CC, E_WARNING, "bad memcached key prefix in memcached.sess_prefix"); return FAILURE; } if (status == MEMCACHED_SUCCESS) { goto success; } } else { memcached_server_list_free(servers); php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not allocate libmemcached structure"); } } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to parse session.save_path"); } } else { memc_sess->memc_sess = php_memc_create_str(p, strlen(p)); if (!memc_sess->memc_sess) { #ifdef HAVE_LIBMEMCACHED_CHECK_CONFIGURATION char error_buffer[1024]; if (libmemcached_check_configuration(p, strlen(p), error_buffer, sizeof(error_buffer)) != MEMCACHED_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "session.save_path configuration error %s", error_buffer); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to initialize memcached session storage"); } #else php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to initialize memcached session storage"); #endif } else { success: PS_SET_MOD_DATA(memc_sess); if (plist_key) { zend_rsrc_list_entry le; le.type = php_memc_sess_list_entry(); le.ptr = memc_sess; if (zend_hash_update(&EG(persistent_list), (char *)plist_key, plist_key_len, (void *)&le, sizeof(le), NULL) == FAILURE) { efree(plist_key); php_error_docref(NULL TSRMLS_CC, E_ERROR, "could not register persistent entry"); } efree(plist_key); } if (MEMC_G(sess_binary_enabled)) { if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, (uint64_t) 1) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session binary protocol"); return FAILURE; } } #ifdef HAVE_MEMCACHED_SASL if (MEMC_G(use_sasl)) { /* * Enable SASL support if username and password are set * */ if (MEMC_G(sess_sasl_username) && MEMC_G(sess_sasl_password)) { /* Force binary protocol */ if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, (uint64_t) 1) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session binary protocol"); return FAILURE; } if (memcached_set_sasl_auth_data(memc_sess->memc_sess, MEMC_G(sess_sasl_username), MEMC_G(sess_sasl_password)) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session sasl credentials"); return FAILURE; } MEMC_G(sess_sasl_data) = 1; } } #endif if (MEMC_G(sess_number_of_replicas) > 0) { if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, (uint64_t) MEMC_G(sess_number_of_replicas)) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session number of replicas"); return FAILURE; } if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, (uint64_t) MEMC_G(sess_randomize_replica_read)) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached session randomize replica read"); } } if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, (uint64_t) MEMC_G(sess_connect_timeout)) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached connection timeout"); return FAILURE; } #ifdef HAVE_MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS /* Allow libmemcached remove failed servers */ if (MEMC_G(sess_remove_failed_enabled)) { if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS, (uint64_t) 1) == MEMCACHED_FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set: remove failed servers"); return FAILURE; } } #endif return SUCCESS; } } if (plist_key) { efree(plist_key); } PS_SET_MOD_DATA(NULL); return FAILURE; } PS_CLOSE_FUNC(memcached) { memcached_sess *memc_sess = PS_GET_MOD_DATA(); if (MEMC_G(sess_locking_enabled)) { php_memc_sess_unlock(memc_sess->memc_sess TSRMLS_CC); } if (memc_sess->memc_sess) { if (!memc_sess->is_persistent) { #ifdef HAVE_MEMCACHED_SASL if (MEMC_G(sess_sasl_data)) { memcached_destroy_sasl_auth_data(memc_sess->memc_sess); } #endif memcached_free(memc_sess->memc_sess); efree(memc_sess); } PS_SET_MOD_DATA(NULL); } return SUCCESS; } PS_READ_FUNC(memcached) { char *payload = NULL; size_t payload_len = 0; int key_len = strlen(key); uint32_t flags = 0; memcached_return status; memcached_sess *memc_sess = PS_GET_MOD_DATA(); size_t key_length; key_length = strlen(MEMC_G(sess_prefix)) + key_len + 5; // prefix + "lock." if (!key_length || key_length >= MEMCACHED_MAX_KEY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id is too long or contains illegal characters"); PS(invalid_session_id) = 1; return FAILURE; } if (MEMC_G(sess_locking_enabled)) { if (php_memc_sess_lock(memc_sess->memc_sess, key TSRMLS_CC) < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to clear session lock record"); return FAILURE; } } payload = memcached_get(memc_sess->memc_sess, key, key_len, &payload_len, &flags, &status); if (status == MEMCACHED_SUCCESS) { *val = estrndup(payload, payload_len); *vallen = payload_len; free(payload); return SUCCESS; } else { return FAILURE; } } PS_WRITE_FUNC(memcached) { int key_len = strlen(key); time_t expiration = 0; long write_try_attempts = 1; memcached_return status; memcached_sess *memc_sess = PS_GET_MOD_DATA(); size_t key_length; key_length = strlen(MEMC_G(sess_prefix)) + key_len + 5; // prefix + "lock." if (!key_length || key_length >= MEMCACHED_MAX_KEY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id is too long or contains illegal characters"); PS(invalid_session_id) = 1; return FAILURE; } if (PS(gc_maxlifetime) > 0) { expiration = PS(gc_maxlifetime); } /* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server plus the initial write */ if (MEMC_G(sess_remove_failed_enabled)) { write_try_attempts = 1 + MEMC_G(sess_number_of_replicas) * ( memcached_behavior_get(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1); } do { status = memcached_set(memc_sess->memc_sess, key, key_len, val, vallen, expiration, 0); if (status == MEMCACHED_SUCCESS) { return SUCCESS; } else { write_try_attempts--; } } while (write_try_attempts > 0); return FAILURE; } PS_DESTROY_FUNC(memcached) { memcached_sess *memc_sess = PS_GET_MOD_DATA(); memcached_delete(memc_sess->memc_sess, key, strlen(key), 0); if (MEMC_G(sess_locking_enabled)) { php_memc_sess_unlock(memc_sess->memc_sess TSRMLS_CC); } return SUCCESS; } PS_GC_FUNC(memcached) { return SUCCESS; } /* }}} */ /* +----------------------------------------------------------------------+ | Copyright (c) 2009-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ #ifndef PHP_MEMCACHED_SESSION_H #define PHP_MEMCACHED_SESSION_H /* session handler struct */ #include "ext/session/php_session.h" extern ps_module ps_mod_memcached; #define ps_memcached_ptr &ps_mod_memcached PS_FUNCS(memcached); PS_OPEN_FUNC(memcached); PS_CLOSE_FUNC(memcached); PS_READ_FUNC(memcached); PS_WRITE_FUNC(memcached); PS_DESTROY_FUNC(memcached); PS_GC_FUNC(memcached); #endif /* PHP_MEMCACHED_SESSION_H */ /* +----------------------------------------------------------------------+ | Copyright (c) 2009 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ #ifndef PHP_LIBMEMCACHED_COMPAT #define PHP_LIBMEMCACHED_COMPAT /* this is the version(s) we support */ #include <libmemcached/memcached.h> memcached_st *php_memc_create_str (const char *str, size_t str_len); #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX < 0x00052000 # define MEMCACHED_SERVER_TEMPORARILY_DISABLED (1024 << 2) #endif #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000002 # define HAVE_MEMCACHED_TOUCH 1 #endif #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000017 # define HAVE_MEMCACHED_INSTANCE_ST 1 #endif #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 # define HAVE_LIBMEMCACHED_CHECK_CONFIGURATION 1 #endif #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000002 # define HAVE_MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS 1 #endif #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x00049000 # define HAVE_LIBMEMCACHED_MEMCACHED 1 #endif #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000018 # define HAVE_MEMCACHED_BEHAVIOR_SERVER_TIMEOUT_LIMIT 1 #endif #ifdef HAVE_MEMCACHED_INSTANCE_ST typedef const memcached_instance_st * php_memcached_instance_st; #else typedef memcached_server_instance_st php_memcached_instance_st; #endif #endif /* +----------------------------------------------------------------------+ | Copyright (c) 2009 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ #include "php_memcached.h" #include "php_memcached_private.h" #include "php_libmemcached_compat.h" memcached_st *php_memc_create_str (const char *str, size_t str_len) { #ifdef HAVE_LIBMEMCACHED_MEMCACHED return memcached (str, str_len); #else memcached_return rc; memcached_st *memc; memcached_server_st *servers; memc = memcached_create(NULL); if (!memc) { return NULL; } servers = memcached_servers_parse (str); if (!servers) { memcached_free (memc); return NULL; } rc = memcached_server_push (memc, servers); memcached_server_free (servers); if (rc != MEMCACHED_SUCCESS) { memcached_free (memc); return NULL; } return memc; #endif }/* +----------------------------------------------------------------------+ | Copyright (c) 2009-2013 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Mikko Koppanen <mkoppanen@php.net> | +----------------------------------------------------------------------+ */ #ifndef _PHP_MEMCACHED_SERVER_H_ # define _PHP_MEMCACHED_SERVER_H_ #ifdef HAVE_MEMCACHED_PROTOCOL #include <libmemcachedprotocol-0.0/handler.h> /* Opaque structure */ typedef struct _php_memc_proto_handler_t php_memc_proto_handler_t; /* Functions */ php_memc_proto_handler_t *php_memc_proto_handler_new (); void php_memc_proto_handler_destroy (php_memc_proto_handler_t **ptr); zend_bool php_memc_proto_handler_run (php_memc_proto_handler_t *h, const char *address); #endif #endif/* +----------------------------------------------------------------------+ | Copyright (c) 2009-2013 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Mikko Koppanen <mkoppanen@php.net> | +----------------------------------------------------------------------+ */ #include "php_memcached.h" #include "php_memcached_private.h" #include "php_memcached_server.h" #include <event2/listener.h> #undef NDEBUG #undef _NDEBUG #include <assert.h> #define MEMC_GET_CB(cb_type) (MEMC_G(server.callbacks)[cb_type]) #define MEMC_HAS_CB(cb_type) (MEMC_GET_CB(cb_type).fci.size > 0) #define MEMC_MAKE_ZVAL_COOKIE(my_zcookie, my_ptr) \ do { \ char *cookie_buf; \ spprintf (&cookie_buf, 0, "%p", my_ptr); \ MAKE_STD_ZVAL(my_zcookie); \ ZVAL_STRING(my_zcookie, cookie_buf, 0); \ } while (0) #define MEMC_MAKE_RESULT_CAS(my_zresult_cas, my_result_cas) \ do { \ my_result_cas = 0; \ if (Z_TYPE_P(my_zresult_cas) != IS_NULL) { \ convert_to_double (my_zresult_cas); \ my_result_cas = (uint64_t) Z_DVAL_P(my_zresult_cas); \ } \ } while (0) ZEND_EXTERN_MODULE_GLOBALS(php_memcached) struct _php_memc_proto_handler_t { memcached_binary_protocol_callback_st callbacks; struct memcached_protocol_st *protocol_handle; struct event_base *event_base; }; typedef struct { struct memcached_protocol_client_st *protocol_client; struct event_base *event_base; zend_bool on_connect_invoked; } php_memc_client_t; static long s_invoke_php_callback (php_memc_server_cb_t *cb, zval ***params, ssize_t param_count TSRMLS_DC) { long retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *retval_ptr = NULL; cb->fci.params = params; cb->fci.param_count = param_count; /* Call the cb */ cb->fci.no_separation = 1; cb->fci.retval_ptr_ptr = &retval_ptr; if (zend_call_function(&(cb->fci), &(cb->fci_cache) TSRMLS_CC) == FAILURE) { char *buf = php_memc_printable_func (&(cb->fci), &(cb->fci_cache) TSRMLS_CC); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to invoke callback %s()", buf); efree (buf); } if (retval_ptr) { convert_to_long (retval_ptr); retval = Z_LVAL_P(retval_ptr); zval_ptr_dtor(&retval_ptr); } return retval; } // memcached protocol callbacks static protocol_binary_response_status s_add_handler(const void *cookie, const void *key, uint16_t key_len, const void *data, uint32_t data_len, uint32_t flags, uint32_t exptime, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zkey, *zvalue, *zflags, *zexptime, *zresult_cas; zval **params [6]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_ADD)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zkey); ZVAL_STRINGL(zkey, key, key_len, 1); MAKE_STD_ZVAL(zvalue); ZVAL_STRINGL(zvalue, data, data_len, 1); MAKE_STD_ZVAL(zflags); ZVAL_LONG(zflags, flags); MAKE_STD_ZVAL(zexptime); ZVAL_LONG(zexptime, exptime); MAKE_STD_ZVAL(zresult_cas); ZVAL_NULL(zresult_cas); params [0] = &zcookie; params [1] = &zkey; params [2] = &zvalue; params [3] = &zflags; params [4] = &zexptime; params [5] = &zresult_cas; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_ADD), params, 6 TSRMLS_CC); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zvalue); zval_ptr_dtor (&zflags); zval_ptr_dtor (&zexptime); zval_ptr_dtor (&zresult_cas); return retval; } static protocol_binary_response_status s_append_prepend_handler (php_memc_event_t event, const void *cookie, const void *key, uint16_t key_len, const void *data, uint32_t data_len, uint64_t cas, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zkey, *zvalue, *zcas, *zresult_cas; zval **params [5]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(event)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zkey); ZVAL_STRINGL(zkey, key, key_len, 1); MAKE_STD_ZVAL(zvalue); ZVAL_STRINGL(zvalue, data, data_len, 1); MAKE_STD_ZVAL(zcas); ZVAL_DOUBLE(zcas, cas); MAKE_STD_ZVAL(zresult_cas); ZVAL_NULL(zresult_cas); params [0] = &zcookie; params [1] = &zkey; params [2] = &zvalue; params [3] = &zcas; params [4] = &zresult_cas; retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 5 TSRMLS_CC); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zvalue); zval_ptr_dtor (&zcas); zval_ptr_dtor (&zresult_cas); return retval; } static protocol_binary_response_status s_append_handler (const void *cookie, const void *key, uint16_t key_len, const void *data, uint32_t data_len, uint64_t cas, uint64_t *result_cas) { return s_append_prepend_handler (MEMC_SERVER_ON_APPEND, cookie, key, key_len, data, data_len, cas, result_cas); } static protocol_binary_response_status s_prepend_handler (const void *cookie, const void *key, uint16_t key_len, const void *data, uint32_t data_len, uint64_t cas, uint64_t *result_cas) { return s_append_prepend_handler (MEMC_SERVER_ON_PREPEND, cookie, key, key_len, data, data_len, cas, result_cas); } static protocol_binary_response_status s_incr_decr_handler (php_memc_event_t event, const void *cookie, const void *key, uint16_t key_len, uint64_t delta, uint64_t initial, uint32_t expiration, uint64_t *result, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zkey, *zdelta, *zinital, *zexpiration, *zresult, *zresult_cas; zval **params [7]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(event)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zkey); ZVAL_STRINGL(zkey, key, key_len, 1); MAKE_STD_ZVAL(zdelta); ZVAL_LONG(zdelta, (long) delta); MAKE_STD_ZVAL(zinital); ZVAL_LONG(zinital, (long) initial); MAKE_STD_ZVAL(zexpiration); ZVAL_LONG(zexpiration, (long) expiration); MAKE_STD_ZVAL(zresult); ZVAL_LONG(zresult, 0); MAKE_STD_ZVAL(zresult_cas); ZVAL_NULL(zresult_cas); params [0] = &zcookie; params [1] = &zkey; params [2] = &zdelta; params [3] = &zinital; params [4] = &zexpiration; params [5] = &zresult; params [6] = &zresult_cas; retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 7 TSRMLS_CC); if (Z_TYPE_P(zresult) != IS_LONG) { convert_to_long (zresult); } *result = (uint64_t) Z_LVAL_P(zresult); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zdelta); zval_ptr_dtor (&zinital); zval_ptr_dtor (&zexpiration); zval_ptr_dtor (&zresult); zval_ptr_dtor (&zresult_cas); return retval; } static protocol_binary_response_status s_increment_handler (const void *cookie, const void *key, uint16_t key_len, uint64_t delta, uint64_t initial, uint32_t expiration, uint64_t *result, uint64_t *result_cas) { return s_incr_decr_handler (MEMC_SERVER_ON_INCREMENT, cookie, key, key_len, delta, initial, expiration, result, result_cas); } static protocol_binary_response_status s_decrement_handler (const void *cookie, const void *key, uint16_t key_len, uint64_t delta, uint64_t initial, uint32_t expiration, uint64_t *result, uint64_t *result_cas) { return s_incr_decr_handler (MEMC_SERVER_ON_DECREMENT, cookie, key, key_len, delta, initial, expiration, result, result_cas); } static protocol_binary_response_status s_delete_handler (const void *cookie, const void *key, uint16_t key_len, uint64_t cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zkey, *zcas; zval **params [3]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_DELETE)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zkey); ZVAL_STRINGL(zkey, key, key_len, 1); MAKE_STD_ZVAL(zcas); ZVAL_DOUBLE(zcas, (double) cas); params [0] = &zcookie; params [1] = &zkey; params [2] = &zcas; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_DELETE), params, 3 TSRMLS_CC); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zcas); return retval; } static protocol_binary_response_status s_flush_handler(const void *cookie, uint32_t when) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zwhen; zval **params [2]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_FLUSH)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zwhen); ZVAL_LONG(zwhen, (long) when); params [0] = &zcookie; params [1] = &zwhen; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_FLUSH), params, 2 TSRMLS_CC); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zwhen); return retval; } static protocol_binary_response_status s_get_handler (const void *cookie, const void *key, uint16_t key_len, memcached_binary_protocol_get_response_handler response_handler) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zkey, *zvalue, *zflags, *zresult_cas; zval **params [5]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_GET)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zkey); ZVAL_STRINGL(zkey, key, key_len, 1); MAKE_STD_ZVAL(zvalue); ZVAL_NULL(zvalue); MAKE_STD_ZVAL(zflags); ZVAL_NULL(zflags); MAKE_STD_ZVAL(zresult_cas); ZVAL_NULL(zresult_cas); params [0] = &zcookie; params [1] = &zkey; params [2] = &zvalue; params [3] = &zflags; params [4] = &zresult_cas; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_GET), params, 5 TSRMLS_CC); /* Succeeded in getting the key */ if (retval == PROTOCOL_BINARY_RESPONSE_SUCCESS) { uint32_t flags = 0; uint64_t result_cas = 0; if (Z_TYPE_P (zvalue) == IS_NULL) { zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zvalue); zval_ptr_dtor (&zflags); zval_ptr_dtor (&zresult_cas); return PROTOCOL_BINARY_RESPONSE_KEY_ENOENT; } if (Z_TYPE_P (zvalue) != IS_STRING) { convert_to_string (zvalue); } if (Z_TYPE_P (zflags) == IS_LONG) { flags = Z_LVAL_P (zflags); } MEMC_MAKE_RESULT_CAS(zresult_cas, result_cas); retval = response_handler(cookie, key, key_len, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), flags, result_cas); } zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zvalue); zval_ptr_dtor (&zflags); zval_ptr_dtor (&zresult_cas); return retval; } static protocol_binary_response_status s_noop_handler(const void *cookie) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie; zval **params [1]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_NOOP)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); params [0] = &zcookie; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_NOOP), params, 1 TSRMLS_CC); zval_ptr_dtor (&zcookie); return retval; } static protocol_binary_response_status s_quit_handler(const void *cookie) { zval **params [1]; protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_QUIT)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); params [0] = &zcookie; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_QUIT), params, 1 TSRMLS_CC); zval_ptr_dtor (&zcookie); return retval; } static protocol_binary_response_status s_set_replace_handler (php_memc_event_t event, const void *cookie, const void *key, uint16_t key_len, const void *data, uint32_t data_len, uint32_t flags, uint32_t expiration, uint64_t cas, uint64_t *result_cas) { protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zkey, *zdata, *zflags, *zexpiration, *zcas, *zresult_cas; zval **params [7]; TSRMLS_FETCH(); if (!MEMC_HAS_CB(event)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zkey); ZVAL_STRINGL(zkey, key, key_len, 1); MAKE_STD_ZVAL(zdata); ZVAL_STRINGL(zdata, ((char *) data), (int) data_len, 1); MAKE_STD_ZVAL(zflags); ZVAL_LONG(zflags, (long) flags); MAKE_STD_ZVAL(zexpiration); ZVAL_LONG(zexpiration, (long) expiration); MAKE_STD_ZVAL(zcas); ZVAL_DOUBLE(zcas, (double) cas); MAKE_STD_ZVAL(zresult_cas); ZVAL_NULL(zresult_cas); params [0] = &zcookie; params [1] = &zkey; params [2] = &zdata; params [3] = &zflags; params [4] = &zexpiration; params [5] = &zcas; params [6] = &zresult_cas; retval = s_invoke_php_callback (&MEMC_GET_CB(event), params, 7 TSRMLS_CC); MEMC_MAKE_RESULT_CAS(zresult_cas, *result_cas); zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zdata); zval_ptr_dtor (&zflags); zval_ptr_dtor (&zexpiration); zval_ptr_dtor (&zcas); zval_ptr_dtor (&zresult_cas); return retval; } static protocol_binary_response_status s_replace_handler (const void *cookie, const void *key, uint16_t key_len, const void *data, uint32_t data_len, uint32_t flags, uint32_t expiration, uint64_t cas, uint64_t *result_cas) { return s_set_replace_handler (MEMC_SERVER_ON_REPLACE, cookie, key, key_len, data, data_len, flags, expiration, cas, result_cas); } static protocol_binary_response_status s_set_handler (const void *cookie, const void *key, uint16_t key_len, const void *data, uint32_t data_len, uint32_t flags, uint32_t expiration, uint64_t cas, uint64_t *result_cas) { return s_set_replace_handler (MEMC_SERVER_ON_SET, cookie, key, key_len, data, data_len, flags, expiration, cas, result_cas); } static protocol_binary_response_status s_stat_handler (const void *cookie, const void *key, uint16_t key_len, memcached_binary_protocol_stat_response_handler response_handler) { zval **params [3]; protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zkey, *zbody; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_STAT)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zkey); ZVAL_STRINGL(zkey, key, key_len, 1); MAKE_STD_ZVAL(zbody); ZVAL_NULL(zbody); params [0] = &zcookie; params [1] = &zkey; params [2] = &zbody; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_STAT), params, 3 TSRMLS_CC); if (retval == PROTOCOL_BINARY_RESPONSE_SUCCESS) { if (Z_TYPE_P (zbody) == IS_NULL) { retval = response_handler(cookie, NULL, 0, NULL, 0); } else { if (Z_TYPE_P (zbody) != IS_STRING) { convert_to_string (zbody); } retval = response_handler(cookie, key, key_len, Z_STRVAL_P (zbody), (uint32_t) Z_STRLEN_P (zbody)); } } zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zkey); zval_ptr_dtor (&zbody); return retval; } static protocol_binary_response_status s_version_handler (const void *cookie, memcached_binary_protocol_version_response_handler response_handler) { zval **params [2]; protocol_binary_response_status retval = PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND; zval *zcookie, *zversion; TSRMLS_FETCH(); if (!MEMC_HAS_CB(MEMC_SERVER_ON_VERSION)) { return retval; } MEMC_MAKE_ZVAL_COOKIE(zcookie, cookie); MAKE_STD_ZVAL(zversion); ZVAL_NULL(zversion); params [0] = &zcookie; params [1] = &zversion; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_VERSION), params, 2 TSRMLS_CC); if (retval == PROTOCOL_BINARY_RESPONSE_SUCCESS) { if (Z_TYPE_P (zversion) != IS_STRING) { convert_to_string (zversion); } retval = response_handler (cookie, Z_STRVAL_P(zversion), (uint32_t) Z_STRLEN_P(zversion)); } zval_ptr_dtor (&zcookie); zval_ptr_dtor (&zversion); return retval; } // libevent callbacks static void s_handle_memcached_event (evutil_socket_t fd, short what, void *arg) { int rc; short flags = 0; php_memc_client_t *client = (php_memc_client_t *) arg; memcached_protocol_event_t events; TSRMLS_FETCH(); if (!client->on_connect_invoked) { if (MEMC_HAS_CB(MEMC_SERVER_ON_CONNECT)) { zval *zremoteip, *zremoteport; zval **params [2]; protocol_binary_response_status retval; struct sockaddr_in addr_in; socklen_t addr_in_len = sizeof(addr_in); MAKE_STD_ZVAL(zremoteip); MAKE_STD_ZVAL(zremoteport); if (getpeername (fd, (struct sockaddr *) &addr_in, &addr_in_len) == 0) { ZVAL_STRING(zremoteip, inet_ntoa (addr_in.sin_addr), 1); ZVAL_LONG(zremoteport, ntohs (addr_in.sin_port)); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "getpeername failed: %s", strerror (errno)); ZVAL_NULL(zremoteip); ZVAL_NULL(zremoteport); } params [0] = &zremoteip; params [1] = &zremoteport; retval = s_invoke_php_callback (&MEMC_GET_CB(MEMC_SERVER_ON_CONNECT), params, 2 TSRMLS_CC); zval_ptr_dtor (&zremoteip); zval_ptr_dtor (&zremoteport); if (retval != PROTOCOL_BINARY_RESPONSE_SUCCESS) { memcached_protocol_client_destroy (client->protocol_client); efree (client); evutil_closesocket (fd); return; } } client->on_connect_invoked = 1; } events = memcached_protocol_client_work (client->protocol_client); if (events & MEMCACHED_PROTOCOL_ERROR_EVENT) { memcached_protocol_client_destroy (client->protocol_client); efree (client); evutil_closesocket (fd); return; } if (events & MEMCACHED_PROTOCOL_WRITE_EVENT) { flags = EV_WRITE; } if (events & MEMCACHED_PROTOCOL_READ_EVENT) { flags |= EV_READ; } rc = event_base_once (client->event_base, fd, flags, s_handle_memcached_event, client, NULL); if (rc != 0) { php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to schedule events"); } } static void s_accept_cb (evutil_socket_t fd, short what, void *arg) { int rc; php_memc_client_t *client; struct sockaddr_storage addr; socklen_t addr_len; evutil_socket_t sock; php_memc_proto_handler_t *handler = (php_memc_proto_handler_t *) arg; TSRMLS_FETCH(); /* Accept the connection */ addr_len = sizeof (addr); sock = accept (fd, (struct sockaddr *) &addr, &addr_len); if (sock == -1) { php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to accept the client: %s", strerror (errno)); return; } client = ecalloc (1, sizeof (php_memc_client_t)); client->protocol_client = memcached_protocol_create_client (handler->protocol_handle, sock); client->event_base = handler->event_base; client->on_connect_invoked = 0; if (!client->protocol_client) { php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to allocate protocol client"); efree (client); evutil_closesocket (sock); return; } // TODO: this should timeout rc = event_base_once (handler->event_base, sock, EV_READ, s_handle_memcached_event, client, NULL); if (rc != 0) { php_error_docref (NULL TSRMLS_CC, E_WARNING, "Failed to add event for client"); memcached_protocol_client_destroy (client->protocol_client); efree (client); evutil_closesocket (sock); return; } } php_memc_proto_handler_t *php_memc_proto_handler_new () { php_memc_proto_handler_t *handler = ecalloc (1, sizeof (php_memc_proto_handler_t)); handler->protocol_handle = memcached_protocol_create_instance (); assert (handler->protocol_handle); memset (&handler->callbacks, 0, sizeof (memcached_binary_protocol_callback_st)); handler->callbacks.interface_version = MEMCACHED_PROTOCOL_HANDLER_V1; handler->callbacks.interface.v1.add = s_add_handler; handler->callbacks.interface.v1.append = s_append_handler; handler->callbacks.interface.v1.decrement = s_decrement_handler; handler->callbacks.interface.v1.delete_object = s_delete_handler; handler->callbacks.interface.v1.flush_object = s_flush_handler; handler->callbacks.interface.v1.get = s_get_handler; handler->callbacks.interface.v1.increment = s_increment_handler; handler->callbacks.interface.v1.noop = s_noop_handler; handler->callbacks.interface.v1.prepend = s_prepend_handler; handler->callbacks.interface.v1.quit = s_quit_handler; handler->callbacks.interface.v1.replace = s_replace_handler; handler->callbacks.interface.v1.set = s_set_handler; handler->callbacks.interface.v1.stat = s_stat_handler; handler->callbacks.interface.v1.version = s_version_handler; memcached_binary_protocol_set_callbacks(handler->protocol_handle, &handler->callbacks); return handler; } static evutil_socket_t s_create_listening_socket (const char *spec) { evutil_socket_t sock; struct sockaddr_storage addr; int addr_len; int rc; TSRMLS_FETCH(); addr_len = sizeof (struct sockaddr); rc = evutil_parse_sockaddr_port (spec, (struct sockaddr *) &addr, &addr_len); if (rc != 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse bind address"); return -1; } sock = socket (AF_INET, SOCK_STREAM, 0); if (sock < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket failed: %s", strerror (errno)); return -1; } rc = bind (sock, (struct sockaddr *) &addr, addr_len); if (rc < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind failed: %s", strerror (errno)); return -1; } rc = listen (sock, 1024); if (rc < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen failed: %s", strerror (errno)); return -1; } rc = evutil_make_socket_nonblocking (sock); if (rc != 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to make socket non-blocking: %s", strerror (errno)); return -1; } rc = evutil_make_listen_socket_reuseable (sock); if (rc != 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to make socket reuseable: %s", strerror (errno)); return -1; } rc = evutil_make_socket_closeonexec (sock); if (rc != 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to make socket closeonexec: %s", strerror (errno)); return -1; } return sock; } zend_bool php_memc_proto_handler_run (php_memc_proto_handler_t *handler, const char *address) { struct event *accept_event; evutil_socket_t sock = s_create_listening_socket (address); TSRMLS_FETCH(); if (sock == -1) { return 0; } handler->event_base = event_base_new(); if (!handler->event_base) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "failed to allocate memory: %s", strerror (errno)); } accept_event = event_new (handler->event_base, sock, EV_READ | EV_PERSIST, s_accept_cb, handler); if (!accept_event) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "failed to allocate memory: %s", strerror (errno)); } event_add (accept_event, NULL); switch (event_base_dispatch (handler->event_base)) { case -1: php_error_docref(NULL TSRMLS_CC, E_ERROR, "event_base_dispatch() failed: %s", strerror (errno)); return 0; break; case 1: php_error_docref(NULL TSRMLS_CC, E_ERROR, "no events registered"); return 0; break; default: return 1; break; } } void php_memc_proto_handler_destroy (php_memc_proto_handler_t **ptr) { php_memc_proto_handler_t *handler = *ptr; if (handler->protocol_handle) memcached_protocol_destroy_instance (handler->protocol_handle); efree (handler); *ptr = NULL; }/**************************************************************** * * The author of this software is David M. Gay. * * Copyright (c) 1991, 1996 by Lucent Technologies. * * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. * ***************************************************************/ /* g_fmt(buf,x) stores the closest decimal approximation to x in buf; * it suffices to declare buf * char buf[32]; */ /* Modified for use with php in the memcached client extension. * * // Teddy Grenman <teddy.grenman@iki.fi>, 2010-05-18. */ #include <zend_operators.h> char *php_memcached_g_fmt(register char *b, double x) { register int i, k; register char *s; int decpt, j, sign; char *b0, *s0, *se; b0 = b; #ifdef IGNORE_ZERO_SIGN if (!x) { *b++ = '0'; *b = 0; goto done; } #endif s = s0 = zend_dtoa(x, 0, 0, &decpt, &sign, &se); if (sign) *b++ = '-'; if (decpt == 9999) /* Infinity or Nan */ { while((*b++ = *s++)); goto done0; } if (decpt <= -4 || decpt > se - s + 5) { *b++ = *s++; if (*s) { *b++ = '.'; while((*b = *s++)) b++; } *b++ = 'e'; /* sprintf(b, "%+.2d", decpt - 1); */ if (--decpt < 0) { *b++ = '-'; decpt = -decpt; } else *b++ = '+'; for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); for(;;) { i = decpt / k; *b++ = i + '0'; if (--j <= 0) break; decpt -= i*k; decpt *= 10; } *b = 0; } else if (decpt <= 0) { *b++ = '.'; for(; decpt < 0; decpt++) *b++ = '0'; while((*b++ = *s++)); } else { while((*b = *s++)) { b++; if (--decpt == 0 && *s) *b++ = '.'; } for(; decpt > 0; decpt--) *b++ = '0'; *b = 0; } done0: zend_freedtoa(s0); #ifdef IGNORE_ZERO_SIGN done: #endif return b0; } /**************************************************************** * * The author of this software is David M. Gay. * * Copyright (c) 1991, 1996 by Lucent Technologies. * * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. * ***************************************************************/ /* g_fmt(buf,x) stores the closest decimal approximation to x in buf; * it suffices to declare buf * char buf[32]; */ /* Modified for use with php in the memcached client * extension by Teddy Grenman, 2010. */ #ifndef MEMC_G_FMT_H #define MEMC_G_FMT_H char *php_memcached_g_fmt(register char *b, double x); #endif /* FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) /* * Always check for bound when decompressing. * Generally it is best to leave it defined. */ #define FASTLZ_SAFE /* * Give hints to the compiler for branch prediction optimization. */ #if defined(__GNUC__) && (__GNUC__ > 2) #define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) #define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) #else #define FASTLZ_EXPECT_CONDITIONAL(c) (c) #define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) #endif /* * Use inlined functions for supported systems. */ #if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) #define FASTLZ_INLINE inline #elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) #define FASTLZ_INLINE __inline #else #define FASTLZ_INLINE #endif /* * Prevent accessing more than 8-bit at once, except on x86 architectures. */ #if !defined(FASTLZ_STRICT_ALIGN) #define FASTLZ_STRICT_ALIGN #if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ #undef FASTLZ_STRICT_ALIGN #elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ #undef FASTLZ_STRICT_ALIGN #elif defined(_M_IX86) /* Intel, MSVC */ #undef FASTLZ_STRICT_ALIGN #elif defined(__386) #undef FASTLZ_STRICT_ALIGN #elif defined(_X86_) /* MinGW */ #undef FASTLZ_STRICT_ALIGN #elif defined(__I86__) /* Digital Mars */ #undef FASTLZ_STRICT_ALIGN #endif #endif /* * FIXME: use preprocessor magic to set this on different platforms! */ typedef unsigned char flzuint8; typedef unsigned short flzuint16; typedef unsigned int flzuint32; /* prototypes */ int fastlz_compress(const void* input, int length, void* output); int fastlz_compress_level(int level, const void* input, int length, void* output); int fastlz_decompress(const void* input, int length, void* output, int maxout); #define MAX_COPY 32 #define MAX_LEN 264 /* 256 + 8 */ #define MAX_DISTANCE 8192 #if !defined(FASTLZ_STRICT_ALIGN) #define FASTLZ_READU16(p) *((const flzuint16*)(p)) #else #define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8) #endif #define HASH_LOG 13 #define HASH_SIZE (1<< HASH_LOG) #define HASH_MASK (HASH_SIZE-1) #define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; } #undef FASTLZ_LEVEL #define FASTLZ_LEVEL 1 #undef FASTLZ_COMPRESSOR #undef FASTLZ_DECOMPRESSOR #define FASTLZ_COMPRESSOR fastlz1_compress #define FASTLZ_DECOMPRESSOR fastlz1_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c" #undef FASTLZ_LEVEL #define FASTLZ_LEVEL 2 #undef MAX_DISTANCE #define MAX_DISTANCE 8191 #define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) #undef FASTLZ_COMPRESSOR #undef FASTLZ_DECOMPRESSOR #define FASTLZ_COMPRESSOR fastlz2_compress #define FASTLZ_DECOMPRESSOR fastlz2_decompress static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); #include "fastlz.c" int fastlz_compress(const void* input, int length, void* output) { /* for short block, choose fastlz1 */ if(length < 65536) return fastlz1_compress(input, length, output); /* else... */ return fastlz2_compress(input, length, output); } int fastlz_decompress(const void* input, int length, void* output, int maxout) { /* magic identifier for compression level */ int level = ((*(const flzuint8*)input) >> 5) + 1; if(level == 1) return fastlz1_decompress(input, length, output, maxout); if(level == 2) return fastlz2_decompress(input, length, output, maxout); /* unknown level, trigger error */ return 0; } int fastlz_compress_level(int level, const void* input, int length, void* output) { if(level == 1) return fastlz1_compress(input, length, output); if(level == 2) return fastlz2_compress(input, length, output); return 0; } #else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output) { const flzuint8* ip = (const flzuint8*) input; const flzuint8* ip_bound = ip + length - 2; const flzuint8* ip_limit = ip + length - 12; flzuint8* op = (flzuint8*) output; const flzuint8* htab[HASH_SIZE]; const flzuint8** hslot; flzuint32 hval; flzuint32 copy; /* sanity check */ if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) { if(length) { /* create literal copy only */ *op++ = length-1; ip_bound++; while(ip <= ip_bound) *op++ = *ip++; return length+1; } else return 0; } /* initializes hash table */ for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) *hslot = ip; /* we start with literal copy */ copy = 2; *op++ = MAX_COPY-1; *op++ = *ip++; *op++ = *ip++; /* main loop */ while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) { const flzuint8* ref; flzuint32 distance; /* minimum match length */ flzuint32 len = 3; /* comparison starting-point */ const flzuint8* anchor = ip; /* check for a run */ #if FASTLZ_LEVEL==2 if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) { distance = 1; ip += 3; ref = anchor - 1 + 3; goto match; } #endif /* find potential match */ HASH_FUNCTION(hval,ip); hslot = htab + hval; ref = htab[hval]; /* calculate distance to the match */ distance = anchor - ref; /* update hash table */ *hslot = anchor; /* is this a match? check the first 3 bytes */ if(distance==0 || #if FASTLZ_LEVEL==1 (distance >= MAX_DISTANCE) || #else (distance >= MAX_FARDISTANCE) || #endif *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) goto literal; #if FASTLZ_LEVEL==2 /* far, needs at least 5-byte match */ if(distance >= MAX_DISTANCE) { if(*ip++ != *ref++ || *ip++!= *ref++) goto literal; len += 2; } match: #endif /* last matched byte */ ip = anchor + len; /* distance is biased */ distance--; if(!distance) { /* zero distance means a run */ flzuint8 x = ip[-1]; while(ip < ip_bound) if(*ref++ != x) break; else ip++; } else for(;;) { /* safe because the outer check against ip limit */ if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; if(*ref++ != *ip++) break; while(ip < ip_bound) if(*ref++ != *ip++) break; break; } /* if we have copied something, adjust the copy count */ if(copy) /* copy is biased, '0' means 1 byte copy */ *(op-copy-1) = copy-1; else /* back, to overwrite the copy count */ op--; /* reset literal counter */ copy = 0; /* length is biased, '1' means a match of 3 bytes */ ip -= 3; len = ip - anchor; /* encode the match */ #if FASTLZ_LEVEL==2 if(distance < MAX_DISTANCE) { if(len < 7) { *op++ = (len << 5) + (distance >> 8); *op++ = (distance & 255); } else { *op++ = (7 << 5) + (distance >> 8); for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = (distance & 255); } } else { /* far away, but not yet in the another galaxy... */ if(len < 7) { distance -= MAX_DISTANCE; *op++ = (len << 5) + 31; *op++ = 255; *op++ = distance >> 8; *op++ = distance & 255; } else { distance -= MAX_DISTANCE; *op++ = (7 << 5) + 31; for(len-=7; len >= 255; len-= 255) *op++ = 255; *op++ = len; *op++ = 255; *op++ = distance >> 8; *op++ = distance & 255; } } #else if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2)) while(len > MAX_LEN-2) { *op++ = (7 << 5) + (distance >> 8); *op++ = MAX_LEN - 2 - 7 -2; *op++ = (distance & 255); len -= MAX_LEN-2; } if(len < 7) { *op++ = (len << 5) + (distance >> 8); *op++ = (distance & 255); } else { *op++ = (7 << 5) + (distance >> 8); *op++ = len - 7; *op++ = (distance & 255); } #endif /* update the hash at match boundary */ HASH_FUNCTION(hval,ip); htab[hval] = ip++; HASH_FUNCTION(hval,ip); htab[hval] = ip++; /* assuming literal copy */ *op++ = MAX_COPY-1; continue; literal: *op++ = *anchor++; ip = anchor; copy++; if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) { copy = 0; *op++ = MAX_COPY-1; } } /* left-over as literal copy */ ip_bound++; while(ip <= ip_bound) { *op++ = *ip++; copy++; if(copy == MAX_COPY) { copy = 0; *op++ = MAX_COPY-1; } } /* if we have copied something, adjust the copy length */ if(copy) *(op-copy-1) = copy-1; else op--; #if FASTLZ_LEVEL==2 /* marker for fastlz2 */ *(flzuint8*)output |= (1 << 5); #endif return op - (flzuint8*)output; } static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout) { const flzuint8* ip = (const flzuint8*) input; const flzuint8* ip_limit = ip + length; flzuint8* op = (flzuint8*) output; flzuint8* op_limit = op + maxout; flzuint32 ctrl = (*ip++) & 31; int loop = 1; do { const flzuint8* ref = op; flzuint32 len = ctrl >> 5; flzuint32 ofs = (ctrl & 31) << 8; if(ctrl >= 32) { #if FASTLZ_LEVEL==2 flzuint8 code; #endif len--; ref -= ofs; if (len == 7-1) #if FASTLZ_LEVEL==1 len += *ip++; ref -= *ip++; #else do { code = *ip++; len += code; } while (code==255); code = *ip++; ref -= code; /* match from 16-bit distance */ if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) { ofs = (*ip++) << 8; ofs += *ip++; ref = op - ofs - MAX_DISTANCE; } #endif #ifdef FASTLZ_SAFE if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) return 0; if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) return 0; #endif if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) ctrl = *ip++; else loop = 0; if(ref == op) { /* optimize copy for a run */ flzuint8 b = ref[-1]; *op++ = b; *op++ = b; *op++ = b; for(; len; --len) *op++ = b; } else { #if !defined(FASTLZ_STRICT_ALIGN) const flzuint16* p; flzuint16* q; #endif /* copy from reference */ ref--; *op++ = *ref++; *op++ = *ref++; *op++ = *ref++; #if !defined(FASTLZ_STRICT_ALIGN) /* copy a byte, so that now it's word aligned */ if(len & 1) { *op++ = *ref++; len--; } /* copy 16-bit at once */ q = (flzuint16*) op; op += len; p = (const flzuint16*) ref; for(len>>=1; len > 4; len-=4) { *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = *p++; } for(; len; --len) *q++ = *p++; #else for(; len; --len) *op++ = *ref++; #endif } } else { ctrl++; #ifdef FASTLZ_SAFE if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) return 0; if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) return 0; #endif *op++ = *ip++; for(--ctrl; ctrl; ctrl--) *op++ = *ip++; loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); if(loop) ctrl = *ip++; } } while(FASTLZ_EXPECT_CONDITIONAL(loop)); return op - (flzuint8*)output; } #endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ /* FastLZ - lightning-fast lossless compression library Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FASTLZ_H #define FASTLZ_H #define FASTLZ_VERSION 0x000100 #define FASTLZ_VERSION_MAJOR 0 #define FASTLZ_VERSION_MINOR 0 #define FASTLZ_VERSION_REVISION 0 #define FASTLZ_VERSION_STRING "0.1.0" #if defined (__cplusplus) extern "C" { #endif /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, the return value might be larger than length (input buffer size). The input buffer and the output buffer can not overlap. */ int fastlz_compress(const void* input, int length, void* output); /** Decompress a block of compressed data and returns the size of the decompressed block. If error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) will be returned instead. The input buffer and the output buffer can not overlap. Decompression is memory safe and guaranteed not to write the output buffer more than what is specified in maxout. */ int fastlz_decompress(const void* input, int length, void* output, int maxout); /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, the return value might be larger than length (input buffer size). The input buffer and the output buffer can not overlap. Compression level can be specified in parameter level. At the moment, only level 1 and level 2 are supported. Level 1 is the fastest compression and generally useful for short data. Level 2 is slightly slower but it gives better compression ratio. Note that the compressed data, regardless of the level, can always be decompressed using the function fastlz_decompress above. */ int fastlz_compress_level(int level, const void* input, int length, void* output); #if defined (__cplusplus) } #endif #endif /* FASTLZ_H */ F&2k@$Uа[Z���GBMB