MemcachedPlugin.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. /**
  3. * StatusNet - the distributed open-source microblogging tool
  4. * Copyright (C) 2009, StatusNet, Inc.
  5. *
  6. * Plugin to implement cache interface for memcached
  7. *
  8. * PHP version 5
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. *
  23. * @category Cache
  24. * @package StatusNet
  25. * @author Evan Prodromou <evan@status.net>
  26. * @author Craig Andrews <candrews@integralblue.com>
  27. * @copyright 2009 StatusNet, Inc.
  28. * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
  29. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  30. * @link http://status.net/
  31. */
  32. if (!defined('STATUSNET')) {
  33. // This check helps protect against security problems;
  34. // your code file can't be executed directly from the web.
  35. exit(1);
  36. }
  37. /**
  38. * A plugin to use memcached for the cache interface
  39. *
  40. * This used to be encoded as config-variable options in the core code;
  41. * it's now broken out to a separate plugin. The same interface can be
  42. * implemented by other plugins.
  43. *
  44. * @category Cache
  45. * @package StatusNet
  46. * @author Evan Prodromou <evan@status.net>
  47. * @author Craig Andrews <candrews@integralblue.com>
  48. * @copyright 2009 StatusNet, Inc.
  49. * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
  50. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  51. * @link http://status.net/
  52. */
  53. class MemcachedPlugin extends Plugin
  54. {
  55. const PLUGIN_VERSION = '2.0.0';
  56. static $cacheInitialized = false;
  57. private $_conn = null;
  58. public $servers = array('127.0.0.1;11211');
  59. public $defaultExpiry = 86400; // 24h
  60. /**
  61. * Initialize the plugin
  62. *
  63. * Note that onStartCacheGet() may have been called before this!
  64. *
  65. * @return boolean flag value
  66. */
  67. function onInitializePlugin()
  68. {
  69. $this->_ensureConn();
  70. self::$cacheInitialized = true;
  71. return true;
  72. }
  73. /**
  74. * Get a value associated with a key
  75. *
  76. * The value should have been set previously.
  77. *
  78. * @param string &$key in; Lookup key
  79. * @param mixed &$value out; value associated with key
  80. *
  81. * @return boolean hook success
  82. */
  83. function onStartCacheGet(&$key, &$value)
  84. {
  85. $this->_ensureConn();
  86. $value = $this->_conn->get($key);
  87. Event::handle('EndCacheGet', array($key, &$value));
  88. return false;
  89. }
  90. /**
  91. * Associate a value with a key
  92. *
  93. * @param string &$key in; Key to use for lookups
  94. * @param mixed &$value in; Value to associate
  95. * @param integer &$flag in; Flag empty or Cache::COMPRESSED
  96. * @param integer &$expiry in; Expiry (passed through to Memcache)
  97. * @param boolean &$success out; Whether the set was successful
  98. *
  99. * @return boolean hook success
  100. */
  101. function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success)
  102. {
  103. $this->_ensureConn();
  104. if ($expiry === null) {
  105. $expiry = $this->defaultExpiry;
  106. }
  107. $success = $this->_conn->set($key, $value, $expiry);
  108. Event::handle('EndCacheSet', array($key, $value, $flag,
  109. $expiry));
  110. return false;
  111. }
  112. /**
  113. * Atomically increment an existing numeric key value.
  114. * Existing expiration time will not be changed.
  115. *
  116. * @param string &$key in; Key to use for lookups
  117. * @param int &$step in; Amount to increment (default 1)
  118. * @param mixed &$value out; Incremented value, or false if key not set.
  119. *
  120. * @return boolean hook success
  121. */
  122. function onStartCacheIncrement(&$key, &$step, &$value)
  123. {
  124. $this->_ensureConn();
  125. $value = $this->_conn->increment($key, $step);
  126. Event::handle('EndCacheIncrement', array($key, $step, $value));
  127. return false;
  128. }
  129. /**
  130. * Delete a value associated with a key
  131. *
  132. * @param string &$key in; Key to lookup
  133. * @param boolean &$success out; whether it worked
  134. *
  135. * @return boolean hook success
  136. */
  137. function onStartCacheDelete(&$key, &$success)
  138. {
  139. $this->_ensureConn();
  140. $success = $this->_conn->delete($key);
  141. Event::handle('EndCacheDelete', array($key));
  142. return false;
  143. }
  144. function onStartCacheReconnect(&$success)
  145. {
  146. // nothing to do
  147. return true;
  148. }
  149. /**
  150. * Ensure that a connection exists
  151. *
  152. * Checks the instance $_conn variable and connects
  153. * if it is empty.
  154. *
  155. * @return void
  156. */
  157. private function _ensureConn()
  158. {
  159. if (empty($this->_conn)) {
  160. $this->_conn = new Memcached(common_config('site', 'nickname'));
  161. if (!count($this->_conn->getServerList())) {
  162. if (is_array($this->servers)) {
  163. $servers = $this->servers;
  164. } else {
  165. $servers = array($this->servers);
  166. }
  167. foreach ($servers as $server) {
  168. if (strpos($server, ';') !== false) {
  169. list($host, $port) = explode(';', $server);
  170. } else {
  171. $host = $server;
  172. $port = 11211;
  173. }
  174. $this->_conn->addServer($host, $port);
  175. }
  176. // Compress items stored in the cache.
  177. // Allows the cache to store objects larger than 1MB (if they
  178. // compress to less than 1MB), and improves cache memory efficiency.
  179. $this->_conn->setOption(Memcached::OPT_COMPRESSION, true);
  180. }
  181. }
  182. }
  183. /**
  184. * Translate general flags to Memcached-specific flags
  185. * @param int $flag
  186. * @return int
  187. */
  188. protected function flag($flag)
  189. {
  190. //no flags are presently supported
  191. return $flag;
  192. }
  193. function onPluginVersion(array &$versions)
  194. {
  195. $versions[] = array('name' => 'Memcached',
  196. 'version' => self::PLUGIN_VERSION,
  197. 'author' => 'Evan Prodromou, Craig Andrews',
  198. 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Memcached',
  199. 'rawdescription' =>
  200. // TRANS: Plugin description.
  201. _m('Use <a href="http://memcached.org/">Memcached</a> to cache query results.'));
  202. return true;
  203. }
  204. }