pictcode / lib / Cake / Cache / Engine / MemcachedEngine.php @ 001918d1
履歴 | 表示 | アノテート | ダウンロード (10.185 KB)
| 1 | 635eef61 | spyder1211 | <?php
 | 
      
|---|---|---|---|
| 2 | /**
 | 
      ||
| 3 |  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 | 
      ||
| 4 |  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 | 
      ||
| 5 |  *
 | 
      ||
| 6 |  * Licensed under The MIT License
 | 
      ||
| 7 |  * For full copyright and license information, please see the LICENSE.txt
 | 
      ||
| 8 |  * Redistributions of files must retain the above copyright notice.
 | 
      ||
| 9 |  *
 | 
      ||
| 10 |  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 | 
      ||
| 11 |  * @link          http://cakephp.org CakePHP(tm) Project
 | 
      ||
| 12 |  * @since         CakePHP(tm) v 2.5.0
 | 
      ||
| 13 |  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 | 
      ||
| 14 |  */
 | 
      ||
| 15 | |||
| 16 | /**
 | 
      ||
| 17 |  * Memcached storage engine for cache. Memcached has some limitations in the amount of
 | 
      ||
| 18 |  * control you have over expire times far in the future. See MemcachedEngine::write() for
 | 
      ||
| 19 |  * more information.
 | 
      ||
| 20 |  *
 | 
      ||
| 21 |  * Main advantage of this Memcached engine over the memcached engine is
 | 
      ||
| 22 |  * support of binary protocol, and igbibnary serialization
 | 
      ||
| 23 |  * (if memcached extension compiled with --enable-igbinary)
 | 
      ||
| 24 |  * Compressed keys can also be incremented/decremented
 | 
      ||
| 25 |  *
 | 
      ||
| 26 |  * @package       Cake.Cache.Engine
 | 
      ||
| 27 |  */
 | 
      ||
| 28 | class MemcachedEngine extends CacheEngine {  | 
      ||
| 29 | |||
| 30 | /**
 | 
      ||
| 31 |  * memcached wrapper.
 | 
      ||
| 32 |  *
 | 
      ||
| 33 |  * @var Memcache
 | 
      ||
| 34 |  */
 | 
      ||
| 35 | protected $_Memcached = null;  | 
      ||
| 36 | |||
| 37 | /**
 | 
      ||
| 38 |  * Settings
 | 
      ||
| 39 |  *
 | 
      ||
| 40 |  *  - servers = string or array of memcached servers, default => 127.0.0.1. If an
 | 
      ||
| 41 |  *    array MemcacheEngine will use them as a pool.
 | 
      ||
| 42 |  *  - compress = boolean, default => false
 | 
      ||
| 43 |  *  - persistent = string The name of the persistent connection. All configurations using
 | 
      ||
| 44 |  *    the same persistent value will share a single underlying connection.
 | 
      ||
| 45 |  *  - serialize = string, default => php. The serializer engine used to serialize data.
 | 
      ||
| 46 |  *    Available engines are php, igbinary and json. Beside php, the memcached extension
 | 
      ||
| 47 |  *    must be compiled with the appropriate serializer support.
 | 
      ||
| 48 |  *  - options - Additional options for the memcached client. Should be an array of option => value.
 | 
      ||
| 49 |  *    Use the Memcached::OPT_* constants as keys.
 | 
      ||
| 50 |  *
 | 
      ||
| 51 |  * @var array
 | 
      ||
| 52 |  */
 | 
      ||
| 53 | public $settings = array();  | 
      ||
| 54 | |||
| 55 | /**
 | 
      ||
| 56 |  * List of available serializer engines
 | 
      ||
| 57 |  *
 | 
      ||
| 58 |  * Memcached must be compiled with json and igbinary support to use these engines
 | 
      ||
| 59 |  *
 | 
      ||
| 60 |  * @var array
 | 
      ||
| 61 |  */
 | 
      ||
| 62 | protected $_serializers = array(  | 
      ||
| 63 | 'igbinary' => Memcached::SERIALIZER_IGBINARY,  | 
      ||
| 64 | 'json' => Memcached::SERIALIZER_JSON,  | 
      ||
| 65 | 'php' => Memcached::SERIALIZER_PHP  | 
      ||
| 66 | );  | 
      ||
| 67 | |||
| 68 | /**
 | 
      ||
| 69 |  * Initialize the Cache Engine
 | 
      ||
| 70 |  *
 | 
      ||
| 71 |  * Called automatically by the cache frontend
 | 
      ||
| 72 |  * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
 | 
      ||
| 73 |  *
 | 
      ||
| 74 |  * @param array $settings array of setting for the engine
 | 
      ||
| 75 |  * @return bool True if the engine has been successfully initialized, false if not
 | 
      ||
| 76 |  * @throws CacheException when you try use authentication without Memcached compiled with SASL support
 | 
      ||
| 77 |  */
 | 
      ||
| 78 | public function init($settings = array()) {  | 
      ||
| 79 | if (!class_exists('Memcached')) {  | 
      ||
| 80 | return false;  | 
      ||
| 81 | }  | 
      ||
| 82 | if (!isset($settings['prefix'])) {  | 
      ||
| 83 | $settings['prefix'] = Inflector::slug(APP_DIR) . '_';  | 
      ||
| 84 | }  | 
      ||
| 85 | |||
| 86 | if (defined('Memcached::HAVE_MSGPACK') && Memcached::HAVE_MSGPACK) {  | 
      ||
| 87 | $this->_serializers['msgpack'] = Memcached::SERIALIZER_MSGPACK;  | 
      ||
| 88 | }  | 
      ||
| 89 | |||
| 90 | $settings += array(  | 
      ||
| 91 | 'engine' => 'Memcached',  | 
      ||
| 92 | 'servers' => array('127.0.0.1'),  | 
      ||
| 93 | 'compress' => false,  | 
      ||
| 94 | 'persistent' => false,  | 
      ||
| 95 | 'login' => null,  | 
      ||
| 96 | 'password' => null,  | 
      ||
| 97 | 'serialize' => 'php',  | 
      ||
| 98 | 'options' => array()  | 
      ||
| 99 | );  | 
      ||
| 100 | parent::init($settings);  | 
      ||
| 101 | |||
| 102 | if (!is_array($this->settings['servers'])) {  | 
      ||
| 103 | $this->settings['servers'] = array($this->settings['servers']);  | 
      ||
| 104 | }  | 
      ||
| 105 | |||
| 106 | if (isset($this->_Memcached)) {  | 
      ||
| 107 | return true;  | 
      ||
| 108 | }  | 
      ||
| 109 | |||
| 110 | if (!$this->settings['persistent']) {  | 
      ||
| 111 | $this->_Memcached = new Memcached();  | 
      ||
| 112 |                 } else {
 | 
      ||
| 113 | $this->_Memcached = new Memcached((string)$this->settings['persistent']);  | 
      ||
| 114 | }  | 
      ||
| 115 |                 $this->_setOptions();
 | 
      ||
| 116 | |||
| 117 | if (count($this->_Memcached->getServerList())) {  | 
      ||
| 118 | return true;  | 
      ||
| 119 | }  | 
      ||
| 120 | |||
| 121 | $servers = array();  | 
      ||
| 122 | foreach ($this->settings['servers'] as $server) {  | 
      ||
| 123 | $servers[] = $this->_parseServerString($server);  | 
      ||
| 124 | }  | 
      ||
| 125 | |||
| 126 | if (!$this->_Memcached->addServers($servers)) {  | 
      ||
| 127 | return false;  | 
      ||
| 128 | }  | 
      ||
| 129 | |||
| 130 | if ($this->settings['login'] !== null && $this->settings['password'] !== null) {  | 
      ||
| 131 | if (!method_exists($this->_Memcached, 'setSaslAuthData')) {  | 
      ||
| 132 | throw new CacheException(  | 
      ||
| 133 | __d('cake_dev', 'Memcached extension is not build with SASL support')  | 
      ||
| 134 | );  | 
      ||
| 135 | }  | 
      ||
| 136 | $this->_Memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);  | 
      ||
| 137 | $this->_Memcached->setSaslAuthData($this->settings['login'], $this->settings['password']);  | 
      ||
| 138 | }  | 
      ||
| 139 | if (is_array($this->settings['options'])) {  | 
      ||
| 140 | foreach ($this->settings['options'] as $opt => $value) {  | 
      ||
| 141 | $this->_Memcached->setOption($opt, $value);  | 
      ||
| 142 | }  | 
      ||
| 143 | }  | 
      ||
| 144 | |||
| 145 | return true;  | 
      ||
| 146 | }  | 
      ||
