PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/ddonthula/zurmouser
PHP | 346 lines | 156 code | 18 blank | 172 comment | 26 complexity | 7b7c815c0b9f4cb63039624221c39383 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, GPL-2.0, GPL-3.0, BSD-3-Clause, LGPL-3.0
  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. * @package system.web.widgets
  63. * @since 1.0
  64. */
  65. class COutputCache extends CFilterWidget
  66. {
  67. /**
  68. * Prefix to the keys for storing cached data
  69. */
  70. const CACHE_KEY_PREFIX='Yii.COutputCache.';
  71. /**
  72. * @var integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
  73. * If it is 0, existing cached content would be removed from the cache.
  74. * If it is a negative value, the cache will be disabled (any existing cached content will
  75. * remain in the cache.)
  76. *
  77. * Note, if cache dependency changes or cache space is limited,
  78. * the data may be purged out of cache earlier.
  79. */
  80. public $duration=60;
  81. /**
  82. * @var boolean whether the content being cached should be differentiated according to route.
  83. * A route consists of the requested controller ID and action ID.
  84. * Defaults to true.
  85. */
  86. public $varyByRoute=true;
  87. /**
  88. * @var boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
  89. */
  90. public $varyBySession=false;
  91. /**
  92. * @var array list of GET parameters that should participate in cache key calculation.
  93. * By setting this property, the output cache will use different cached data
  94. * for each different set of GET parameter values.
  95. */
  96. public $varyByParam;
  97. /**
  98. * @var string a PHP expression whose result is used in the cache key calculation.
  99. * By setting this property, the output cache will use different cached data
  100. * for each different expression result.
  101. * The expression can also be a valid PHP callback,
  102. * including class method name (array(ClassName/Object, MethodName)),
  103. * or anonymous function (PHP 5.3.0+). The function/method signature should be as follows:
  104. * <pre>
  105. * function foo($cache) { ... }
  106. * </pre>
  107. * where $cache refers to the output cache component.
  108. */
  109. public $varyByExpression;
  110. /**
  111. * @var array list of request types (e.g. GET, POST) for which the cache should be enabled only.
  112. * Defaults to null, meaning all request types.
  113. */
  114. public $requestTypes;
  115. /**
  116. * @var string the ID of the cache application component. Defaults to 'cache' (the primary cache application component.)
  117. */
  118. public $cacheID='cache';
  119. /**
  120. * @var mixed the dependency that the cached content depends on.
  121. * This can be either an object implementing {@link ICacheDependency} interface or an array
  122. * specifying the configuration of the dependency object. For example,
  123. * <pre>
  124. * array(
  125. * 'class'=>'CDbCacheDependency',
  126. * 'sql'=>'SELECT MAX(lastModified) FROM Post',
  127. * )
  128. * </pre>
  129. * would make the output cache depends on the last modified time of all posts.
  130. * If any post has its modification time changed, the cached content would be invalidated.
  131. */
  132. public $dependency;
  133. private $_key;
  134. private $_cache;
  135. private $_contentCached;
  136. private $_content;
  137. private $_actions;
  138. /**
  139. * Performs filtering before the action is executed.
  140. * This method is meant to be overridden by child classes if begin-filtering is needed.
  141. * @param CFilterChain $filterChain list of filters being applied to an action
  142. * @return boolean whether the filtering process should stop after this filter. Defaults to false.
  143. */
  144. public function filter($filterChain)
  145. {
  146. if(!$this->getIsContentCached())
  147. $filterChain->run();
  148. $this->run();
  149. }
  150. /**
  151. * Marks the start of content to be cached.
  152. * Content displayed after this method call and before {@link endCache()}
  153. * will be captured and saved in cache.
  154. * This method does nothing if valid content is already found in cache.
  155. */
  156. public function init()
  157. {
  158. if($this->getIsContentCached())
  159. $this->replayActions();
  160. elseif($this->_cache!==null)
  161. {
  162. $this->getController()->getCachingStack()->push($this);
  163. ob_start();
  164. ob_implicit_flush(false);
  165. }
  166. }
  167. /**
  168. * Marks the end of content to be cached.
  169. * Content displayed before this method call and after {@link init()}
  170. * will be captured and saved in cache.
  171. * This method does nothing if valid content is already found in cache.
  172. */
  173. public function run()
  174. {
  175. if($this->getIsContentCached())
  176. {
  177. if($this->getController()->isCachingStackEmpty())
  178. echo $this->getController()->processDynamicOutput($this->_content);
  179. else
  180. echo $this->_content;
  181. }
  182. elseif($this->_cache!==null)
  183. {
  184. $this->_content=ob_get_clean();
  185. $this->getController()->getCachingStack()->pop();
  186. $data=array($this->_content,$this->_actions);
  187. if(is_array($this->dependency))
  188. $this->dependency=Yii::createComponent($this->dependency);
  189. $this->_cache->set($this->getCacheKey(),$data,$this->duration,$this->dependency);
  190. if($this->getController()->isCachingStackEmpty())
  191. echo $this->getController()->processDynamicOutput($this->_content);
  192. else
  193. echo $this->_content;
  194. }
  195. }
  196. /**
  197. * @return boolean whether the content can be found from cache
  198. */
  199. public function getIsContentCached()
  200. {
  201. if($this->_contentCached!==null)
  202. return $this->_contentCached;
  203. else
  204. return $this->_contentCached=$this->checkContentCache();
  205. }
  206. /**
  207. * Looks for content in cache.
  208. * @return boolean whether the content is found in cache.
  209. */
  210. protected function checkContentCache()
  211. {
  212. if((empty($this->requestTypes) || in_array(Yii::app()->getRequest()->getRequestType(),$this->requestTypes))
  213. && ($this->_cache=$this->getCache())!==null)
  214. {
  215. if($this->duration>0 && ($data=$this->_cache->get($this->getCacheKey()))!==false)
  216. {
  217. $this->_content=$data[0];
  218. $this->_actions=$data[1];
  219. return true;
  220. }
  221. if($this->duration==0)
  222. $this->_cache->delete($this->getCacheKey());
  223. if($this->duration<=0)
  224. $this->_cache=null;
  225. }
  226. return false;
  227. }
  228. /**
  229. * @return ICache the cache used for caching the content.
  230. */
  231. protected function getCache()
  232. {
  233. return Yii::app()->getComponent($this->cacheID);
  234. }
  235. /**
  236. * Caclulates the base cache key.
  237. * The calculated key will be further variated in {@link getCacheKey}.
  238. * Derived classes may override this method if more variations are needed.
  239. * @return string basic cache key without variations
  240. */
  241. protected function getBaseCacheKey()
  242. {
  243. return self::CACHE_KEY_PREFIX.$this->getId().'.';
  244. }
  245. /**
  246. * Calculates the cache key.
  247. * The key is calculated based on {@link getBaseCacheKey} and other factors, including
  248. * {@link varyByRoute}, {@link varyByParam} and {@link varyBySession}.
  249. * @return string cache key
  250. */
  251. protected function getCacheKey()
  252. {
  253. if($this->_key!==null)
  254. return $this->_key;
  255. else
  256. {
  257. $key=$this->getBaseCacheKey().'.';
  258. if($this->varyByRoute)
  259. {
  260. $controller=$this->getController();
  261. $key.=$controller->getUniqueId().'/';
  262. if(($action=$controller->getAction())!==null)
  263. $key.=$action->getId();
  264. }
  265. $key.='.';
  266. if($this->varyBySession)
  267. $key.=Yii::app()->getSession()->getSessionID();
  268. $key.='.';
  269. if(is_array($this->varyByParam) && isset($this->varyByParam[0]))
  270. {
  271. $params=array();
  272. foreach($this->varyByParam as $name)
  273. {
  274. if(isset($_GET[$name]))
  275. $params[$name]=$_GET[$name];
  276. else
  277. $params[$name]='';
  278. }
  279. $key.=serialize($params);
  280. }
  281. $key.='.';
  282. if($this->varyByExpression!==null)
  283. $key.=$this->evaluateExpression($this->varyByExpression);
  284. $key.='.';
  285. return $this->_key=$key;
  286. }
  287. }
  288. /**
  289. * Records a method call when this output cache is in effect.
  290. * When the content is served from the output cache, the recorded
  291. * method will be re-invoked.
  292. * @param string $context a property name of the controller. The property should refer to an object
  293. * whose method is being recorded. If empty it means the controller itself.
  294. * @param string $method the method name
  295. * @param array $params parameters passed to the method
  296. */
  297. public function recordAction($context,$method,$params)
  298. {
  299. $this->_actions[]=array($context,$method,$params);
  300. }
  301. /**
  302. * Replays the recorded method calls.
  303. */
  304. protected function replayActions()
  305. {
  306. if(empty($this->_actions))
  307. return;
  308. $controller=$this->getController();
  309. $cs=Yii::app()->getClientScript();
  310. foreach($this->_actions as $action)
  311. {
  312. if($action[0]==='clientScript')
  313. $object=$cs;
  314. elseif($action[0]==='')
  315. $object=$controller;
  316. else
  317. $object=$controller->{$action[0]};
  318. if(method_exists($object,$action[1]))
  319. call_user_func_array(array($object,$action[1]),$action[2]);
  320. elseif($action[0]==='' && function_exists($action[1]))
  321. call_user_func_array($action[1],$action[2]);
  322. else
  323. throw new CException(Yii::t('yii','Unable to replay the action "{object}.{method}". The method does not exist.',
  324. array('object'=>$action[0],
  325. 'method'=>$action[1])));
  326. }
  327. }
  328. }