PageRenderTime 50ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/system/libraries/Zend/Cache/Backend/Memcached.php

https://bitbucket.org/micromax/vox-fw
PHP | 504 lines | 250 code | 34 blank | 220 comment | 33 complexity | 6d6fc426bcfab7e303d8d645d72d9d6f MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Cache
  17. * @subpackage Zend_Cache_Backend
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Memcached.php 23775 2011-03-01 17:25:24Z ralph $
  21. */
  22. /**
  23. * @see Zend_Cache_Backend_Interface
  24. */
  25. require_once 'Zend/Cache/Backend/ExtendedInterface.php';
  26. /**
  27. * @see Zend_Cache_Backend
  28. */
  29. require_once 'Zend/Cache/Backend.php';
  30. /**
  31. * @package Zend_Cache
  32. * @subpackage Zend_Cache_Backend
  33. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  34. * @license http://framework.zend.com/license/new-bsd New BSD License
  35. */
  36. class Zend_Cache_Backend_Memcached extends Zend_Cache_Backend implements Zend_Cache_Backend_ExtendedInterface
  37. {
  38. /**
  39. * Default Values
  40. */
  41. const DEFAULT_HOST = '127.0.0.1';
  42. const DEFAULT_PORT = 11211;
  43. const DEFAULT_PERSISTENT = true;
  44. const DEFAULT_WEIGHT = 1;
  45. const DEFAULT_TIMEOUT = 1;
  46. const DEFAULT_RETRY_INTERVAL = 15;
  47. const DEFAULT_STATUS = true;
  48. const DEFAULT_FAILURE_CALLBACK = null;
  49. /**
  50. * Log message
  51. */
  52. const TAGS_UNSUPPORTED_BY_CLEAN_OF_MEMCACHED_BACKEND = 'Zend_Cache_Backend_Memcached::clean() : tags are unsupported by the Memcached backend';
  53. const TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND = 'Zend_Cache_Backend_Memcached::save() : tags are unsupported by the Memcached backend';
  54. /**
  55. * Available options
  56. *
  57. * =====> (array) servers :
  58. * an array of memcached server ; each memcached server is described by an associative array :
  59. * 'host' => (string) : the name of the memcached server
  60. * 'port' => (int) : the port of the memcached server
  61. * 'persistent' => (bool) : use or not persistent connections to this memcached server
  62. * 'weight' => (int) : number of buckets to create for this server which in turn control its
  63. * probability of it being selected. The probability is relative to the total
  64. * weight of all servers.
  65. * 'timeout' => (int) : value in seconds which will be used for connecting to the daemon. Think twice
  66. * before changing the default value of 1 second - you can lose all the
  67. * advantages of caching if your connection is too slow.
  68. * 'retry_interval' => (int) : controls how often a failed server will be retried, the default value
  69. * is 15 seconds. Setting this parameter to -1 disables automatic retry.
  70. * 'status' => (bool) : controls if the server should be flagged as online.
  71. * 'failure_callback' => (callback) : Allows the user to specify a callback function to run upon
  72. * encountering an error. The callback is run before failover
  73. * is attempted. The function takes two parameters, the hostname
  74. * and port of the failed server.
  75. *
  76. * =====> (boolean) compression :
  77. * true if you want to use on-the-fly compression
  78. *
  79. * =====> (boolean) compatibility :
  80. * true if you use old memcache server or extension
  81. *
  82. * @var array available options
  83. */
  84. protected $_options = array(
  85. 'servers' => array(array(
  86. 'host' => self::DEFAULT_HOST,
  87. 'port' => self::DEFAULT_PORT,
  88. 'persistent' => self::DEFAULT_PERSISTENT,
  89. 'weight' => self::DEFAULT_WEIGHT,
  90. 'timeout' => self::DEFAULT_TIMEOUT,
  91. 'retry_interval' => self::DEFAULT_RETRY_INTERVAL,
  92. 'status' => self::DEFAULT_STATUS,
  93. 'failure_callback' => self::DEFAULT_FAILURE_CALLBACK
  94. )),
  95. 'compression' => false,
  96. 'compatibility' => false,
  97. );
  98. /**
  99. * Memcache object
  100. *
  101. * @var mixed memcache object
  102. */
  103. protected $_memcache = null;
  104. /**
  105. * Constructor
  106. *
  107. * @param array $options associative array of options
  108. * @throws Zend_Cache_Exception
  109. * @return void
  110. */
  111. public function __construct(array $options = array())
  112. {
  113. if (!extension_loaded('memcache')) {
  114. Zend_Cache::throwException('The memcache extension must be loaded for using this backend !');
  115. }
  116. parent::__construct($options);
  117. if (isset($this->_options['servers'])) {
  118. $value= $this->_options['servers'];
  119. if (isset($value['host'])) {
  120. // in this case, $value seems to be a simple associative array (one server only)
  121. $value = array(0 => $value); // let's transform it into a classical array of associative arrays
  122. }
  123. $this->setOption('servers', $value);
  124. }
  125. $this->_memcache = new Memcache;
  126. foreach ($this->_options['servers'] as $server) {
  127. if (!array_key_exists('port', $server)) {
  128. $server['port'] = self::DEFAULT_PORT;
  129. }
  130. if (!array_key_exists('persistent', $server)) {
  131. $server['persistent'] = self::DEFAULT_PERSISTENT;
  132. }
  133. if (!array_key_exists('weight', $server)) {
  134. $server['weight'] = self::DEFAULT_WEIGHT;
  135. }
  136. if (!array_key_exists('timeout', $server)) {
  137. $server['timeout'] = self::DEFAULT_TIMEOUT;
  138. }
  139. if (!array_key_exists('retry_interval', $server)) {
  140. $server['retry_interval'] = self::DEFAULT_RETRY_INTERVAL;
  141. }
  142. if (!array_key_exists('status', $server)) {
  143. $server['status'] = self::DEFAULT_STATUS;
  144. }
  145. if (!array_key_exists('failure_callback', $server)) {
  146. $server['failure_callback'] = self::DEFAULT_FAILURE_CALLBACK;
  147. }
  148. if ($this->_options['compatibility']) {
  149. // No status for compatibility mode (#ZF-5887)
  150. $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'],
  151. $server['weight'], $server['timeout'],
  152. $server['retry_interval']);
  153. } else {
  154. $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'],
  155. $server['weight'], $server['timeout'],
  156. $server['retry_interval'],
  157. $server['status'], $server['failure_callback']);
  158. }
  159. }
  160. }
  161. /**
  162. * Test if a cache is available for the given id and (if yes) return it (false else)
  163. *
  164. * @param string $id Cache id
  165. * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
  166. * @return string|false cached datas
  167. */
  168. public function load($id, $doNotTestCacheValidity = false)
  169. {
  170. $tmp = $this->_memcache->get($id);
  171. if (is_array($tmp) && isset($tmp[0])) {
  172. return $tmp[0];
  173. }
  174. return false;
  175. }
  176. /**
  177. * Test if a cache is available or not (for the given id)
  178. *
  179. * @param string $id Cache id
  180. * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record
  181. */
  182. public function test($id)
  183. {
  184. $tmp = $this->_memcache->get($id);
  185. if (is_array($tmp)) {
  186. return $tmp[1];
  187. }
  188. return false;
  189. }
  190. /**
  191. * Save some string datas into a cache record
  192. *
  193. * Note : $data is always "string" (serialization is done by the
  194. * core not by the backend)
  195. *
  196. * @param string $data Datas to cache
  197. * @param string $id Cache id
  198. * @param array $tags Array of strings, the cache record will be tagged by each string entry
  199. * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
  200. * @return boolean True if no problem
  201. */
  202. public function save($data, $id, $tags = array(), $specificLifetime = false)
  203. {
  204. $lifetime = $this->getLifetime($specificLifetime);
  205. if ($this->_options['compression']) {
  206. $flag = MEMCACHE_COMPRESSED;
  207. } else {
  208. $flag = 0;
  209. }
  210. // ZF-8856: using set because add needs a second request if item already exists
  211. $result = @$this->_memcache->set($id, array($data, time(), $lifetime), $flag, $lifetime);
  212. if (count($tags) > 0) {
  213. $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
  214. }
  215. return $result;
  216. }
  217. /**
  218. * Remove a cache record
  219. *
  220. * @param string $id Cache id
  221. * @return boolean True if no problem
  222. */
  223. public function remove($id)
  224. {
  225. return $this->_memcache->delete($id, 0);
  226. }
  227. /**
  228. * Clean some cache records
  229. *
  230. * Available modes are :
  231. * 'all' (default) => remove all cache entries ($tags is not used)
  232. * 'old' => unsupported
  233. * 'matchingTag' => unsupported
  234. * 'notMatchingTag' => unsupported
  235. * 'matchingAnyTag' => unsupported
  236. *
  237. * @param string $mode Clean mode
  238. * @param array $tags Array of tags
  239. * @throws Zend_Cache_Exception
  240. * @return boolean True if no problem
  241. */
  242. public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
  243. {
  244. switch ($mode) {
  245. case Zend_Cache::CLEANING_MODE_ALL:
  246. return $this->_memcache->flush();
  247. break;
  248. case Zend_Cache::CLEANING_MODE_OLD:
  249. $this->_log("Zend_Cache_Backend_Memcached::clean() : CLEANING_MODE_OLD is unsupported by the Memcached backend");
  250. break;
  251. case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
  252. case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
  253. case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
  254. $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_MEMCACHED_BACKEND);
  255. break;
  256. default:
  257. Zend_Cache::throwException('Invalid mode for clean() method');
  258. break;
  259. }
  260. }
  261. /**
  262. * Return true if the automatic cleaning is available for the backend
  263. *
  264. * @return boolean
  265. */
  266. public function isAutomaticCleaningAvailable()
  267. {
  268. return false;
  269. }
  270. /**
  271. * Set the frontend directives
  272. *
  273. * @param array $directives Assoc of directives
  274. * @throws Zend_Cache_Exception
  275. * @return void
  276. */
  277. public function setDirectives($directives)
  278. {
  279. parent::setDirectives($directives);
  280. $lifetime = $this->getLifetime(false);
  281. if ($lifetime > 2592000) {
  282. // #ZF-3490 : For the memcached backend, there is a lifetime limit of 30 days (2592000 seconds)
  283. $this->_log('memcached backend has a limit of 30 days (2592000 seconds) for the lifetime');
  284. }
  285. if ($lifetime === null) {
  286. // #ZF-4614 : we tranform null to zero to get the maximal lifetime
  287. parent::setDirectives(array('lifetime' => 0));
  288. }
  289. }
  290. /**
  291. * Return an array of stored cache ids
  292. *
  293. * @return array array of stored cache ids (string)
  294. */
  295. public function getIds()
  296. {
  297. $this->_log("Zend_Cache_Backend_Memcached::save() : getting the list of cache ids is unsupported by the Memcache backend");
  298. return array();
  299. }
  300. /**
  301. * Return an array of stored tags
  302. *
  303. * @return array array of stored tags (string)
  304. */
  305. public function getTags()
  306. {
  307. $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
  308. return array();
  309. }
  310. /**
  311. * Return an array of stored cache ids which match given tags
  312. *
  313. * In case of multiple tags, a logical AND is made between tags
  314. *
  315. * @param array $tags array of tags
  316. * @return array array of matching cache ids (string)
  317. */
  318. public function getIdsMatchingTags($tags = array())
  319. {
  320. $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
  321. return array();
  322. }
  323. /**
  324. * Return an array of stored cache ids which don't match given tags
  325. *
  326. * In case of multiple tags, a logical OR is made between tags
  327. *
  328. * @param array $tags array of tags
  329. * @return array array of not matching cache ids (string)
  330. */
  331. public function getIdsNotMatchingTags($tags = array())
  332. {
  333. $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
  334. return array();
  335. }
  336. /**
  337. * Return an array of stored cache ids which match any given tags
  338. *
  339. * In case of multiple tags, a logical AND is made between tags
  340. *
  341. * @param array $tags array of tags
  342. * @return array array of any matching cache ids (string)
  343. */
  344. public function getIdsMatchingAnyTags($tags = array())
  345. {
  346. $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
  347. return array();
  348. }
  349. /**
  350. * Return the filling percentage of the backend storage
  351. *
  352. * @throws Zend_Cache_Exception
  353. * @return int integer between 0 and 100
  354. */
  355. public function getFillingPercentage()
  356. {
  357. $mems = $this->_memcache->getExtendedStats();
  358. $memSize = null;
  359. $memUsed = null;
  360. foreach ($mems as $key => $mem) {
  361. if ($mem === false) {
  362. $this->_log('can\'t get stat from ' . $key);
  363. continue;
  364. }
  365. $eachSize = $mem['limit_maxbytes'];
  366. $eachUsed = $mem['bytes'];
  367. if ($eachUsed > $eachSize) {
  368. $eachUsed = $eachSize;
  369. }
  370. $memSize += $eachSize;
  371. $memUsed += $eachUsed;
  372. }
  373. if ($memSize === null || $memUsed === null) {
  374. Zend_Cache::throwException('Can\'t get filling percentage');
  375. }
  376. return ((int) (100. * ($memUsed / $memSize)));
  377. }
  378. /**
  379. * Return an array of metadatas for the given cache id
  380. *
  381. * The array must include these keys :
  382. * - expire : the expire timestamp
  383. * - tags : a string array of tags
  384. * - mtime : timestamp of last modification time
  385. *
  386. * @param string $id cache id
  387. * @return array array of metadatas (false if the cache id is not found)
  388. */
  389. public function getMetadatas($id)
  390. {
  391. $tmp = $this->_memcache->get($id);
  392. if (is_array($tmp)) {
  393. $data = $tmp[0];
  394. $mtime = $tmp[1];
  395. if (!isset($tmp[2])) {
  396. // because this record is only with 1.7 release
  397. // if old cache records are still there...
  398. return false;
  399. }
  400. $lifetime = $tmp[2];
  401. return array(
  402. 'expire' => $mtime + $lifetime,
  403. 'tags' => array(),
  404. 'mtime' => $mtime
  405. );
  406. }
  407. return false;
  408. }
  409. /**
  410. * Give (if possible) an extra lifetime to the given cache id
  411. *
  412. * @param string $id cache id
  413. * @param int $extraLifetime
  414. * @return boolean true if ok
  415. */
  416. public function touch($id, $extraLifetime)
  417. {
  418. if ($this->_options['compression']) {
  419. $flag = MEMCACHE_COMPRESSED;
  420. } else {
  421. $flag = 0;
  422. }
  423. $tmp = $this->_memcache->get($id);
  424. if (is_array($tmp)) {
  425. $data = $tmp[0];
  426. $mtime = $tmp[1];
  427. if (!isset($tmp[2])) {
  428. // because this record is only with 1.7 release
  429. // if old cache records are still there...
  430. return false;
  431. }
  432. $lifetime = $tmp[2];
  433. $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime;
  434. if ($newLifetime <=0) {
  435. return false;
  436. }
  437. // #ZF-5702 : we try replace() first becase set() seems to be slower
  438. if (!($result = $this->_memcache->replace($id, array($data, time(), $newLifetime), $flag, $newLifetime))) {
  439. $result = $this->_memcache->set($id, array($data, time(), $newLifetime), $flag, $newLifetime);
  440. }
  441. return $result;
  442. }
  443. return false;
  444. }
  445. /**
  446. * Return an associative array of capabilities (booleans) of the backend
  447. *
  448. * The array must include these keys :
  449. * - automatic_cleaning (is automating cleaning necessary)
  450. * - tags (are tags supported)
  451. * - expired_read (is it possible to read expired cache records
  452. * (for doNotTestCacheValidity option for example))
  453. * - priority does the backend deal with priority when saving
  454. * - infinite_lifetime (is infinite lifetime can work with this backend)
  455. * - get_list (is it possible to get the list of cache ids and the complete list of tags)
  456. *
  457. * @return array associative of with capabilities
  458. */
  459. public function getCapabilities()
  460. {
  461. return array(
  462. 'automatic_cleaning' => false,
  463. 'tags' => false,
  464. 'expired_read' => false,
  465. 'priority' => false,
  466. 'infinite_lifetime' => false,
  467. 'get_list' => false
  468. );
  469. }
  470. }