| 147 | |||
| 148 | /**
 | 
      ||
| 149 |  * Settings the memcached instance
 | 
      ||
| 150 |  *
 | 
      ||
| 151 |  * @throws CacheException when the Memcached extension is not built with the desired serializer engine
 | 
      ||
| 152 |  * @return void
 | 
      ||
| 153 |  */
 | 
      ||
| 154 | protected function _setOptions() {  | 
      ||
| 155 | $this->_Memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);  | 
      ||
| 156 | |||
| 157 | $serializer = strtolower($this->settings['serialize']);  | 
      ||
| 158 | if (!isset($this->_serializers[$serializer])) {  | 
      ||
| 159 | throw new CacheException(  | 
      ||
| 160 | __d('cake_dev', '%s is not a valid serializer engine for Memcached', $serializer)  | 
      ||
| 161 | );  | 
      ||
| 162 | }  | 
      ||
| 163 | |||
| 164 | if ($serializer !== 'php' && !constant('Memcached::HAVE_' . strtoupper($serializer))) {  | 
      ||
| 165 | throw new CacheException(  | 
      ||
| 166 | __d('cake_dev', 'Memcached extension is not compiled with %s support', $serializer)  | 
      ||
| 167 | );  | 
      ||
| 168 | }  | 
      ||
| 169 | |||
| 170 | $this->_Memcached->setOption(Memcached::OPT_SERIALIZER, $this->_serializers[$serializer]);  | 
      ||
| 171 | |||
| 172 |                 // Check for Amazon ElastiCache instance
 | 
      ||
| 173 | if (defined('Memcached::OPT_CLIENT_MODE') && defined('Memcached::DYNAMIC_CLIENT_MODE')) {  | 
      ||
| 174 | $this->_Memcached->setOption(Memcached::OPT_CLIENT_MODE, Memcached::DYNAMIC_CLIENT_MODE);  | 
      ||
| 175 | }  | 
      ||
| 176 | |||
| 177 | $this->_Memcached->setOption(Memcached::OPT_COMPRESSION, (bool)$this->settings['compress']);  | 
      ||
| 178 | }  | 
      ||
| 179 | |||
| 180 | /**
 | 
      ||
| 181 |  * Parses the server address into the host/port. Handles both IPv6 and IPv4
 | 
      ||
| 182 |  * addresses and Unix sockets
 | 
      ||
| 183 |  *
 | 
      ||
| 184 |  * @param string $server The server address string.
 | 
      ||
| 185 |  * @return array Array containing host, port
 | 
      ||
| 186 |  */
 | 
      ||
| 187 | protected function _parseServerString($server) {  | 
      ||
| 188 | $socketTransport = 'unix://';  | 
      ||
| 189 | if (strpos($server, $socketTransport) === 0) {  | 
      ||
| 190 | return array(substr($server, strlen($socketTransport)), 0);  | 
      ||
| 191 | }  | 
      ||
| 192 | if (substr($server, 0, 1) === '[') {  | 
      ||
| 193 | $position = strpos($server, ']:');  | 
      ||
| 194 | if ($position !== false) {  | 
      ||
| 195 |                                 $position++;
 | 
      ||
| 196 | }  | 
      ||
| 197 |                 } else {
 | 
      ||
| 198 | $position = strpos($server, ':');  | 
      ||
| 199 | }  | 
      ||
| 200 | $port = 11211;  | 
      ||
| 201 | $host = $server;  | 
      ||
| 202 | if ($position !== false) {  | 
      ||
| 203 | $host = substr($server, 0, $position);  | 
      ||
| 204 | $port = substr($server, $position + 1);  | 
      ||
| 205 | }  | 
      ||
| 206 | return array($host, (int)$port);  | 
      ||
| 207 | }  | 
      ||
