I’ve searched this forum but not found any topic about chain of cache.
So I write this post, hope this will help someone, and please let me know if there has any problem in code.
I face a scenerio when I need caching in chain like this:
$_GLOBAL(cache in one request) -> MEMCACHE -> DBCACHE -> FILECACHE …
So, this is the code:
<?php
/**
* Chain of responsibility pattern
* Configure in main.php like these:
*
* 'cache'=>array(
* 'class'=>'application.components.ChainCache',
* 'chainCache'=>array(
* array(
* 'class'=>'GlobalCache', //cache for a request (a static $variable)
* ),
* array(
* 'class'=>'CDbCache',
* 'autoCreateCacheTable' => true,
* ),
* ),
* ),
*/
class ChainCache extends CCache {
/**
* Array of Cache classes configured in main.php
* @var array
*/
public $chainCache = array();
/**
* Default expire time of a cache when it's auto set a value
* @var integer
*/
public $defaultExpireTime = 300;
/**
* Array of cache components, corresponding to $chainCache config
* @var array
*/
private $caches = array();
/**
* Lowest level cache.
* @var CCache
*/
private $cache;
public function init() {
parent::init();
if (empty($this->chainCache)) {
throw new Exception('No cache class declared in [chainCache] configuration');
}
$c = count($this->chainCache);
for ($i = 0; $i < $c; $i++) {
$this->caches[$i] = Yii::createComponent($this->chainCache[$i]);
$this->caches[$i]->init();
$behavior = new ChainCacheBehavior();
$this->caches[$i]->attachBehavior('nextCache', $behavior);
}
for ($i = 0; $i < $c - 1; $i++) {
$this->caches[$i]->assignNext($this->caches[$i + 1]);
}
$this->cache = $this->caches[0];
}
protected function getValue($key) {
$val = $this->cache->getValue($key);
if ($val == false) {
$cache = $this->cache;
$previous = $cache;
while (($cache = $cache->next) != null) {
$val = $cache->getValue($key);
if ($val !== false) {
$previous->setValue($key, $val, $this->defaultExpireTime);
return $val;
}
$previous = $cache;
}
}
return $val;
}
protected function setValue($key, $value, $expire) {
$ret = $this->cache->setValue($key, $value, $expire);
if ($ret === true) {
$cache = $this->cache;
while (($cache = $cache->next) != null) {
$ret = $cache->setValue($key, $value, $expire);
if ($ret === false)
return $ret;
}
}
return $ret;
}
protected function addValue($key, $value, $expire) {
$ret = $this->cache->addValue($key, $value, $expire);
if ($ret === true) {
$cache = $this->cache;
while (($cache = $cache->next) != null) {
$ret = $cache->addValue($key, $value, $expire);
if ($ret === false)
return $ret;
}
}
return $ret;
}
protected function deleteValue($key) {
$ret = $this->cache->deleteValue($key);
if ($ret === true) {
$cache = $this->cache;
while (($cache = $cache->next) != null) {
$ret = $cache->deleteValue($key);
if ($ret === false)
return $ret;
}
}
return $ret;
}
protected function flushValues() {
$ret = $this->cache->flushValues();
if ($ret === true) {
$cache = $this->cache;
if (($cache = $cache->next) != null) {
$ret = $this->cache->next->flushValues();
if ($ret === false)
return $ret;
}
}
return $ret;
}
}
/**
* Add a new property for CCache's child classes
*/
class ChainCacheBehavior extends CBehavior {
public $nextCache = null;
public function assignNext($cache) {
$this->nextCache = &$cache;
return $cache;
}
public function getNext() {
if ($this->getOwner() != null)
return $this->nextCache;
return null;
}
}
class GlobalCache extends CCache {
private static $global = array();
protected function getValue($key) {
if (isset(self::$global[$key]))
return self::$global[$key];
return false;
}
protected function setValue($key, $value, $expire) {
self::$global[$key] = $value;
return true;
}
protected function addValue($key, $value, $expire) {
if (!isset(self::$global[$key])) {
self::$global[$key] = $value;
return true;
}
return false;
}
protected function deleteValue($key) {
unset(self::$global[$key]);
return true;
}
protected function flushValues() {
unset(self::$global);
return true;
}
}
How to use:
-
Copy the code above into a file named ChainCache.php (protected/components/ChainCache.php):
-
Define the config for caching in main.php
return array(
//....
'components'=>array(
//....
'cache'=>array(
'class'=>'application.components.ChainCache',
'chainCache'=>array(
array(
'class'=>'GlobalCache',//class in ChainCache.php
),
array(
'class'=>'CDbCache',
'autoCreateCacheTable' => false,
),
//define new cache classes if need
),
),
),
);
- Use in your code:
Yii::app()->cache->set('test', 'Hello');
Yii::app()->cache->get('test');
That’s all.
Thanks for reading.