00001 <?php
00027 require_once 'Zend/Cache/Backend/ExtendedInterface.php';
00028
00032 require_once 'Zend/Cache/Backend.php';
00033
00034
00042 class Zend_Cache_Backend_TwoLevels extends Zend_Cache_Backend implements Zend_Cache_Backend_ExtendedInterface
00043 {
00080 protected $_options = array(
00081 'slow_backend' => 'File',
00082 'fast_backend' => 'Apc',
00083 'slow_backend_options' => array(),
00084 'fast_backend_options' => array(),
00085 'stats_update_factor' => 10,
00086 'slow_backend_custom_naming' => false,
00087 'fast_backend_custom_naming' => false,
00088 'slow_backend_autoload' => false,
00089 'fast_backend_autoload' => false,
00090 'auto_refresh_fast_cache' => true
00091 );
00092
00098 protected $_slowBackend;
00099
00105 protected $_fastBackend;
00106
00112 protected $_fastBackendFillingPercentage = null;
00113
00121 public function __construct(array $options = array())
00122 {
00123 parent::__construct($options);
00124 if ($this->_options['slow_backend'] === null) {
00125 Zend_Cache::throwException('slow_backend option has to set');
00126 }
00127 if ($this->_options['fast_backend'] === null) {
00128 Zend_Cache::throwException('fast_backend option has to set');
00129 }
00130 $this->_slowBackend = Zend_Cache::_makeBackend($this->_options['slow_backend'], $this->_options['slow_backend_options'], $this->_options['slow_backend_custom_naming'], $this->_options['slow_backend_autoload']);
00131 $this->_fastBackend = Zend_Cache::_makeBackend($this->_options['fast_backend'], $this->_options['fast_backend_options'], $this->_options['fast_backend_custom_naming'], $this->_options['fast_backend_autoload']);
00132 if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_slowBackend))) {
00133 Zend_Cache::throwException('slow_backend must implement the Zend_Cache_Backend_ExtendedInterface interface');
00134 }
00135 if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_fastBackend))) {
00136 Zend_Cache::throwException('fast_backend must implement the Zend_Cache_Backend_ExtendedInterface interface');
00137 }
00138 $this->_slowBackend->setDirectives($this->_directives);
00139 $this->_fastBackend->setDirectives($this->_directives);
00140 }
00141
00148 public function test($id)
00149 {
00150 $fastTest = $this->_fastBackend->test($id);
00151 if ($fastTest) {
00152 return $fastTest;
00153 } else {
00154 return $this->_slowBackend->test($id);
00155 }
00156 }
00157
00171 public function save($data, $id, $tags = array(), $specificLifetime = false, $priority = 8)
00172 {
00173 $usage = $this->_getFastFillingPercentage('saving');
00174 $boolFast = true;
00175 $lifetime = $this->getLifetime($specificLifetime);
00176 $preparedData = $this->_prepareData($data, $lifetime, $priority);
00177 if (($priority > 0) && (10 * $priority >= $usage)) {
00178 $fastLifetime = $this->_getFastLifetime($lifetime, $priority);
00179 $boolFast = $this->_fastBackend->save($preparedData, $id, array(), $fastLifetime);
00180 }
00181 $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime);
00182 return ($boolFast && $boolSlow);
00183 }
00184
00194 public function load($id, $doNotTestCacheValidity = false)
00195 {
00196 $res = $this->_fastBackend->load($id, $doNotTestCacheValidity);
00197 if ($res === false) {
00198 $res = $this->_slowBackend->load($id, $doNotTestCacheValidity);
00199 if ($res === false) {
00200
00201 return false;
00202 }
00203 }
00204 $array = unserialize($res);
00205
00206 if ($this->_options['auto_refresh_fast_cache']) {
00207 if ($array['priority'] == 10) {
00208
00209 return $array['data'];
00210 }
00211 $newFastLifetime = $this->_getFastLifetime($array['lifetime'], $array['priority'], time() - $array['expire']);
00212
00213 $usage = $this->_getFastFillingPercentage('loading');
00214 if (($array['priority'] > 0) && (10 * $array['priority'] >= $usage)) {
00215
00216 $preparedData = $this->_prepareData($array['data'], $array['lifetime'], $array['priority']);
00217 $this->_fastBackend->save($preparedData, $id, array(), $newFastLifetime);
00218 }
00219 }
00220 return $array['data'];
00221 }
00222
00229 public function remove($id)
00230 {
00231 $boolFast = $this->_fastBackend->remove($id);
00232 $boolSlow = $this->_slowBackend->remove($id);
00233 return $boolFast && $boolSlow;
00234 }
00235
00254 public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
00255 {
00256 switch($mode) {
00257 case Zend_Cache::CLEANING_MODE_ALL:
00258 $boolFast = $this->_fastBackend->clean(Zend_Cache::CLEANING_MODE_ALL);
00259 $boolSlow = $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_ALL);
00260 return $boolFast && $boolSlow;
00261 break;
00262 case Zend_Cache::CLEANING_MODE_OLD:
00263 return $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_OLD);
00264 case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
00265 $ids = $this->_slowBackend->getIdsMatchingTags($tags);
00266 $res = true;
00267 foreach ($ids as $id) {
00268 $bool = $this->remove($id);
00269 $res = $res && $bool;
00270 }
00271 return $res;
00272 break;
00273 case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
00274 $ids = $this->_slowBackend->getIdsNotMatchingTags($tags);
00275 $res = true;
00276 foreach ($ids as $id) {
00277 $bool = $this->remove($id);
00278 $res = $res && $bool;
00279 }
00280 return $res;
00281 break;
00282 case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
00283 $ids = $this->_slowBackend->getIdsMatchingAnyTags($tags);
00284 $res = true;
00285 foreach ($ids as $id) {
00286 $bool = $this->remove($id);
00287 $res = $res && $bool;
00288 }
00289 return $res;
00290 break;
00291 default:
00292 Zend_Cache::throwException('Invalid mode for clean() method');
00293 break;
00294 }
00295 }
00296
00302 public function getIds()
00303 {
00304 return $this->_slowBackend->getIds();
00305 }
00306
00312 public function getTags()
00313 {
00314 return $this->_slowBackend->getTags();
00315 }
00316
00325 public function getIdsMatchingTags($tags = array())
00326 {
00327 return $this->_slowBackend->getIdsMatchingTags($tags);
00328 }
00329
00338 public function getIdsNotMatchingTags($tags = array())
00339 {
00340 return $this->_slowBackend->getIdsNotMatchingTags($tags);
00341 }
00342
00351 public function getIdsMatchingAnyTags($tags = array())
00352 {
00353 return $this->_slowBackend->getIdsMatchingAnyTags($tags);
00354 }
00355
00356
00362 public function getFillingPercentage()
00363 {
00364 return $this->_slowBackend->getFillingPercentage();
00365 }
00366
00378 public function getMetadatas($id)
00379 {
00380 return $this->_slowBackend->getMetadatas($id);
00381 }
00382
00390 public function touch($id, $extraLifetime)
00391 {
00392 return $this->_slowBackend->touch($id, $extraLifetime);
00393 }
00394
00409 public function getCapabilities()
00410 {
00411 $slowBackendCapabilities = $this->_slowBackend->getCapabilities();
00412 return array(
00413 'automatic_cleaning' => $slowBackendCapabilities['automatic_cleaning'],
00414 'tags' => $slowBackendCapabilities['tags'],
00415 'expired_read' => $slowBackendCapabilities['expired_read'],
00416 'priority' => $slowBackendCapabilities['priority'],
00417 'infinite_lifetime' => $slowBackendCapabilities['infinite_lifetime'],
00418 'get_list' => $slowBackendCapabilities['get_list']
00419 );
00420 }
00421
00430 private function _prepareData($data, $lifetime, $priority)
00431 {
00432 $lt = $lifetime;
00433 if ($lt === null) {
00434 $lt = 9999999999;
00435 }
00436 return serialize(array(
00437 'data' => $data,
00438 'lifetime' => $lifetime,
00439 'expire' => time() + $lt,
00440 'priority' => $priority
00441 ));
00442 }
00443
00452 private function _getFastLifetime($lifetime, $priority, $maxLifetime = null)
00453 {
00454 if ($lifetime === null) {
00455
00456
00457 $fastLifetime = (int) (2592000 / (11 - $priority));
00458 } else {
00459 $fastLifetime = (int) ($lifetime / (11 - $priority));
00460 }
00461 if (($maxLifetime !== null) && ($maxLifetime >= 0)) {
00462 if ($fastLifetime > $maxLifetime) {
00463 return $maxLifetime;
00464 }
00465 }
00466 return $fastLifetime;
00467 }
00468
00476 public function ___expire($id)
00477 {
00478 $this->_fastBackend->remove($id);
00479 $this->_slowBackend->___expire($id);
00480 }
00481
00482 private function _getFastFillingPercentage($mode)
00483 {
00484
00485 if ($mode == 'saving') {
00486
00487 if ($this->_fastBackendFillingPercentage === null) {
00488 $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage();
00489 } else {
00490 $rand = rand(1, $this->_options['stats_update_factor']);
00491 if ($rand == 1) {
00492
00493 $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage();
00494 }
00495 }
00496 } else {
00497
00498
00499 if ($this->_fastBackendFillingPercentage === null) {
00500 $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage();
00501 }
00502 }
00503 return $this->_fastBackendFillingPercentage;
00504 }
00505
00506 }