Last active
January 7, 2017 04:52
-
-
Save nickelfault/12200664d2aa56315dddc27eeb90c9bd to your computer and use it in GitHub Desktop.
Memcache Engine for CakePHP 3.x (src/Cache/Engine/MemcacheEngine.php)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Memcache storage engine for cache | |
* | |
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | |
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | |
* | |
* Licensed under The MIT License | |
* For full copyright and license information, please see the LICENSE.txt | |
* Redistributions of files must retain the above copyright notice. | |
* | |
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | |
* @link http://cakephp.org CakePHP(tm) Project | |
* @package Cake.Cache.Engine | |
* @since CakePHP(tm) v 1.2.0.4933 | |
* @license http://www.opensource.org/licenses/mit-license.php MIT License | |
*/ | |
namespace App\Cache\Engine; | |
use Cake\Cache\CacheEngine; | |
use Cake\Utility\Inflector; | |
use InvalidArgumentException; | |
/** | |
* Memcache storage engine for cache. Memcache has some limitations in the amount of | |
* control you have over expire times far in the future. See MemcacheEngine::write() for | |
* more information. | |
* | |
* @package Cake.Cache.Engine | |
* @deprecated 3.0.0 You should use the Memcached adapter instead. | |
*/ | |
class MemcacheEngine extends CacheEngine { | |
/** | |
* Contains the compiled group names | |
* (prefixed with the global configuration prefix) | |
* | |
* @var array | |
*/ | |
protected $_compiledGroupNames = []; | |
/** | |
* Memcache wrapper. | |
* | |
* @var Memcache | |
*/ | |
protected $_Memcache = null; | |
/** | |
* The default config used unless overridden by runtime configuration | |
* | |
* - `compress` Whether to compress data | |
* - `duration` Specify how long items in this cache configuration last. | |
* - `groups` List of groups or 'tags' associated to every key stored in this config. | |
* handy for deleting a complete group from cache. | |
* - `host` Hostname or IP address of memcached server | |
* - `persistent` Whether or not to use a persistent connection | |
* - `port` Port of the memcached server(s) | |
* - `prefix` Prepended to all entries. Good for when you need to share a keyspace | |
* with either another cache config or another application. | |
* - `probability` Probability of hitting a cache gc cleanup. Setting to 0 will disable | |
* cache::gc from ever being called automatically. | |
* - `servers` String or array of memcached servers. If an array MemcacheEngine will use | |
* them as a pool. | |
* | |
* @var array | |
*/ | |
protected $_defaultConfig = [ | |
'compress' => false, | |
'duration' => 3600, | |
'groups' => [], | |
'host' => null, | |
'persistent' => true, | |
'port' => 11211, | |
'prefix' => 'cake_', | |
'probability' => 100, | |
'servers' => ['127.0.0.1'], | |
]; | |
/** | |
* Initialize the Cache Engine | |
* | |
* Called automatically by the cache frontend | |
* To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); | |
* | |
* @param array $config array of setting for the engine | |
* @return bool True if the engine has been successfully initialized, false if not | |
*/ | |
public function init(array $config = []) { | |
if (!class_exists('Memcache')) { | |
return false; | |
} | |
if (!isset($config['prefix'])) { | |
$config['prefix'] = Inflector::slug(APP_DIR) . '_'; | |
} | |
parent::init($config); | |
if ($this->_config['compress']) { | |
$this->_config['compress'] = MEMCACHE_COMPRESSED; | |
} | |
if (!empty($config['host'])) { | |
if (empty($config['port'])) { | |
$config['servers'] = [$config['host']]; | |
} else { | |
$config['servers'] = [sprintf('%s:%d', $config['host'], $config['port'])]; | |
} | |
} | |
if (isset($config['servers'])) { | |
$this->config('servers', $config['servers'], false); | |
} | |
if (!is_array($this->_config['servers'])) { | |
$this->_config['servers'] = [$this->_config['servers']]; | |
} | |
if (!isset($this->_Memcache)) { | |
$return = false; | |
$this->_Memcache = new \Memcache(); | |
foreach ($this->_config['servers'] as $server) { | |
list($host, $port) = $this->_parseServerString($server); | |
if ($this->_Memcache->addServer($host, $port, $this->_config['persistent'])) { | |
$return = true; | |
} | |
} | |
return $return; | |
} | |
return true; | |
} | |
/** | |
* Parses the server address into the host/port. Handles both IPv6 and IPv4 | |
* addresses and Unix sockets | |
* | |
* @param string $server The server address string. | |
* @return array Array containing host, port | |
*/ | |
protected function _parseServerString($server) { | |
if ($server[0] === 'u') { | |
return [ | |
$server, | |
0, | |
]; | |
} | |
if (substr($server, 0, 1) === '[') { | |
$position = strpos($server, ']:'); | |
if ($position !== false) { | |
$position++; | |
} | |
} else { | |
$position = strpos($server, ':'); | |
} | |
$port = $this->_config['port']; | |
$host = $server; | |
if ($position !== false) { | |
$host = substr($server, 0, $position); | |
$port = substr($server, $position + 1); | |
} | |
return [ | |
$host, | |
$port, | |
]; | |
} | |
/** | |
* Write data for key into cache. When using memcache as your cache engine | |
* remember that the Memcache pecl extension does not support cache expiry times greater | |
* than 30 days in the future. Any duration greater than 30 days will be treated as never expiring. | |
* | |
* @param string $key Identifier for the data | |
* @param mixed $value Data to be cached | |
* @return bool True if the data was successfully cached, false on failure | |
* @see http://php.net/manual/en/memcache.set.php | |
*/ | |
public function write($key, $value) { | |
$duration = $this->_config['duration']; | |
if ($duration > 30 * DAY) { | |
$duration = 0; | |
} | |
return $this->_Memcache->set($key, $value, $this->_config['compress'], $duration); | |
} | |
/** | |
* Read a key from the cache | |
* | |
* @param string $key Identifier for the data | |
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it | |
*/ | |
public function read($key) { | |
return $this->_Memcache->get($key); | |
} | |
/** | |
* Increments the value of an integer cached key | |
* | |
* @param string $key Identifier for the data | |
* @param int $offset How much to increment | |
* @return New incremented value, false otherwise | |
* @throws InvalidArgumentException when you try to increment with compress = true | |
*/ | |
public function increment($key, $offset = 1) { | |
if ($this->_config['compress']) { | |
throw new InvalidArgumentException(sprintf('Method %s not implemented for compressed cache in %s', 'increment()', __CLASS__)); | |
} | |
return $this->_Memcache->increment($key, $offset); | |
} | |
/** | |
* Decrements the value of an integer cached key | |
* | |
* @param string $key Identifier for the data | |
* @param int $offset How much to subtract | |
* @return New decremented value, false otherwise | |
* @throws InvalidArgumentException when you try to decrement with compress = true | |
*/ | |
public function decrement($key, $offset = 1) { | |
if ($this->_config['compress']) { | |
throw new InvalidArgumentException(sprintf('Method %s not implemented for compressed cache in %s', 'decrement()', __CLASS__)); | |
} | |
return $this->_Memcache->decrement($key, $offset); | |
} | |
/** | |
* Delete a key from the cache | |
* | |
* @param string $key Identifier for the data | |
* @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed | |
*/ | |
public function delete($key) { | |
return $this->_Memcache->delete($key); | |
} | |
/** | |
* Delete all keys from the cache | |
* | |
* @param bool $check If true no deletes will occur and instead CakePHP will rely | |
* on key TTL values. | |
* @return bool True if the cache was successfully cleared, false otherwise | |
*/ | |
public function clear($check) { | |
if ($check) { | |
return true; | |
} | |
foreach ($this->_Memcache->getExtendedStats('slabs', 0) as $slabs) { | |
foreach (array_keys($slabs) as $slabId) { | |
if (!is_numeric($slabId)) { | |
continue; | |
} | |
foreach ($this->_Memcache->getExtendedStats('cachedump', $slabId, 0) as $stats) { | |
if (!is_array($stats)) { | |
continue; | |
} | |
foreach (array_keys($stats) as $key) { | |
if (strpos($key, $this->_config['prefix']) === 0) { | |
$this->_Memcache->delete($key); | |
} | |
} | |
} | |
} | |
} | |
return true; | |
} | |
/** | |
* Connects to a server in connection pool | |
* | |
* @param string $host host ip address or name | |
* @return bool True if memcache server was connected | |
*/ | |
public function connect($host) { | |
if ($this->_Memcache->getServerStatus($host, $this->_config['port']) === 0) { | |
if ($this->_Memcache->connect($host, $this->_config['port'])) { | |
return true; | |
} | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Returns the `group value` for each of the configured groups | |
* If the group initial value was not found, then it initializes | |
* the group accordingly. | |
* | |
* @return array | |
*/ | |
public function groups() { | |
if (empty($this->_compiledGroupNames)) { | |
foreach ($this->_config['groups'] as $group) { | |
$this->_compiledGroupNames[] = $this->_config['prefix'] . $group; | |
} | |
} | |
$groups = $this->_Memcache->get($this->_compiledGroupNames); | |
if (count($groups) !== count($this->_config['groups'])) { | |
foreach ($this->_compiledGroupNames as $group) { | |
if (!isset($groups[$group])) { | |
$this->_Memcache->set($group, 1, false, 0); | |
$groups[$group] = 1; | |
} | |
} | |
ksort($groups); | |
} | |
$result = []; | |
$groups = array_values($groups); | |
foreach ($this->_config['groups'] as $i => $group) { | |
$result[] = $group . $groups[$i]; | |
} | |
return $result; | |
} | |
/** | |
* Increments the group value to simulate deletion of all keys under a group | |
* old values will remain in storage until they expire. | |
* | |
* @param string $group The group to clear. | |
* @return bool success | |
*/ | |
public function clearGroup($group) { | |
return (bool)$this->_Memcache->increment($this->_config['prefix'] . $group); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment