PageRenderTime 74ms CodeModel.GetById 40ms app.highlight 10ms RepoModel.GetById 20ms app.codeStats 0ms

/yii/web/widgets/COutputCache.php

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