| 208 | |||
| 209 | /**
 | 
      ||
| 210 |  * Write data for key into cache. When using memcached as your cache engine
 | 
      ||
| 211 |  * remember that the Memcached pecl extension does not support cache expiry times greater
 | 
      ||
| 212 |  * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
 | 
      ||
| 213 |  *
 | 
      ||
| 214 |  * @param string $key Identifier for the data
 | 
      ||
| 215 |  * @param mixed $value Data to be cached
 | 
      ||
| 216 |  * @param int $duration How long to cache the data, in seconds
 | 
      ||
| 217 |  * @return bool True if the data was successfully cached, false on failure
 | 
      ||
| 218 |  * @see http://php.net/manual/en/memcache.set.php
 | 
      ||
| 219 |  */
 | 
      ||
| 220 | public function write($key, $value, $duration) {  | 
      ||
| 221 | if ($duration > 30 * DAY) {  | 
      ||
| 222 | $duration = 0;  | 
      ||
| 223 | }  | 
      ||
| 224 | |||
| 225 | return $this->_Memcached->set($key, $value, $duration);  | 
      ||
| 226 | }  | 
      ||
| 227 | |||
| 228 | /**
 | 
      ||
| 229 |  * Read a key from the cache
 | 
      ||
| 230 |  *
 | 
      ||
| 231 |  * @param string $key Identifier for the data
 | 
      ||
| 232 |  * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
 | 
      ||
| 233 |  */
 | 
      ||
| 234 | public function read($key) {  | 
      ||
| 235 | return $this->_Memcached->get($key);  | 
      ||
| 236 | }  | 
      ||
| 237 | |||
| 238 | /**
 | 
      ||
| 239 |  * Increments the value of an integer cached key
 | 
      ||
| 240 |  *
 | 
      ||
| 241 |  * @param string $key Identifier for the data
 | 
      ||
| 242 |  * @param int $offset How much to increment
 | 
      ||
| 243 |  * @return New incremented value, false otherwise
 | 
      ||
| 244 |  * @throws CacheException when you try to increment with compress = true
 | 
      ||
| 245 |  */
 | 
      ||
| 246 | public function increment($key, $offset = 1) {  | 
      ||
| 247 | return $this->_Memcached->increment($key, $offset);  | 
      ||
| 248 | }  | 
      ||
| 249 | |||
| 250 | /**
 | 
      ||
| 251 |  * Decrements the value of an integer cached key
 | 
      ||
| 252 |  *
 | 
      ||
| 253 |  * @param string $key Identifier for the data
 | 
      ||
| 254 |  * @param int $offset How much to subtract
 | 
      ||
| 255 |  * @return New decremented value, false otherwise
 | 
      ||
| 256 |  * @throws CacheException when you try to decrement with compress = true
 | 
      ||
| 257 |  */
 | 
      ||
| 258 | public function decrement($key, $offset = 1) {  | 
      ||
| 259 | return $this->_Memcached->decrement($key, $offset);  | 
      ||
| 260 | }  | 
      ||
| 261 | |||
| 262 | /**
 | 
      ||
| 263 |  * Delete a key from the cache
 | 
      ||
| 264 |  *
 | 
      ||
| 265 |  * @param string $key Identifier for the data
 | 
      ||
| 266 |  * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
 | 
      ||
| 267 |  */
 | 
      ||
| 268 | public function delete($key) {  | 
      ||
| 269 | return $this->_Memcached->delete($key);  | 
      ||
| 270 | }  | 
      ||
