[ * 'adapter' => 'Apc' * ], * 'distributed' => [ * 'adapter' => 'Memcached', * 'host' => '127.0.0.1:11211' * ], * 'default' => [ * 'adapter' => 'File', * 'strategies => ['Serializer'] * ] * ]; * ``` * * Adapter configurations can be scoped, adapters will then handle the * namespacing of the keys transparently for you: * * ``` * Cache::config([ * 'primary' => ['adapter' => 'Apc', 'scope' => 'primary'], * 'secondary' => ['adapter' => 'Apc', 'scope' => 'secondary'] * ]; * ``` * * Cache adapters differ in the functionality they provide and how the provide it. To see * if an adapter meets your requirement and for more information on the specifics * (i.e. atomicity of operations), consult the documentation the adapter first. * * All adapters will provide `write`, `read`, `delete` and `increment`/`decrement` functionality. On * top of that adapters may provide `clean` and `clear` functionality as well as direct access to * additional methods. Which allows for a very wide range of flexibility at the cost of portability. * * ``` * Cache::adapter('default')->methodName($argument); * ``` * * @see lithium\core\Adaptable * @see lithium\storage\cache\Adapter * @see lithium\storage\cache\adapter */ class Cache extends \lithium\core\Adaptable { /** * Can be used for expiry parameters or configuration options to * specify that a cached item should persist as long and expire as * late as possible. */ const PERSIST = 0; /** * Stores configurations for cache adapters. * * @var array */ protected static $_configurations = []; /** * Libraries::locate() compatible path to adapters for this class. * * @var string Dot-delimited path. */ protected static $_adapters = 'adapter.storage.cache'; /** * Libraries::locate() compatible path to strategies for this class. * * @var string Dot-delimited path. */ protected static $_strategies = 'strategy.storage.cache'; /** * Generates one or multiple safe cache keys optionally adding a suffix * with a hash over the provided data. The hash value is generated in * an optimized way dependending on the type of data. * * Simple usage (in this case noop): * ``` * Cache::key('default', 'post'); * // returns `'post'` * * Cache::key('default', ['posts', 'banners']); * // returns `['posts', 'banners']` * * Cache::key('default', ['posts' => 'foo', 'banners' => 'bar]); * // returns `['posts' => 'foo', 'banners' => 'bar']` * ``` * * Make a key safe to use with adapter (exact result depends * on key constraints enforced by the selected adapter: * ``` * Cache::key('default', 'posts for Helgi Þorbjörnsson'); * // returns `'posts_for_Helgi__orbj_rnsson_c7f8433a'` * ``` * * Using additional scalar or non-scalar data to generate key: * ``` * Cache::key('default', 'post', 2); * // returns `'post:1ad5be0d'` * * Cache::key('default', 'post', array(2, 'json')); * // returns `'post:723f0e19'` * * Cache::key('default', ['posts', 'banners'], 'json'); * // returns `['posts:6b072545', 'banners:6b072545']` * * Cache::key('default', ['posts' => 'foo', 'banners' => 'bar'], 'json'); * // returns `['posts:38ec40e5' => 'foo', 'banners:38ec40e5' => 'bar']` * ``` * * Or with a resuable key generator function: * ``` * $posts[0] = ['id' => 1]; * $posts[1] = ['id' => 2]; * * $key = function($data) { return 'post:' . $data['id']}; * * Cache::key('default', $key, $post[0]); // returns `'post:1'` * Cache::key('default', $key, $post[1]); // returns `'post:2'` * ``` * * This example shows a key mutating generator function: * ``` * $base = 'post'; * $key = function($id) use (&base) { return $base .= ":{$id}"; }; * * Cache::key('default', $key, 1); // returns `'post:1'` * Cache::key('default', $key, 2); // returns `'post:1:2'` * ``` * * @param string $name Configuration to be used for generating key/s. * @param mixed $key String or an array of strings that will be used as the cache key/s. * Also accepts associative arrays where the key part will be modified, but * the value left untouched. Also accepts a key generator function that * is passed $data and must return a string that will be used as the key. * @param mixed $data Additional data to use when generating key. Can be any kind of type except * a resource. The method will calculate a hash of the data and append that to * the key/s. When $key is a function the data is passed to it instead. * @return string|array The generated cache key/s. */ public static function key($name, $key, $data = null) { $adapter = static::adapter($name); if (is_callable($key)) { return current($adapter->key([$key($data)])); } $keys = ($isMulti = is_array($key)) ? $key : [$key]; $keys = ($hasData = !is_integer(key($keys))) ? array_keys($keys) : $keys; if ($data !== null) { $data = Hash::calculate($data, ['type' => 'crc32b']); $keys = array_map(function($key) use ($data) { return $key .= ":{$data}"; }, $keys); } $keys = $adapter->key($keys); $keys = $hasData ? array_combine($keys, array_values((array) $key)) : $keys; return $isMulti ? $keys : current($keys); } /** * Writes to the specified cache configuration. * * Can handle single- and multi-key writes. * * This method has two valid syntaxes depending on if you're storing * data using a single key or multiple keys as outlined below. * ``` * // To write data to a single-key use the following syntax. * Cache::write('default', 'foo', 'bar', '+1 minute'); * * // For multi-key writes the $data parameter's role becomes * // the one of the $expiry parameter. * Cache::write('default', ['foo' => 'bar', ... ], '+1 minute'); * ``` * * These two calls are synonymical and demonstrate the two * possible ways to specify the expiration time. * ``` * Cache::write('default', 'foo', 'bar', '+1 minute'); * Cache::write('default', 'foo', 'bar', 60); * ``` * * @param string $name Configuration to be used for writing. * @param mixed $key Key to uniquely identify the cache entry or an array of key/value pairs * for multi-key writes mapping cache keys to the data to be cached. * @param mixed $data Data to be cached. * @param string|integer $expiry A `strtotime()` compatible cache time. Alternatively an integer * denoting the seconds until the item expires (TTL). If no expiry time is * set, then the default cache expiration time set with the cache adapter * configuration will be used. To persist an item use `Cache::PERSIST`. * @param array $options Options for the method and strategies. * - `'strategies'` _boolean_: Indicates if strategies should be used, * defaults to `true`. * - `'conditions'` _mixed_: A function or item that must return or * evaluate to `true` in order to continue write operation. * @return boolean `true` on successful cache write, `false` otherwise. When writing * multiple items and an error occurs writing any of the items the * whole operation fails and this method will return `false`. * @filter */ public static function write($name, $key, $data = null, $expiry = null, array $options = []) { $options += ['conditions' => null, 'strategies' => true]; if (is_callable($options['conditions']) && !$options['conditions']()) { return false; } try { $adapter = static::adapter($name); } catch (ConfigException $e) { return false; } if (is_array($key)) { $keys = $key; $expiry = $data; } else { $keys = [$key => $data]; } if ($options['strategies']) { foreach ($keys as $key => &$value) { $value = static::applyStrategies(__FUNCTION__, $name, $value, [ 'key' => $key, 'class' => __CLASS__ ]); } } $params = compact('keys', 'expiry'); return Filters::run(get_called_class(), __FUNCTION__, $params, function($params) use ($adapter) { return $adapter->write($params['keys'], $params['expiry']); }); } /** * Reads from the specified cache configuration. * * Can handle single- and multi-key reads. * * Read-through caching can be used by passing expiry and the to-be-cached value * in the `write` option. Following three ways to achieve this. * ``` * Cache::read('default', 'foo', [ * 'write' => ['+5 days' => 'bar'] * ]); // returns `'bar'` * * Cache::read('default', 'foo', [ * 'write' => ['+5 days' => function() { return 'bar'; }] * ]); * * Cache::read('default', 'foo', [ * 'write' => function() { return ['+5 days' => 'bar']; } * ]); * ``` * * @param string $name Configuration to be used for reading. * @param mixed $key Key to uniquely identify the cache entry or an array of keys * for multikey-reads. * @param array $options Options for the method and strategies. * - `'write'`: Allows for read-through caching see description for usage. * - `'strategies'` _boolean_: Indicates if strategies should be used, * defaults to `true`. * - `'conditions'` _mixed_: A function or item that must return or * evaluate to `true` in order to continue write operation. * @return mixed For single-key reads will return the result if the cache * key has been found otherwise returns `null`. When reading * multiple keys a results array is returned mapping keys to * retrieved values. Keys where the value couldn't successfully * been read will not be contained in the results array. * @filter */ public static function read($name, $key, array $options = []) { $options += ['conditions' => null, 'strategies' => true, 'write' => null]; if (is_callable($options['conditions']) && !$options['conditions']()) { return false; } try { $adapter = static::adapter($name); } catch (ConfigException $e) { return false; } if ($isMulti = is_array($key)) { $keys = $key; } else { $keys = [$key]; } $params = compact('keys'); $results = Filters::run(get_called_class(), __FUNCTION__, $params, function($params) use ($adapter) { return $adapter->read($params['keys']); }); if ($write = $options['write']) { $isEvaluated = false; foreach ($keys as $key) { if (isset($results[$key])) { continue; } if (!$isEvaluated) { $write = is_callable($write) ? $write() : $write; $expiry = key($write); $value = current($write); $value = is_callable($value) ? $value() : $value; $isEvaluated = true; } if (!static::write($name, $key, $value, $expiry)) { return false; } $results[$key] = static::applyStrategies('write', $name, $value, [ 'key' => $key, 'mode' => 'LIFO', 'class' => __CLASS__ ]); } } if ($options['strategies']) { foreach ($results as $key => &$result) { $result = static::applyStrategies(__FUNCTION__, $name, $result, [ 'key' => $key, 'mode' => 'LIFO', 'class' => __CLASS__ ]); } } return $isMulti ? $results : ($results ? reset($results) : null); } /** * Deletes using the specified cache configuration. * * Can handle single- and multi-key deletes. * * @param string $name The cache configuration to delete from. * @param mixed $key Key to be deleted or an array of keys to delete. * @param array $options Options for the method and strategies. * - `'conditions'` _mixed_: A function or item that must return or * evaluate to `true` in order to continue write operation. * @return boolean `true` on successful cache delete, `false` otherwise. When deleting * multiple items and an error occurs deleting any of the items the * whole operation fails and this method will return `false`. * @filter */ public static function delete($name, $key, array $options = []) { $options += ['conditions' => null, 'strategies' => true]; if (is_callable($options['conditions']) && !$options['conditions']()) { return false; } try { $adapter = static::adapter($name); } catch (ConfigException $e) { return false; } if (is_array($key)) { $keys = $key; } else { $keys = [$key]; } $params = compact('keys'); return Filters::run(get_called_class(), __FUNCTION__, $params, function($params) use ($adapter) { return $adapter->delete($params['keys']); }); } /** * Performs a increment operation on specified numeric cache item * from the given cache configuration. * * @param string $name Name of the cache configuration to use. * @param string $key Key of numeric cache item to increment * @param integer $offset Offset to increment - defaults to 1. * @param array $options Options for this method. * - `'conditions'`: A function or item that must return or evaluate to * `true` in order to continue operation. * @return integer|boolean Item's new value on successful increment, false otherwise. * @filter */ public static function increment($name, $key, $offset = 1, array $options = []) { $options += ['conditions' => null]; if (is_callable($options['conditions']) && !$options['conditions']()) { return false; } try { $adapter = static::adapter($name); } catch (ConfigException $e) { return false; } $params = compact('key', 'offset'); return Filters::run(get_called_class(), __FUNCTION__, $params, function($params) use ($adapter) { return $adapter->increment($params['key'], $params['offset']); }); } /** * Performs a decrement operation on specified numeric cache item * from the given cache configuration. * * @param string $name Name of the cache configuration to use. * @param string $key Key of numeric cache item to decrement * @param integer $offset Offset to decrement - defaults to 1. * @param array $options Options for this method. * - `'conditions'`: A function or item that must return or evaluate to * `true` in order to continue operation. * @return integer|boolean Item's new value on successful decrement, false otherwise. * @filter */ public static function decrement($name, $key, $offset = 1, array $options = []) { $options += ['conditions' => null]; if (is_callable($options['conditions']) && !$options['conditions']()) { return false; } try { $adapter = static::adapter($name); } catch (ConfigException $e) { return false; } $params = compact('key', 'offset'); return Filters::run(get_called_class(), __FUNCTION__, $params, function($params) use ($adapter) { return $adapter->decrement($params['key'], $params['offset']); }); } /** * Perform garbage collection on specified cache configuration. All invalidated cache * keys - *without* honoring a configured scope - from the specified configuration are * removed. * * @param string $name The cache configuration to be cleaned. * @return boolean `true` on successful cleaning, `false` if failed partially or entirely. */ public static function clean($name) { try { return static::adapter($name)->clean(); } catch (ConfigException $e) { return false; } } /** * Clears entire cache by flushing it. All cache keys - *without* honoring * a configured scope - from the specified configuration are removed. * * @param string $name The cache configuration to be cleared. * @return boolean `true` on successful clearing, `false` if failed partially or entirely. */ public static function clear($name) { try { return static::adapter($name)->clear(); } catch (ConfigException $e) { return false; } } } ?>