ElggMemcache.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <?php
  2. /**
  3. * Memcache wrapper class.
  4. *
  5. * @package Elgg.Core
  6. * @subpackage Memcache
  7. */
  8. class ElggMemcache extends \ElggSharedMemoryCache {
  9. /**
  10. * Global Elgg configuration
  11. *
  12. * @var \stdClass
  13. */
  14. private $CONFIG;
  15. /**
  16. * Minimum version of memcached needed to run
  17. *
  18. */
  19. private static $MINSERVERVERSION = '1.1.12';
  20. /**
  21. * Memcache object
  22. */
  23. private $memcache;
  24. /**
  25. * Expiry of saved items (default timeout after a day to prevent anything getting too stale)
  26. */
  27. private $expires = 86400;
  28. /**
  29. * The version of memcache running
  30. */
  31. private $version = 0;
  32. /**
  33. * Connect to memcache.
  34. *
  35. * @param string $namespace The namespace for this cache to write to -
  36. * note, namespaces of the same name are shared!
  37. *
  38. * @throws ConfigurationException
  39. */
  40. public function __construct($namespace = 'default') {
  41. global $CONFIG;
  42. $this->CONFIG = $CONFIG;
  43. $this->setNamespace($namespace);
  44. // Do we have memcache?
  45. if (!class_exists('Memcache')) {
  46. throw new \ConfigurationException('PHP memcache module not installed, you must install php5-memcache');
  47. }
  48. // Create memcache object
  49. $this->memcache = new Memcache;
  50. // Now add servers
  51. if (!$this->CONFIG->memcache_servers) {
  52. throw new \ConfigurationException('No memcache servers defined, please populate the $this->CONFIG->memcache_servers variable');
  53. }
  54. if (is_callable(array($this->memcache, 'addServer'))) {
  55. foreach ($this->CONFIG->memcache_servers as $server) {
  56. if (is_array($server)) {
  57. $this->memcache->addServer(
  58. $server[0],
  59. isset($server[1]) ? $server[1] : 11211,
  60. isset($server[2]) ? $server[2] : false,
  61. isset($server[3]) ? $server[3] : 1,
  62. isset($server[4]) ? $server[4] : 1,
  63. isset($server[5]) ? $server[5] : 15,
  64. isset($server[6]) ? $server[6] : true
  65. );
  66. } else {
  67. $this->memcache->addServer($server, 11211);
  68. }
  69. }
  70. } else {
  71. // don't use _elgg_services()->translator->translate() here because most of the config hasn't been loaded yet
  72. // and it caches the language, which is hard coded in $this->CONFIG->language as en.
  73. // overriding it with real values later has no effect because it's already cached.
  74. _elgg_services()->logger->error("This version of the PHP memcache API doesn't support multiple servers.");
  75. $server = $this->CONFIG->memcache_servers[0];
  76. if (is_array($server)) {
  77. $this->memcache->connect($server[0], $server[1]);
  78. } else {
  79. $this->memcache->addServer($server, 11211);
  80. }
  81. }
  82. // Get version
  83. $this->version = $this->memcache->getVersion();
  84. if (version_compare($this->version, \ElggMemcache::$MINSERVERVERSION, '<')) {
  85. $msg = vsprintf('Memcache needs at least version %s to run, you are running %s',
  86. array(\ElggMemcache::$MINSERVERVERSION,
  87. $this->version
  88. ));
  89. throw new \ConfigurationException($msg);
  90. }
  91. // Set some defaults
  92. if (isset($this->CONFIG->memcache_expires)) {
  93. $this->expires = $this->CONFIG->memcache_expires;
  94. }
  95. // make sure memcache is reset
  96. _elgg_services()->events->registerHandler('cache:flush', 'system', array($this, 'clear'));
  97. }
  98. /**
  99. * Set the default expiry.
  100. *
  101. * @param int $expires The lifetime as a unix timestamp or time from now. Defaults forever.
  102. *
  103. * @return void
  104. */
  105. public function setDefaultExpiry($expires = 0) {
  106. $this->expires = $expires;
  107. }
  108. /**
  109. * Combine a key with the namespace.
  110. * Memcache can only accept <250 char key. If the given key is too long it is shortened.
  111. *
  112. * @param string $key The key
  113. *
  114. * @return string The new key.
  115. */
  116. private function makeMemcacheKey($key) {
  117. $prefix = $this->getNamespace() . ":";
  118. if (strlen($prefix . $key) > 250) {
  119. $key = md5($key);
  120. }
  121. return $prefix . $key;
  122. }
  123. /**
  124. * Saves a name and value to the cache
  125. *
  126. * @param string $key Name
  127. * @param string $data Value
  128. * @param integer $expires Expires (in seconds)
  129. *
  130. * @return bool
  131. */
  132. public function save($key, $data, $expires = null) {
  133. $key = $this->makeMemcacheKey($key);
  134. if ($expires === null) {
  135. $expires = $this->expires;
  136. }
  137. $result = $this->memcache->set($key, $data, null, $expires);
  138. if ($result === false) {
  139. _elgg_services()->logger->error("MEMCACHE: SAVE FAIL $key");
  140. } else {
  141. _elgg_services()->logger->info("MEMCACHE: SAVE SUCCESS $key");
  142. }
  143. return $result;
  144. }
  145. /**
  146. * Retrieves data.
  147. *
  148. * @param string $key Name of data to retrieve
  149. * @param int $offset Offset
  150. * @param int $limit Limit
  151. *
  152. * @return mixed
  153. */
  154. public function load($key, $offset = 0, $limit = null) {
  155. $key = $this->makeMemcacheKey($key);
  156. $result = $this->memcache->get($key);
  157. if ($result === false) {
  158. _elgg_services()->logger->info("MEMCACHE: LOAD MISS $key");
  159. } else {
  160. _elgg_services()->logger->info("MEMCACHE: LOAD HIT $key");
  161. }
  162. return $result;
  163. }
  164. /**
  165. * Delete data
  166. *
  167. * @param string $key Name of data
  168. *
  169. * @return bool
  170. */
  171. public function delete($key) {
  172. $key = $this->makeMemcacheKey($key);
  173. return $this->memcache->delete($key, 0);
  174. }
  175. /**
  176. * Clears the entire cache
  177. *
  178. * @return true
  179. */
  180. public function clear() {
  181. $result = $this->memcache->flush();
  182. if ($result === false) {
  183. _elgg_services()->logger->info("MEMCACHE: failed to flush {$this->getNamespace()}");
  184. } else {
  185. sleep(1); // needed because http://php.net/manual/en/memcache.flush.php#81420
  186. _elgg_services()->logger->info("MEMCACHE: flushed {$this->getNamespace()}");
  187. }
  188. return $result;
  189. // @todo Namespaces as in #532
  190. }
  191. }