Memcache.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\storage\cache\adapter;
  9. use Memcached;
  10. use lithium\util\Set;
  11. /**
  12. * A Memcache (libmemcached) cache adapter implementation. Requires
  13. * [pecl/memcached](http://pecl.php.net/package/memcached).
  14. *
  15. * The `Memcache` cache adapter is meant to be used through the `Cache` interface,
  16. * which abstracts away key generation, adapter instantiation and filter
  17. * implementation.
  18. *
  19. * A simple configuration of this adapter can be accomplished in `config/bootstrap/cache.php`
  20. * as follows:
  21. *
  22. * {{{
  23. * Cache::config(array(
  24. * 'cache-config-name' => array(
  25. * 'adapter' => 'Memcached',
  26. * 'host' => '127.0.0.1:11211'
  27. * )
  28. * ));
  29. * }}}
  30. *
  31. * The `'host'` key accepts entries in multiple formats, depending on the number of Memcache servers
  32. * you are connecting to. See the `__construct()` method for more information.
  33. *
  34. * This Memcache adapter provides basic support for `write`, `read`, `delete`
  35. * and `clear` cache functionality, as well as allowing the first four
  36. * methods to be filtered as per the Lithium filtering system.
  37. *
  38. * This adapter supports multi-key `write` and `read` operations.
  39. *
  40. * @see lithium\storage\cache\adapter\Memcache::__construct()
  41. * @see lithium\storage\Cache::key()
  42. * @see lithium\storage\Cache::adapter()
  43. */
  44. class Memcache extends \lithium\core\Object {
  45. /**
  46. * The default port used to connect to Memcache servers, if none is specified.
  47. */
  48. const CONN_DEFAULT_PORT = 11211;
  49. /**
  50. * `Memcached` object instance used by this adapter.
  51. *
  52. * @var object
  53. */
  54. public $connection = null;
  55. /**
  56. * Object constructor. Instantiates the `Memcached` object, adds appropriate servers to the
  57. * pool, and configures any optional settings passed (see the `_init()` method). When adding
  58. * servers, the following formats are valid for the `'host'` key:
  59. *
  60. * - `'127.0.0.1'`: Configure the adapter to connect to one Memcache server on the default port.
  61. * - `'127.0.0.1:11222'`: Configure the adapter to connect to one Memcache server on a custom
  62. * port.
  63. * - `array('167.221.1.5:11222' => 200, '167.221.1.6')`: Connect to one server on a
  64. * custom port with a high selection weight, and a second server on the default port with the
  65. * default selection weight.
  66. *
  67. * @see lithium\storage\Cache::config()
  68. * @param array $config Configuration parameters for this cache adapter.
  69. * These settings are indexed by name and queryable through
  70. * `Cache::config('name')`. The available options are as follows:
  71. * - `'expiry'` _mixed_: The default expiration time for cache values, if no value
  72. * is otherwise set. See the `$expiry` parameter of `Memcache::write()`.
  73. * - `'host'` _mixed_: Specifies one or more Memcache servers to connect to, with
  74. * optional server selection weights. See above for example values.
  75. */
  76. public function __construct(array $config = array()) {
  77. $defaults = array(
  78. 'expiry' => '+1 hour',
  79. 'host' => '127.0.0.1'
  80. );
  81. parent::__construct(Set::merge($defaults, $config));
  82. }
  83. /**
  84. * Handles the actual `Memcached` connection and server connection adding for the adapter
  85. * constructor.
  86. *
  87. * @return void
  88. */
  89. protected function _init() {
  90. $this->connection = $this->connection ?: new Memcached();
  91. $servers = array();
  92. if (isset($this->_config['servers'])) {
  93. $this->connection->addServers($this->_config['servers']);
  94. return;
  95. }
  96. $this->connection->addServers($this->_formatHostList($this->_config['host']));
  97. }
  98. /**
  99. * Formats standard `'host:port'` strings into arrays used by `Memcached`.
  100. *
  101. * @param mixed $host A host string in `'host:port'` format, or an array of host strings
  102. * optionally paired with relative selection weight values.
  103. * @return array Returns an array of `Memcached` server definitions.
  104. */
  105. protected function _formatHostList($host) {
  106. $fromString = function($host) {
  107. if (strpos($host, ':')) {
  108. list($host, $port) = explode(':', $host);
  109. return array($host, intval($port));
  110. }
  111. return array($host, Memcache::CONN_DEFAULT_PORT);
  112. };
  113. if (is_string($host)) {
  114. return array($fromString($host));
  115. }
  116. $servers = array();
  117. while (list($server, $weight) = each($this->_config['host'])) {
  118. if (is_string($weight)) {
  119. $servers[] = $fromString($weight);
  120. continue;
  121. }
  122. $server = $fromString($server);
  123. $server[] = $weight;
  124. $servers[] = $server;
  125. }
  126. return $servers;
  127. }
  128. /**
  129. * Write value(s) to the cache.
  130. *
  131. * This adapter method supports multi-key write. By specifying `$key` as an
  132. * associative array of key/value pairs, `$data` is ignored and all keys that
  133. * are cached will receive an expiration time of `$expiry`.
  134. *
  135. * @param string|array $key The key to uniquely identify the cached item.
  136. * @param mixed $value The value to be cached.
  137. * @param mixed $expiry A Unix timestamp or `strtotime()`-compatible string indicating when
  138. * `$value` should expire. If no expiry time is set, then the default cache
  139. * expiration time set with the cache configuration will be used.
  140. * @return closure Function returning boolean `true` on successful write, `false` otherwise.
  141. */
  142. public function write($key, $value, $expiry = null) {
  143. $connection =& $this->connection;
  144. $expiry = ($expiry) ?: $this->_config['expiry'];
  145. return function($self, $params) use (&$connection, $expiry) {
  146. $expires = is_int($expiry) ? $expiry : strtotime($expiry);
  147. $key = $params['key'];
  148. if (is_array($key)) {
  149. return $connection->setMulti($key, $expires);
  150. }
  151. return $connection->set($key, $params['data'], $expires);
  152. };
  153. }
  154. /**
  155. * Read value(s) from the cache.
  156. *
  157. * This adapter method supports multi-key reads. By specifying `$key` as an
  158. * array of key names, this adapter will attempt to return an array of data
  159. * containing key/value pairs of the requested data.
  160. *
  161. * @param string|array $key The key to uniquely identify the cached item.
  162. * @return closure Function returning cached value if successful, `null` otherwise.
  163. */
  164. public function read($key) {
  165. $connection =& $this->connection;
  166. return function($self, $params) use (&$connection) {
  167. $key = $params['key'];
  168. if (is_array($key)) {
  169. return $connection->getMulti($key);
  170. }
  171. if (($result = $connection->get($key)) === false) {
  172. if ($connection->getResultCode() === Memcached::RES_NOTFOUND) {
  173. $result = null;
  174. }
  175. }
  176. return $result;
  177. };
  178. }
  179. /**
  180. * Delete value from the cache.
  181. *
  182. * @param string $key The key to uniquely identify the cached item.
  183. * @return closure Function returning `true` on successful delete, `false` otherwise.
  184. */
  185. public function delete($key) {
  186. $connection =& $this->connection;
  187. return function($self, $params) use (&$connection) {
  188. return $connection->delete($params['key']);
  189. };
  190. }
  191. /**
  192. * Performs an atomic decrement operation on specified numeric cache item.
  193. *
  194. * Note that, as per the Memcached specification:
  195. * "If the item's value is not numeric, it is treated as if the value were 0.
  196. * If the operation would decrease the value below 0, the new value will be 0."
  197. * (see http://www.php.net/manual/memcached.decrement.php)
  198. *
  199. * @param string $key Key of numeric cache item to decrement
  200. * @param integer $offset Offset to decrement - defaults to 1.
  201. * @return closure Function returning item's new value on successful decrement, else `false`
  202. */
  203. public function decrement($key, $offset = 1) {
  204. $connection =& $this->connection;
  205. return function($self, $params) use (&$connection, $offset) {
  206. return $connection->decrement($params['key'], $offset);
  207. };
  208. }
  209. /**
  210. * Performs an atomic increment operation on specified numeric cache item.
  211. *
  212. * Note that, as per the Memcached specification:
  213. * "If the item's value is not numeric, it is treated as if the value were 0."
  214. * (see http://www.php.net/manual/memcached.decrement.php)
  215. *
  216. * @param string $key Key of numeric cache item to increment
  217. * @param integer $offset Offset to increment - defaults to 1.
  218. * @return closure Function returning item's new value on successful increment, else `false`
  219. */
  220. public function increment($key, $offset = 1) {
  221. $connection =& $this->connection;
  222. return function($self, $params) use (&$connection, $offset) {
  223. return $connection->increment($params['key'], $offset);
  224. };
  225. }
  226. /**
  227. * Clears user-space cache.
  228. *
  229. * @return mixed Returns `true` on successful clear, `false` otherwise.
  230. */
  231. public function clear() {
  232. return $this->connection->flush();
  233. }
  234. /**
  235. * Determines if the `Memcached` extension has been installed.
  236. *
  237. * @return boolean Returns `true` if the `Memcached` extension is installed and enabled, `false`
  238. * otherwise.
  239. */
  240. public static function enabled() {
  241. return extension_loaded('memcached');
  242. }
  243. }
  244. ?>