PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/web/widgets/COutputCache.php

https://bitbucket.org/wildanm/zurmo
PHP | 347 lines | 156 code | 18 blank | 173 comment | 34 complexity | a4c124949b347298f260e5acbce91627 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, BSD-2-Clause, GPL-3.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * COutputCache class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * COutputCache enables caching the output generated by an action or a view fragment.
  12. *
  13. * If the output to be displayed is found valid in cache, the cached
  14. * version will be displayed instead, which saves the time for generating
  15. * the original output.
  16. *
  17. * Since COutputCache extends from {@link CFilterWidget}, it can be used
  18. * as either a filter (for action caching) or a widget (for fragment caching).
  19. * For the latter, the shortcuts {@link CBaseController::beginCache()} and {@link CBaseController::endCache()}
  20. * are often used instead, like the following in a view file:
  21. * <pre>
  22. * if($this->beginCache('cacheName',array('property1'=>'value1',...))
  23. * {
  24. * // ... display the content to be cached here
  25. * $this->endCache();
  26. * }
  27. * </pre>
  28. *
  29. * COutputCache must work with a cache application component specified via {@link cacheID}.
  30. * If the cache application component is not available, COutputCache will be disabled.
  31. *
  32. * The validity of the cached content is determined based on two factors:
  33. * the {@link duration} and the cache {@link dependency}.
  34. * The former specifies the number of seconds that the data can remain
  35. * valid in cache (defaults to 60s), while the latter specifies conditions
  36. * that the cached data depends on. If a dependency changes,
  37. * (e.g. relevant data in DB are updated), the cached data will be invalidated.
  38. * For more details about cache dependency, see {@link CCacheDependency}.
  39. *
  40. * Sometimes, it is necessary to turn off output caching only for certain request types.
  41. * For example, we only want to cache a form when it is initially requested;
  42. * any subsequent display of the form should not be cached because it contains user input.
  43. * We can set {@link requestTypes} to be <code>array('GET')</code> to accomplish this task.
  44. *
  45. * The content fetched from cache may be variated with respect to
  46. * some parameters. COutputCache supports four kinds of variations:
  47. * <ul>
  48. * <li>{@link varyByRoute}: this specifies whether the cached content
  49. * should be varied with the requested route (controller and action)</li>
  50. * <li>{@link varyByParam}: this specifies a list of GET parameter names
  51. * and uses the corresponding values to determine the version of the cached content.</li>
  52. * <li>{@link varyBySession}: this specifies whether the cached content
  53. * should be varied with the user session.</li>
  54. * <li>{@link varyByExpression}: this specifies whether the cached content
  55. * should be varied with the result of the specified PHP expression.</li>
  56. * </ul>
  57. * For more advanced variation, override {@link getBaseCacheKey()} method.
  58. *
  59. * @property boolean $isContentCached Whether the content can be found from cache.
  60. *
  61. * @author Qiang Xue <qiang.xue@gmail.com>
  62. * @version $Id$
  63. * @package system.web.widgets
  64. * @since 1.0
  65. */
  66. class COutputCache extends CFilterWidget
  67. {
  68. /**
  69. * Prefix to the keys for storing cached data
  70. */
  71. const CACHE_KEY_PREFIX='Yii.COutputCache.';
  72. /**
  73. * @var integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
  74. * If it is 0, existing cached content would be removed from the cache.
  75. * If it is a negative value, the cache will be disabled (any existing cached content will
  76. * remain in the cache.)
  77. *
  78. * Note, if cache dependency changes or cache space is limited,
  79. * the data may be purged out of cache earlier.
  80. */
  81. public $duration=60;
  82. /**
  83. * @var boolean whether the content being cached should be differentiated according to route.
  84. * A route consists of the requested controller ID and action ID.
  85. * Defaults to true.
  86. */
  87. public $varyByRoute=true;
  88. /**
  89. * @var boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
  90. */
  91. public $varyBySession=false;
  92. /**
  93. * @var array list of GET parameters that should participate in cache key calculation.
  94. * By setting this property, the output cache will use different cached data
  95. * for each different set of GET parameter values.
  96. */
  97. public $varyByParam;
  98. /**
  99. * @var string a PHP expression whose result is used in the cache key calculation.
  100. * By setting this property, the output cache will use different cached data
  101. * for each different expression result.
  102. * The expression can also be a valid PHP callback,
  103. * including class method name (array(ClassName/Object, MethodName)),
  104. * or anonymous function (PHP 5.3.0+). The function/method signature should be as follows:
  105. * <pre>
  106. * function foo($cache) { ... }
  107. * </pre>
  108. * where $cache refers to the output cache component.
  109. */
  110. public $varyByExpression;
  111. /**
  112. * @var array list of request types (e.g. GET, POST) for which the cache should be enabled only.
  113. * Defaults to null, meaning all request types.
  114. */
  115. public $requestTypes;
  116. /**
  117. * @var string the ID of the cache application component. Defaults to 'cache' (the primary cache application component.)
  118. */
  119. public $cacheID='cache';
  120. /**
  121. * @var mixed the dependency that the cached content depends on.
  122. * This can be either an object implementing {@link ICacheDependency} interface or an array
  123. * specifying the configuration of the dependency object. For example,
  124. * <pre>
  125. * array(
  126. * 'class'=>'CDbCacheDependency',
  127. * 'sql'=>'SELECT MAX(lastModified) FROM Post',
  128. * )
  129. * </pre>
  130. * would make the output cache depends on the last modified time of all posts.
  131. * If any post has its modification time changed, the cached content would be invalidated.
  132. */
  133. public $dependency;
  134. private $_key;
  135. private $_cache;
  136. private $_contentCached;
  137. private $_content;
  138. private $_actions;
  139. /**
  140. * Performs filtering before the action is executed.
  141. * This method is meant to be overridden by child classes if begin-filtering is needed.
  142. * @param CFilterChain $filterChain list of filters being applied to an action
  143. * @return boolean whether the filtering process should stop after this filter. Defaults to false.
  144. */
  145. public function filter($filterChain)
  146. {
  147. if(!$this->getIsContentCached())
  148. $filterChain->run();
  149. $this->run();
  150. }
  151. /**
  152. * Marks the start of content to be cached.
  153. * Content displayed after this method call and before {@link endCache()}
  154. * will be captured and saved in cache.
  155. * This method does nothing if valid content is already found in cache.
  156. */
  157. public function init()
  158. {
  159. if($this->getIsContentCached())
  160. $this->replayActions();
  161. else if($this->_cache!==null)
  162. {
  163. $this->getController()->getCachingStack()->push($this);
  164. ob_start();
  165. ob_implicit_flush(false);
  166. }
  167. }
  168. /**
  169. * Marks the end of content to be cached.
  170. * Content displayed before this method call and after {@link init()}
  171. * will be captured and saved in cache.
  172. * This method does nothing if valid content is already found in cache.
  173. */
  174. public function run()
  175. {
  176. if($this->getIsContentCached())
  177. {
  178. if($this->getController()->isCachingStackEmpty())
  179. echo $this->getController()->processDynamicOutput($this->_content);
  180. else
  181. echo $this->_content;
  182. }
  183. else if($this->_cache!==null)
  184. {
  185. $this->_content=ob_get_clean();
  186. $this->getController()->getCachingStack()->pop();
  187. $data=array($this->_content,$this->_actions);
  188. if(is_array($this->dependency))
  189. $this->dependency=Yii::createComponent($this->dependency);
  190. $this->_cache->set($this->getCacheKey(),$data,$this->duration,$this->dependency);
  191. if($this->getController()->isCachingStackEmpty())
  192. echo $this->getController()->processDynamicOutput($this->_content);
  193. else
  194. echo $this->_content;
  195. }
  196. }
  197. /**
  198. * @return boolean whether the content can be found from cache
  199. */
  200. public function getIsContentCached()
  201. {
  202. if($this->_contentCached!==null)
  203. return $this->_contentCached;
  204. else
  205. return $this->_contentCached=$this->checkContentCache();
  206. }
  207. /**
  208. * Looks for content in cache.
  209. * @return boolean whether the content is found in cache.
  210. */
  211. protected function checkContentCache()
  212. {
  213. if((empty($this->requestTypes) || in_array(Yii::app()->getRequest()->getRequestType(),$this->requestTypes))
  214. && ($this->_cache=$this->getCache())!==null)
  215. {
  216. if($this->duration>0 && ($data=$this->_cache->get($this->getCacheKey()))!==false)
  217. {
  218. $this->_content=$data[0];
  219. $this->_actions=$data[1];
  220. return true;
  221. }
  222. if($this->duration==0)
  223. $this->_cache->delete($this->getCacheKey());
  224. if($this->duration<=0)
  225. $this->_cache=null;
  226. }
  227. return false;
  228. }
  229. /**
  230. * @return ICache the cache used for caching the content.
  231. */
  232. protected function getCache()
  233. {
  234. return Yii::app()->getComponent($this->cacheID);
  235. }
  236. /**
  237. * Caclulates the base cache key.
  238. * The calculated key will be further variated in {@link getCacheKey}.
  239. * Derived classes may override this method if more variations are needed.
  240. * @return string basic cache key without variations
  241. */
  242. protected function getBaseCacheKey()
  243. {
  244. return self::CACHE_KEY_PREFIX.$this->getId().'.';
  245. }
  246. /**
  247. * Calculates the cache key.
  248. * The key is calculated based on {@link getBaseCacheKey} and other factors, including
  249. * {@link varyByRoute}, {@link varyByParam} and {@link varyBySession}.
  250. * @return string cache key
  251. */
  252. protected function getCacheKey()
  253. {
  254. if($this->_key!==null)
  255. return $this->_key;
  256. else
  257. {
  258. $key=$this->getBaseCacheKey().'.';
  259. if($this->varyByRoute)
  260. {
  261. $controller=$this->getController();
  262. $key.=$controller->getUniqueId().'/';
  263. if(($action=$controller->getAction())!==null)
  264. $key.=$action->getId();
  265. }
  266. $key.='.';
  267. if($this->varyBySession)
  268. $key.=Yii::app()->getSession()->getSessionID();
  269. $key.='.';
  270. if(is_array($this->varyByParam) && isset($this->varyByParam[0]))
  271. {
  272. $params=array();
  273. foreach($this->varyByParam as $name)
  274. {
  275. if(isset($_GET[$name]))
  276. $params[$name]=$_GET[$name];
  277. else
  278. $params[$name]='';
  279. }
  280. $key.=serialize($params);
  281. }
  282. $key.='.';
  283. if($this->varyByExpression!==null)
  284. $key.=$this->evaluateExpression($this->varyByExpression);
  285. $key.='.';
  286. return $this->_key=$key;
  287. }
  288. }
  289. /**
  290. * Records a method call when this output cache is in effect.
  291. * When the content is served from the output cache, the recorded
  292. * method will be re-invoked.
  293. * @param string $context a property name of the controller. The property should refer to an object
  294. * whose method is being recorded. If empty it means the controller itself.
  295. * @param string $method the method name
  296. * @param array $params parameters passed to the method
  297. */
  298. public function recordAction($context,$method,$params)
  299. {
  300. $this->_actions[]=array($context,$method,$params);
  301. }
  302. /**
  303. * Replays the recorded method calls.
  304. */
  305. protected function replayActions()
  306. {
  307. if(empty($this->_actions))
  308. return;
  309. $controller=$this->getController();
  310. $cs=Yii::app()->getClientScript();
  311. foreach($this->_actions as $action)
  312. {
  313. if($action[0]==='clientScript')
  314. $object=$cs;
  315. else if($action[0]==='')
  316. $object=$controller;
  317. else
  318. $object=$controller->{$action[0]};
  319. if(method_exists($object,$action[1]))
  320. call_user_func_array(array($object,$action[1]),$action[2]);
  321. else if($action[0]==='' && function_exists($action[1]))
  322. call_user_func_array($action[1],$action[2]);
  323. else
  324. throw new CException(Yii::t('yii','Unable to replay the action "{object}.{method}". The method does not exist.',
  325. array('object'=>$action[0],
  326. 'method'=>$action[1])));
  327. }
  328. }
  329. }