MemcacheEngine.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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. parent::init(array_merge(array(
  65. 'engine' => 'Memcache',
  66. 'prefix' => Inflector::slug(APP_DIR) . '_',
  67. 'servers' => array('127.0.0.1'),
  68. 'compress' => false,
  69. 'persistent' => true
  70. ), $settings)
  71. );
  72. if ($this->settings['compress']) {
  73. $this->settings['compress'] = MEMCACHE_COMPRESSED;
  74. }
  75. if (!is_array($this->settings['servers'])) {
  76. $this->settings['servers'] = array($this->settings['servers']);
  77. }
  78. if (!isset($this->_Memcache)) {
  79. $return = false;
  80. $this->_Memcache = new Memcache();
  81. foreach ($this->settings['servers'] as $server) {
  82. list($host, $port) = $this->_parseServerString($server);
  83. if ($this->_Memcache->addServer($host, $port, $this->settings['persistent'])) {
  84. $return = true;
  85. }
  86. }
  87. return $return;
  88. }
  89. return true;
  90. }
  91. /**
  92. * Parses the server address into the host/port. Handles both IPv6 and IPv4
  93. * addresses and Unix sockets
  94. *
  95. * @param string $server The server address string.
  96. * @return array Array containing host, port
  97. */
  98. protected function _parseServerString($server) {
  99. if ($server[0] == 'u') {
  100. return array($server, 0);
  101. }
  102. if (substr($server, 0, 1) == '[') {
  103. $position = strpos($server, ']:');
  104. if ($position !== false) {
  105. $position++;
  106. }
  107. } else {
  108. $position = strpos($server, ':');
  109. }
  110. $port = 11211;
  111. $host = $server;
  112. if ($position !== false) {
  113. $host = substr($server, 0, $position);
  114. $port = substr($server, $position + 1);
  115. }
  116. return array($host, $port);
  117. }
  118. /**
  119. * Write data for key into cache. When using memcache as your cache engine
  120. * remember that the Memcache pecl extension does not support cache expiry times greater
  121. * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
  122. *
  123. * @param string $key Identifier for the data
  124. * @param mixed $value Data to be cached
  125. * @param integer $duration How long to cache the data, in seconds
  126. * @return boolean True if the data was successfully cached, false on failure
  127. * @see http://php.net/manual/en/memcache.set.php
  128. */
  129. public function write($key, $value, $duration) {
  130. if ($duration > 30 * DAY) {
  131. $duration = 0;
  132. }
  133. return $this->_Memcache->set($key, $value, $this->settings['compress'], $duration);
  134. }
  135. /**
  136. * Read a key from the cache
  137. *
  138. * @param string $key Identifier for the data
  139. * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
  140. */
  141. public function read($key) {
  142. return $this->_Memcache->get($key);
  143. }
  144. /**
  145. * Increments the value of an integer cached key
  146. *
  147. * @param string $key Identifier for the data
  148. * @param integer $offset How much to increment
  149. * @return New incremented value, false otherwise
  150. * @throws CacheException when you try to increment with compress = true
  151. */
  152. public function increment($key, $offset = 1) {
  153. if ($this->settings['compress']) {
  154. throw new CacheException(
  155. __d('cake_dev', 'Method increment() not implemented for compressed cache in %s', __CLASS__)
  156. );
  157. }
  158. return $this->_Memcache->increment($key, $offset);
  159. }
  160. /**
  161. * Decrements the value of an integer cached key
  162. *
  163. * @param string $key Identifier for the data
  164. * @param integer $offset How much to subtract
  165. * @return New decremented value, false otherwise
  166. * @throws CacheException when you try to decrement with compress = true
  167. */
  168. public function decrement($key, $offset = 1) {
  169. if ($this->settings['compress']) {
  170. throw new CacheException(
  171. __d('cake_dev', 'Method decrement() not implemented for compressed cache in %s', __CLASS__)
  172. );
  173. }
  174. return $this->_Memcache->decrement($key, $offset);
  175. }
  176. /**
  177. * Delete a key from the cache
  178. *
  179. * @param string $key Identifier for the data
  180. * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
  181. */
  182. public function delete($key) {
  183. return $this->_Memcache->delete($key);
  184. }
  185. /**
  186. * Delete all keys from the cache
  187. *
  188. * @param boolean $check
  189. * @return boolean True if the cache was successfully cleared, false otherwise
  190. */
  191. public function clear($check) {
  192. if ($check) {
  193. return true;
  194. }
  195. foreach ($this->_Memcache->getExtendedStats('slabs') as $slabs) {
  196. foreach (array_keys($slabs) as $slabId) {
  197. if (!is_numeric($slabId)) {
  198. continue;
  199. }
  200. foreach ($this->_Memcache->getExtendedStats('cachedump', $slabId) as $stats) {
  201. if (!is_array($stats)) {
  202. continue;
  203. }
  204. foreach (array_keys($stats) as $key) {
  205. if (strpos($key, $this->settings['prefix']) === 0) {
  206. $this->_Memcache->delete($key);
  207. }
  208. }
  209. }
  210. }
  211. }
  212. return true;
  213. }
  214. /**
  215. * Connects to a server in connection pool
  216. *
  217. * @param string $host host ip address or name
  218. * @param integer $port Server port
  219. * @return boolean True if memcache server was connected
  220. */
  221. public function connect($host, $port = 11211) {
  222. if ($this->_Memcache->getServerStatus($host, $port) === 0) {
  223. if ($this->_Memcache->connect($host, $port)) {
  224. return true;
  225. }
  226. return false;
  227. }
  228. return true;
  229. }
  230. /**
  231. * Returns the `group value` for each of the configured groups
  232. * If the group initial value was not found, then it initializes
  233. * the group accordingly.
  234. *
  235. * @return array
  236. **/
  237. public function groups() {
  238. $groups = $this->_compiledGroupNames;
  239. if (empty($groups)) {
  240. foreach ($this->settings['groups'] as $group) {
  241. $groups[] = $this->settings['prefix'] . $group;
  242. }
  243. $this->_compiledGroupNames = $groups;
  244. }
  245. $groups = $this->_Memcache->get($groups);
  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. foreach ($groups as $group => $value) {
  257. $result[] = $group . $value;
  258. }
  259. return $result;
  260. }
  261. /**
  262. * Increments the group value to simulate deletion of all keys under a group
  263. * old values will remain in storage until they expire.
  264. *
  265. * @return boolean success
  266. **/
  267. public function clearGroup($group) {
  268. return (bool) $this->_Memcache->increment($this->settings['prefix'] . $group);
  269. }
  270. }