[ * 'adapter' => 'Memcached', * 'host' => '127.0.0.1:11211' * ] * ]); * ``` * * The `'host'` key accepts entries in multiple formats, depending on the number of * Memcache servers you are connecting to. See the `__construct()` method for more * information. * * @link http://php.net/class.memcached.php * @link http://pecl.php.net/package/memcached * @see lithium\storage\cache\adapter\Memcache::__construct() * @see lithium\storage\Cache::key() * @see lithium\storage\Cache::adapter() */ class Memcache extends \lithium\storage\cache\Adapter { /** * The default host used to connect to the server. */ const DEFAULT_HOST = '127.0.0.1'; /** * The default port used to connect to the server. */ const DEFAULT_PORT = 11211; /** * `Memcached` object instance used by this adapter. * * @var object */ public $connection = null; /** * Constructor. Instantiates the `Memcached` object, adds appropriate servers to the pool, * and configures any optional settings passed (see the `_init()` method). * * @see lithium\storage\Cache::config() * @param array $config Configuration for this cache adapter. These settings are queryable * through `Cache::config('name')`. The available options are as follows: * - `'scope'` _string_: Scope which will prefix keys; per default not set. * - `'expiry'` _mixed_: The default expiration time for cache values, if no value * is otherwise set. Can be either a `strtotime()` compatible tring or TTL in * seconds. To indicate items should not expire use `Cache::PERSIST`. Defaults * to `+1 hour`. * - `'host'` _string|array_: A string in the form of `''`, `':'` or * `':'` indicating the host and/or port to connect to. When one or both are * not provided uses general server defaults. * Use the array format for multiple hosts (optionally with server selection weights): * `array('167.221.1.5:11222', '167.221.1.6')` * `array('167.221.1.5:11222' => 200, '167.221.1.6')` * @return void */ public function __construct(array $config = []) { $defaults = [ 'scope' => null, 'expiry' => '+1 hour', 'host' => static::DEFAULT_HOST . ':' . static::DEFAULT_PORT ]; parent::__construct(Set::merge($defaults, $config)); } /** * Generates safe cache keys. * * As per the protocol no control characters or whitespace is allowed * in the key name. There's also a limit of max. 250 characters which is * checked and enforced here. The limit is actually lowered to 250 minus * the length of an crc32b hash minus separator (241) minus scope length * minus separator (241 - x). * * @param array $keys The original keys. * @return array Keys modified and safe to use with adapter. */ public function key(array $keys) { $length = 241 - ($this->_config['scope'] ? strlen($this->_config['scope']) + 1 : 0); return array_map( function($key) use ($length) { $result = substr(preg_replace('/[[:cntrl:]\s]/u', '_', $key), 0, $length); return $key !== $result ? $result . '_' . hash('crc32b', $key) : $result; }, $keys ); } /** * Handles the actual `Memcached` connection and server connection * adding for the adapter constructor and sets prefix using the scope * if provided. * * @return void */ protected function _init() { $this->connection = $this->connection ?: new Memcached(); $this->connection->addServers($this->_formatHostList($this->_config['host'])); if ($this->_config['scope']) { $this->connection->setOption(Memcached::OPT_PREFIX_KEY, "{$this->_config['scope']}:"); } } /** * Dispatches a not-found method to the connection object. That way, one can * easily use a custom method on the adapter. If you want to know, what methods * are available, have a look at the documentation of memcached. * * ``` * Cache::adapter('memcache')->methodName($argument); * ``` * * @link http://php.net/class.memcached.php * @param string $method Name of the method to call. * @param array $params Parameter list to use when calling $method. * @return mixed Returns the result of the method call. */ public function __call($method, $params = []) { return call_user_func_array([&$this->connection, $method], $params); } /** * Determines if a given method can be called. * * @deprecated * @param string $method Name of the method. * @param boolean $internal Provide `true` to perform check from inside the * class/object. When `false` checks also for public visibility; * defaults to `false`. * @return boolean Returns `true` if the method can be called, `false` otherwise. */ public function respondsTo($method, $internal = false) { $message = '`' . __METHOD__ . '()` has been deprecated. '; $message .= 'Use `is_callable([$adapter->connection, \'\'])` instead.'; trigger_error($message, E_USER_DEPRECATED); if (parent::respondsTo($method, $internal)) { return true; } return is_callable([$this->connection, $method]); } /** * Formats standard `'host:port'` strings into arrays used by `Memcached`. * * @param mixed $host A host string in `'host:port'` format, or an array of host strings * optionally paired with relative selection weight values. * @return array Returns an array of `Memcached` server definitions. */ protected function _formatHostList($host) { $hosts = []; foreach ((array) $this->_config['host'] as $host => $weight) { $host = HostString::parse(($hasWeight = is_integer($weight)) ? $host : $weight) + [ 'host' => static::DEFAULT_HOST, 'port' => static::DEFAULT_PORT ]; $host = [$host['host'], $host['port']]; if ($hasWeight) { $host[] = $weight; } $hosts[] = $host; } return $hosts; } /** * Write values to the cache. All items to be cached will receive an * expiration time of `$expiry`. * * Expiration is always based off the current unix time in order to gurantee we never * exceed the TTL limit of 30 days when specifying the TTL directly. * * @param array $keys Key/value pairs with keys to uniquely identify the to-be-cached item. * @param string|integer $expiry A `strtotime()` compatible cache time or TTL in seconds. * To persist an item use `\lithium\storage\Cache::PERSIST`. * @return boolean `true` on successful write, `false` otherwise. */ public function write(array $keys, $expiry = null) { $expiry = $expiry || $expiry === Cache::PERSIST ? $expiry : $this->_config['expiry']; if (!$expiry || $expiry === Cache::PERSIST) { $expires = 0; } elseif (is_int($expiry)) { $expires = $expiry + time(); } else { $expires = strtotime($expiry); } if (count($keys) > 1) { return $this->connection->setMulti($keys, $expires); } return $this->connection->set(key($keys), current($keys), $expires); } /** * Read values from the cache. Will attempt to return an array of data * containing key/value pairs of the requested data. * * @param array $keys Keys to uniquely identify the cached items. * @return array Cached values keyed by cache keys on successful read, * keys which could not be read will not be included in * the results array. */ public function read(array $keys) { if (count($keys) > 1) { if (!$results = $this->connection->getMulti($keys)) { return []; } } else { $result = $this->connection->get($key = current($keys)); if ($result === false && $this->connection->getResultCode() === Memcached::RES_NOTFOUND) { return []; } $results = [$key => $result]; } return $results; } /** * Will attempt to remove specified keys from the user space cache. * * @param array $keys Keys to uniquely identify the cached items. * @return boolean `true` on successful delete, `false` otherwise. */ public function delete(array $keys) { if (count($keys) > 1) { return $this->connection->deleteMulti($keys); } return $this->connection->delete(current($keys)); } /** * Performs an atomic decrement operation on specified numeric cache item. * * Note that, as per the Memcached specification: * "If the item's value is not numeric, it is treated as if the value were 0. * If the operation would decrease the value below 0, the new value will be 0." * * @link http://php.net/manual/memcached.decrement.php * @param string $key Key of numeric cache item to decrement. * @param integer $offset Offset to decrement - defaults to `1`. * @return integer|boolean The item's new value on successful decrement, else `false`. */ public function decrement($key, $offset = 1) { return $this->connection->decrement($key, $offset); } /** * Performs an atomic increment operation on specified numeric cache item. * * Note that, as per the Memcached specification: * "If the item's value is not numeric, it is treated as if the value were 0." * * @link http://php.net/manual/memcached.decrement.php * @param string $key Key of numeric cache item to increment. * @param integer $offset Offset to increment - defaults to `1`. * @return integer|boolean The item's new value on successful increment, else `false`. */ public function increment($key, $offset = 1) { return $this->connection->increment($key, $offset); } /** * Clears entire cache by flushing it. All cache keys using the * configuration but *without* honoring the scope are removed. * * Internally keys are not removed but invalidated. Thus this * operation doesn't actually free memory on the instance. * * The behavior and result when removing a single key * during this process fails is unknown. * * @return boolean `true` on successful clearing, `false` otherwise. */ public function clear() { return $this->connection->flush(); } /** * Determines if the `Memcached` extension has been installed. * * @return boolean Returns `true` if the `Memcached` extension is installed and enabled, `false` * otherwise. */ public static function enabled() { return extension_loaded('memcached'); } } ?>