| 271 | |||
| 272 | /**
 | 
      ||
| 273 |  * Delete all keys from the cache
 | 
      ||
| 274 |  *
 | 
      ||
| 275 |  * @param bool $check If true no deletes will occur and instead CakePHP will rely
 | 
      ||
| 276 |  *   on key TTL values.
 | 
      ||
| 277 |  * @return bool True if the cache was successfully cleared, false otherwise. Will
 | 
      ||
| 278 |  *   also return false if you are using a binary protocol.
 | 
      ||
| 279 |  */
 | 
      ||
| 280 | public function clear($check) {  | 
      ||
| 281 | if ($check) {  | 
      ||
| 282 | return true;  | 
      ||
| 283 | }  | 
      ||
| 284 | |||
| 285 | $keys = $this->_Memcached->getAllKeys();  | 
      ||
| 286 | if ($keys === false) {  | 
      ||
| 287 | return false;  | 
      ||
| 288 | }  | 
      ||
| 289 | |||
| 290 | foreach ($keys as $key) {  | 
      ||
| 291 | if (strpos($key, $this->settings['prefix']) === 0) {  | 
      ||
| 292 | $this->_Memcached->delete($key);  | 
      ||
| 293 | }  | 
      ||
| 294 | }  | 
      ||
| 295 | |||
| 296 | return true;  | 
      ||
| 297 | }  | 
      ||
| 298 | |||
| 299 | /**
 | 
      ||
| 300 |  * Returns the `group value` for each of the configured groups
 | 
      ||
| 301 |  * If the group initial value was not found, then it initializes
 | 
      ||
| 302 |  * the group accordingly.
 | 
      ||
| 303 |  *
 | 
      ||
| 304 |  * @return array
 | 
      ||
| 305 |  */
 | 
      ||
| 306 | public function groups() {  | 
      ||
| 307 | if (empty($this->_compiledGroupNames)) {  | 
      ||
| 308 | foreach ($this->settings['groups'] as $group) {  | 
      ||
| 309 | $this->_compiledGroupNames[] = $this->settings['prefix'] . $group;  | 
      ||
| 310 | }  | 
      ||
| 311 | }  | 
      ||
| 312 | |||
| 313 | $groups = $this->_Memcached->getMulti($this->_compiledGroupNames);  | 
      ||
| 314 | if (count($groups) !== count($this->settings['groups'])) {  | 
      ||
| 315 | foreach ($this->_compiledGroupNames as $group) {  | 
      ||
| 316 | if (!isset($groups[$group])) {  | 
      ||
| 317 | $this->_Memcached->set($group, 1, 0);  | 
      ||
| 318 | $groups[$group] = 1;  | 
      ||
| 319 | }  | 
      ||
| 320 | }  | 
      ||
| 321 | ksort($groups);  | 
      ||
| 322 | }  | 
      ||
| 323 | |||
| 324 | $result = array();  | 
      ||
| 325 | $groups = array_values($groups);  | 
      ||
| 326 | foreach ($this->settings['groups'] as $i => $group) {  | 
      ||
| 327 | $result[] = $group . $groups[$i];  | 
      ||
| 328 | }  | 
      ||
| 329 | |||
| 330 | return $result;  | 
      ||
| 331 | }  | 
      ||
| 332 | |||
| 333 | /**
 | 
      ||
| 334 |  * Increments the group value to simulate deletion of all keys under a group
 | 
      ||
| 335 |  * old values will remain in storage until they expire.
 | 
      ||
| 336 |  *
 | 
      ||
| 337 |  * @param string $group The group to clear.
 | 
      ||
| 338 |  * @return bool success
 | 
      ||
| 339 |  */
 | 
      ||
| 340 | public function clearGroup($group) {  | 
      ||
| 341 | return (bool)$this->_Memcached->increment($this->settings['prefix'] . $group);  | 
      ||
| 342 | }  | 
      ||
| 343 | }  |