PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

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

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