MemcacheEngine.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. /**
  3. * Memcache storage engine for cache
  4. *
  5. *
  6. * PHP 5
  7. *
  8. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  9. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. *
  11. * Licensed under The MIT License
  12. * Redistributions of files must retain the above copyright notice.
  13. *
  14. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  15. * @link http://cakephp.org CakePHP(tm) Project
  16. * @package Cake.Cache.Engine
  17. * @since CakePHP(tm) v 1.2.0.4933
  18. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  19. */
  20. /**
  21. * Memcache storage engine for cache. Memcache has some limitations in the amount of
  22. * control you have over expire times far in the future. See MemcacheEngine::write() for
  23. * more information.
  24. *
  25. * @package Cake.Cache.Engine
  26. */
  27. class MemcacheEngine extends CacheEngine {
  28. /**
  29. * Contains the compiled group names
  30. * (prefixed witht the global configuration prefix)
  31. *
  32. * @var array
  33. */
  34. protected $_compiledGroupNames = array();
  35. /**
  36. * Memcache wrapper.
  37. *
  38. * @var Memcache
  39. */
  40. protected $_Memcache = null;
  41. /**
  42. * Settings
  43. *
  44. * - servers = string or array of memcache servers, default => 127.0.0.1. If an
  45. * array MemcacheEngine will use them as a pool.
  46. * - compress = boolean, default => false
  47. *
  48. * @var array
  49. */
  50. public $settings = array();
  51. /**
  52. * Initialize the Cache Engine
  53. *
  54. * Called automatically by the cache frontend
  55. * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
  56. *
  57. * @param array $settings array of setting for the engine
  58. * @return boolean True if the engine has been successfully initialized, false if not
  59. */
  60. public function init($settings = array()) {
  61. if (!class_exists('Memcache')) {
  62. return false;
  63. }
  64. if (!isset($settings['prefix'])) {
  65. $settings['prefix'] = Inflector::slug(APP_DIR) . '_';
  66. }
  67. $settings += array(
  68. 'engine' => 'Memcache',
  69. 'servers' => array('127.0.0.1'),
  70. 'compress' => false,
  71. 'persistent' => true
  72. );
  73. parent::init($settings);
  74. if ($this->settings['compress']) {
  75. $this->settings['compress'] = MEMCACHE_COMPRESSED;
  76. }
  77. if (is_string($this->settings['servers'])) {
  78. $this->settings['servers'] = array($this->settings['servers']);
  79. }
  80. if (!isset($this->_Memcache)) {
  81. $return = false;
  82. $this->_Memcache = new Memcache();
  83. foreach ($this->settings['servers'] as $server) {
  84. list($host, $port) = $this->_parseServerString($server);
  85. if ($this->_Memcache->addServer($host, $port, $this->settings['persistent'])) {
  86. $return = true;
  87. }
  88. }
  89. return $return;
  90. }
  91. return true;
  92. }
  93. /**
  94. * Parses the server address into the host/port. Handles both IPv6 and IPv4
  95. * addresses and Unix sockets
  96. *
  97. * @param string $server The server address string.
  98. * @return array Array containing host, port
  99. */
  100. protected function _parseServerString($server) {
  101. if ($server[0] == 'u') {
  102. return array($server, 0);
  103. }
  104. if (substr($server, 0, 1) == '[') {
  105. $position = strpos($server, ']:');
  106. if ($position !== false) {
  107. $position++;
  108. }
  109. } else {
  110. $position = strpos($server, ':');
  111. }
  112. $port = 11211;
  113. $host = $server;
  114. if ($position !== false) {
  115. $host = substr($server, 0, $position);
  116. $port = substr($server, $position + 1);
  117. }
  118. return array($host, $port);
  119. }
  120. /**
  121. * Write data for key into cache. When using memcache as your cache engine
  122. * remember that the Memcache pecl extension does not support cache expiry times greater
  123. * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
  124. *
  125. * @param string $key Identifier for the data
  126. * @param mixed $value Data to be cached
  127. * @param integer $duration How long to cache the data, in seconds
  128. * @return boolean True if the data was successfully cached, false on failure
  129. * @see http://php.net/manual/en/memcache.set.php
  130. */
  131. public function write($key, $value, $duration) {
  132. if ($duration > 30 * DAY) {
  133. $duration = 0;
  134. }
  135. return $this->_Memcache->set($key, $value, $this->settings['compress'], $duration);
  136. }
  137. /**
  138. * Read a key from the cache
  139. *
  140. * @param string $key Identifier for the data
  141. * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
  142. */
  143. public function read($key) {
  144. return $this->_Memcache->get($key);
  145. }
  146. /**
  147. * Increments the value of an integer cached key
  148. *
  149. * @param string $key Identifier for the data
  150. * @param integer $offset How much to increment
  151. * @return New incremented value, false otherwise
  152. * @throws CacheException when you try to increment with compress = true
  153. */
  154. public function increment($key, $offset = 1) {
  155. if ($this->settings['compress']) {
  156. throw new CacheException(
  157. __d('cake_dev', 'Method increment() not implemented for compressed cache in %s', __CLASS__)
  158. );
  159. }
  160. return $this->_Memcache->increment($key, $offset);
  161. }
  162. /**
  163. * Decrements the value of an integer cached key
  164. *
  165. * @param string $key Identifier for the data
  166. * @param integer $offset How much to subtract
  167. * @return New decremented value, false otherwise
  168. * @throws CacheException when you try to decrement with compress = true
  169. */
  170. public function decrement($key, $offset = 1) {
  171. if ($this->settings['compress']) {
  172. throw new CacheException(
  173. __d('cake_dev', 'Method decrement() not implemented for compressed cache in %s', __CLASS__)
  174. );
  175. }
  176. return $this->_Memcache->decrement($key, $offset);
  177. }
  178. /**
  179. * Delete a key from the cache
  180. *
  181. * @param string $key Identifier for the data
  182. * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
  183. */
  184. public function delete($key) {
  185. return $this->_Memcache->delete($key);
  186. }
  187. /**
  188. * Delete all keys from the cache
  189. *
  190. * @param boolean $check
  191. * @return boolean True if the cache was successfully cleared, false otherwise
  192. */
  193. public function clear($check) {
  194. if ($check) {
  195. return true;
  196. }
  197. foreach ($this->_Memcache->getExtendedStats('slabs') as $slabs) {
  198. foreach (array_keys($slabs) as $slabId) {
  199. if (!is_numeric($slabId)) {
  200. continue;
  201. }
  202. foreach ($this->_Memcache->getExtendedStats('cachedump', $slabId) as $stats) {
  203. if (!is_array($stats)) {
  204. continue;
  205. }
  206. foreach (array_keys($stats) as $key) {
  207. if (strpos($key, $this->settings['prefix']) === 0) {
  208. $this->_Memcache->delete($key);
  209. }
  210. }
  211. }
  212. }
  213. }
  214. return true;
  215. }
  216. /**
  217. * Connects to a server in connection pool
  218. *
  219. * @param string $host host ip address or name
  220. * @param integer $port Server port
  221. * @return boolean True if memcache server was connected
  222. */
  223. public function connect($host, $port = 11211) {
  224. if ($this->_Memcache->getServerStatus($host, $port) === 0) {
  225. if ($this->_Memcache->connect($host, $port)) {
  226. return true;
  227. }
  228. return false;
  229. }
  230. return true;
  231. }
  232. /**
  233. * Returns the `group value` for each of the configured groups
  234. * If the group initial value was not found, then it initializes
  235. * the group accordingly.
  236. *
  237. * @return array
  238. */
  239. public function groups() {
  240. if (empty($this->_compiledGroupNames)) {
  241. foreach ($this->settings['groups'] as $group) {
  242. $this->_compiledGroupNames[] = $this->settings['prefix'] . $group;
  243. }
  244. }
  245. $groups = $this->_Memcache->get($this->_compiledGroupNames);
  246. if (count($groups) !== count($this->settings['groups'])) {
  247. foreach ($this->_compiledGroupNames as $group) {
  248. if (!isset($groups[$group])) {
  249. $this->_Memcache->set($group, 1, false, 0);
  250. $groups[$group] = 1;
  251. }
  252. }
  253. ksort($groups);
  254. }
  255. $result = array();
  256. $groups = array_values($groups);
  257. foreach ($this->settings['groups'] as $i => $group) {
  258. $result[] = $group . $groups[$i];
  259. }
  260. return $result;
  261. }
  262. /**
  263. * Increments the group value to simulate deletion of all keys under a group
  264. * old values will remain in storage until they expire.
  265. *
  266. * @return boolean success
  267. */
  268. public function clearGroup($group) {
  269. return (bool)$this->_Memcache->increment($this->settings['prefix'] . $group);
  270. }
  271. }