123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- <?php
- /**
- * COutputCache class file.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.yiiframework.com/
- * @copyright 2008-2013 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
- /**
- * COutputCache enables caching the output generated by an action or a view fragment.
- *
- * If the output to be displayed is found valid in cache, the cached
- * version will be displayed instead, which saves the time for generating
- * the original output.
- *
- * Since COutputCache extends from {@link CFilterWidget}, it can be used
- * as either a filter (for action caching) or a widget (for fragment caching).
- * For the latter, the shortcuts {@link CBaseController::beginCache()} and {@link CBaseController::endCache()}
- * are often used instead, like the following in a view file:
- * <pre>
- * if($this->beginCache('cacheName',array('property1'=>'value1',...))
- * {
- * // ... display the content to be cached here
- * $this->endCache();
- * }
- * </pre>
- *
- * COutputCache must work with a cache application component specified via {@link cacheID}.
- * If the cache application component is not available, COutputCache will be disabled.
- *
- * The validity of the cached content is determined based on two factors:
- * the {@link duration} and the cache {@link dependency}.
- * The former specifies the number of seconds that the data can remain
- * valid in cache (defaults to 60s), while the latter specifies conditions
- * that the cached data depends on. If a dependency changes,
- * (e.g. relevant data in DB are updated), the cached data will be invalidated.
- * For more details about cache dependency, see {@link CCacheDependency}.
- *
- * Sometimes, it is necessary to turn off output caching only for certain request types.
- * For example, we only want to cache a form when it is initially requested;
- * any subsequent display of the form should not be cached because it contains user input.
- * We can set {@link requestTypes} to be <code>array('GET')</code> to accomplish this task.
- *
- * The content fetched from cache may be variated with respect to
- * some parameters. COutputCache supports four kinds of variations:
- * <ul>
- * <li>{@link varyByRoute}: this specifies whether the cached content
- * should be varied with the requested route (controller and action)</li>
- * <li>{@link varyByParam}: this specifies a list of GET parameter names
- * and uses the corresponding values to determine the version of the cached content.</li>
- * <li>{@link varyBySession}: this specifies whether the cached content
- * should be varied with the user session.</li>
- * <li>{@link varyByExpression}: this specifies whether the cached content
- * should be varied with the result of the specified PHP expression.</li>
- * <li>{@link varyByLanguage}: this specifies whether the cached content
- * should by varied with the user's language. Available since 1.1.14.</li>
- * </ul>
- * For more advanced variation, override {@link getBaseCacheKey()} method.
- *
- * @property boolean $isContentCached Whether the content can be found from cache.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @package system.web.widgets
- * @since 1.0
- */
- class COutputCache extends CFilterWidget
- {
- /**
- * Prefix to the keys for storing cached data
- */
- const CACHE_KEY_PREFIX='Yii.COutputCache.';
- /**
- * @var integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
- * If it is 0, existing cached content would be removed from the cache.
- * If it is a negative value, the cache will be disabled (any existing cached content will
- * remain in the cache.)
- *
- * Note, if cache dependency changes or cache space is limited,
- * the data may be purged out of cache earlier.
- */
- public $duration=60;
- /**
- * @var boolean whether the content being cached should be differentiated according to route.
- * A route consists of the requested controller ID and action ID.
- * Defaults to true.
- */
- public $varyByRoute=true;
- /**
- * @var boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
- */
- public $varyBySession=false;
- /**
- * @var array list of GET parameters that should participate in cache key calculation.
- * By setting this property, the output cache will use different cached data
- * for each different set of GET parameter values.
- */
- public $varyByParam;
- /**
- * @var string a PHP expression whose result is used in the cache key calculation.
- * By setting this property, the output cache will use different cached data
- * for each different expression result.
- * The expression can also be a valid PHP callback,
- * including class method name (array(ClassName/Object, MethodName)),
- * or anonymous function (PHP 5.3.0+). The function/method signature should be as follows:
- * <pre>
- * function foo($cache) { ... }
- * </pre>
- * where $cache refers to the output cache component.
- *
- * The PHP expression will be evaluated using {@link evaluateExpression}.
- *
- * A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
- * please refer to the {@link http://www.php.net/manual/en/language.expressions.php php manual}.
- */
- public $varyByExpression;
- /**
- * @var boolean whether the content being cached should be differentiated according to user's language.
- * A language is retrieved via Yii::app()->language.
- * Defaults to false.
- * @since 1.1.14
- */
- public $varyByLanguage=false;
- /**
- * @var array list of request types (e.g. GET, POST) for which the cache should be enabled only.
- * Defaults to null, meaning all request types.
- */
- public $requestTypes;
- /**
- * @var string the ID of the cache application component. Defaults to 'cache' (the primary cache application component.)
- */
- public $cacheID='cache';
- /**
- * @var mixed the dependency that the cached content depends on.
- * This can be either an object implementing {@link ICacheDependency} interface or an array
- * specifying the configuration of the dependency object. For example,
- * <pre>
- * array(
- * 'class'=>'CDbCacheDependency',
- * 'sql'=>'SELECT MAX(lastModified) FROM Post',
- * )
- * </pre>
- * would make the output cache depends on the last modified time of all posts.
- * If any post has its modification time changed, the cached content would be invalidated.
- */
- public $dependency;
- private $_key;
- private $_cache;
- private $_contentCached;
- private $_content;
- private $_actions;
- /**
- * Performs filtering before the action is executed.
- * This method is meant to be overridden by child classes if begin-filtering is needed.
- * @param CFilterChain $filterChain list of filters being applied to an action
- * @return boolean whether the filtering process should stop after this filter. Defaults to false.
- */
- public function filter($filterChain)
- {
- if(!$this->getIsContentCached())
- $filterChain->run();
- $this->run();
- }
- /**
- * Marks the start of content to be cached.
- * Content displayed after this method call and before {@link endCache()}
- * will be captured and saved in cache.
- * This method does nothing if valid content is already found in cache.
- */
- public function init()
- {
- if($this->getIsContentCached())
- $this->replayActions();
- elseif($this->_cache!==null)
- {
- $this->getController()->getCachingStack()->push($this);
- ob_start();
- ob_implicit_flush(false);
- }
- }
- /**
- * Marks the end of content to be cached.
- * Content displayed before this method call and after {@link init()}
- * will be captured and saved in cache.
- * This method does nothing if valid content is already found in cache.
- */
- public function run()
- {
- if($this->getIsContentCached())
- {
- if($this->getController()->isCachingStackEmpty())
- echo $this->getController()->processDynamicOutput($this->_content);
- else
- echo $this->_content;
- }
- elseif($this->_cache!==null)
- {
- $this->_content=ob_get_clean();
- $this->getController()->getCachingStack()->pop();
- $data=array($this->_content,$this->_actions);
- if(is_array($this->dependency))
- $this->dependency=Yii::createComponent($this->dependency);
- $this->_cache->set($this->getCacheKey(),$data,$this->duration,$this->dependency);
- if($this->getController()->isCachingStackEmpty())
- echo $this->getController()->processDynamicOutput($this->_content);
- else
- echo $this->_content;
- }
- }
- /**
- * @return boolean whether the content can be found from cache
- */
- public function getIsContentCached()
- {
- if($this->_contentCached!==null)
- return $this->_contentCached;
- else
- return $this->_contentCached=$this->checkContentCache();
- }
- /**
- * Looks for content in cache.
- * @return boolean whether the content is found in cache.
- */
- protected function checkContentCache()
- {
- if((empty($this->requestTypes) || in_array(Yii::app()->getRequest()->getRequestType(),$this->requestTypes))
- && ($this->_cache=$this->getCache())!==null)
- {
- if($this->duration>0 && ($data=$this->_cache->get($this->getCacheKey()))!==false)
- {
- $this->_content=$data[0];
- $this->_actions=$data[1];
- return true;
- }
- if($this->duration==0)
- $this->_cache->delete($this->getCacheKey());
- if($this->duration<=0)
- $this->_cache=null;
- }
- return false;
- }
- /**
- * @return ICache the cache used for caching the content.
- */
- protected function getCache()
- {
- return Yii::app()->getComponent($this->cacheID);
- }
- /**
- * Caclulates the base cache key.
- * The calculated key will be further variated in {@link getCacheKey}.
- * Derived classes may override this method if more variations are needed.
- * @return string basic cache key without variations
- */
- protected function getBaseCacheKey()
- {
- return self::CACHE_KEY_PREFIX.$this->getId().'.';
- }
- /**
- * Calculates the cache key.
- * The key is calculated based on {@link getBaseCacheKey} and other factors, including
- * {@link varyByRoute}, {@link varyByParam}, {@link varyBySession} and {@link varyByLanguage}.
- * @return string cache key
- */
- protected function getCacheKey()
- {
- if($this->_key!==null)
- return $this->_key;
- else
- {
- $key=$this->getBaseCacheKey().'.';
- if($this->varyByRoute)
- {
- $controller=$this->getController();
- $key.=$controller->getUniqueId().'/';
- if(($action=$controller->getAction())!==null)
- $key.=$action->getId();
- }
- $key.='.';
- if($this->varyBySession)
- $key.=Yii::app()->getSession()->getSessionID();
- $key.='.';
- if(is_array($this->varyByParam) && isset($this->varyByParam[0]))
- {
- $params=array();
- foreach($this->varyByParam as $name)
- {
- if(isset($_GET[$name]))
- $params[$name]=$_GET[$name];
- else
- $params[$name]='';
- }
- $key.=serialize($params);
- }
- $key.='.';
- if($this->varyByExpression!==null)
- $key.=$this->evaluateExpression($this->varyByExpression);
- $key.='.';
- if($this->varyByLanguage)
- $key.=Yii::app()->language;
- $key.='.';
- return $this->_key=$key;
- }
- }
- /**
- * Records a method call when this output cache is in effect.
- * When the content is served from the output cache, the recorded
- * method will be re-invoked.
- * @param string $context a property name of the controller. The property should refer to an object
- * whose method is being recorded. If empty it means the controller itself.
- * @param string $method the method name
- * @param array $params parameters passed to the method
- */
- public function recordAction($context,$method,$params)
- {
- $this->_actions[]=array($context,$method,$params);
- }
- /**
- * Replays the recorded method calls.
- */
- protected function replayActions()
- {
- if(empty($this->_actions))
- return;
- $controller=$this->getController();
- $cs=Yii::app()->getClientScript();
- foreach($this->_actions as $action)
- {
- if($action[0]==='clientScript')
- $object=$cs;
- elseif($action[0]==='')
- $object=$controller;
- else
- $object=$controller->{$action[0]};
- if(method_exists($object,$action[1]))
- call_user_func_array(array($object,$action[1]),$action[2]);
- elseif($action[0]==='' && function_exists($action[1]))
- call_user_func_array($action[1],$action[2]);
- else
- throw new CException(Yii::t('yii','Unable to replay the action "{object}.{method}". The method does not exist.',
- array('object'=>$action[0],
- 'method'=>$action[1])));
- }
- }
- }
|