Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TOutputCache class file
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TOutputCache.php 2541 2008-10-21 15:05:13Z qiang.xue $
10
 * @package System.Web.UI.WebControls
11
 */
12
 
13
/**
14
 * TOutputCache class.
15
 *
16
 * TOutputCache enables caching a portion of a Web page, also known as
17
 * partial caching. The content being cached can be either static or
18
 * dynamic.
19
 *
20
 * To use TOutputCache, simply enclose the content to be cached
21
 * within the TOutputCache component tag on a template, e.g.,
22
 * <code>
23
 * <com:TOutputCache>
24
 *   content to be cached
25
 * </com:TOutputCache>
26
 * </code>
27
 * where content to be cached can be static text and/or component tags.
28
 *
29
 * The validity of the cached content is determined based on two factors:
30
 * the {@link setDuration Duration} and the cache dependency.
31
 * The former specifies the number of seconds that the data can remain
32
 * valid in cache (defaults to 60s), while the latter specifies conditions
33
 * that the cached data depends on. If a dependency changes,
34
 * (e.g. relevant data in DB are updated), the cached data will be invalidated.
35
 *
36
 * There are two ways to specify cache dependency. One may write event handlers
37
 * to respond to the {@link onCheckDependency OnCheckDependency} event and set
38
 * the event parameter's {@link TOutputCacheCheckDependencyEventParameter::getIsValid IsValid}
39
 * property to indicate whether the cached data remains valid or not.
40
 * One can also extend TOutputCache and override its {@link getCacheDependency}
41
 * function. While the former is easier to use, the latter offers more extensibility.
42
 *
43
 * The content fetched from cache may be variated with respect to
44
 * some parameters. It supports variation with respect to request parameters,
45
 * which is specified by {@link setVaryByParam VaryByParam} property.
46
 * If a specified request parameter is different, a different version of
47
 * cached content is used. This is extremely useful if a page's content
48
 * may be variated according to some GET parameters.
49
 * The content being cached may also be variated with user sessions if
50
 * {@link setVaryBySession VaryBySession} is set true.
51
 * To variate the cached content by other factors, override {@link calculateCacheKey()} method.
52
 *
53
 * Output caches can be nested. An outer cache takes precedence over an
54
 * inner cache. This means, if the content cached by the inner cache expires
55
 * or is invalidated, while that by the outer cache not, the outer cached
56
 * content will be used.
57
 *
58
 * Note, TOutputCache is effective only for non-postback page requests
59
 * and when cache module is enabled.
60
 *
61
 * Do not attempt to address child controls of TOutputCache when the cached
62
 * content is to be used. Use {@link getContentCached ContentCached} property
63
 * to determine whether the content is cached or not.
64
 *
65
 * @author Qiang Xue <qiang.xue@gmail.com>
66
 * @version $Id: TOutputCache.php 2541 2008-10-21 15:05:13Z qiang.xue $
67
 * @package System.Web.UI.WebControls
68
 * @since 3.1
69
 */
70
class TOutputCache extends TControl implements INamingContainer
71
{
72
	const CACHE_ID_PREFIX='prado:outputcache';
73
	private $_cacheModuleID='';
74
	private $_dataCached=false;
75
	private $_cacheAvailable=false;
76
	private $_cacheChecked=false;
77
	private $_cacheKey=null;
78
	private $_duration=60;
79
	private $_cache=null;
80
	private $_contents;
81
	private $_state;
82
	private $_actions=array();
83
	private $_varyByParam='';
84
	private $_keyPrefix='';
85
	private $_varyBySession=false;
86
	private $_cachePostBack=false;
87
	private $_cacheTime=0;
88
 
89
	/**
90
	 * Returns a value indicating whether body contents are allowed for this control.
91
	 * This method overrides the parent implementation by checking if cached
92
	 * content is available or not. If yes, it returns false, otherwise true.
93
	 * @param boolean whether body contents are allowed for this control.
94
	 */
95
	public function getAllowChildControls()
96
	{
97
		$this->determineCacheability();
98
		return !$this->_dataCached;
99
	}
100
 
101
	private function determineCacheability()
102
	{
103
		if(!$this->_cacheChecked)
104
		{
105
			$this->_cacheChecked=true;
106
			if($this->_duration>0 && ($this->_cachePostBack || !$this->getPage()->getIsPostBack()))
107
			{
108
				if($this->_cacheModuleID!=='')
109
				{
110
					$this->_cache=$this->getApplication()->getModule($this->_cacheModuleID);
111
					if(!($this->_cache instanceof ICache))
112
						throw new TConfigurationException('outputcache_cachemoduleid_invalid',$this->_cacheModuleID);
113
				}
114
				else
115
					$this->_cache=$this->getApplication()->getCache();
116
				if($this->_cache!==null)
117
				{
118
					$this->_cacheAvailable=true;
119
					$data=$this->_cache->get($this->getCacheKey());
120
					if(is_array($data))
121
					{
122
						$param=new TOutputCacheCheckDependencyEventParameter;
123
						$param->setCacheTime(isset($data[3])?$data[3]:0);
124
						$this->onCheckDependency($param);
125
						$this->_dataCached=$param->getIsValid();
126
					}
127
					else
128
						$this->_dataCached=false;
129
					if($this->_dataCached)
130
						list($this->_contents,$this->_state,$this->_actions,$this->_cacheTime)=$data;
131
				}
132
			}
133
		}
134
	}
135
 
136
	/**
137
	 * Performs the Init step for the control and all its child controls.
138
	 * This method overrides the parent implementation by setting up
139
	 * the stack of the output cache in the page.
140
	 * Only framework developers should use this method.
141
	 * @param TControl the naming container control
142
	 */
143
	protected function initRecursive($namingContainer=null)
144
	{
145
		if($this->_cacheAvailable && !$this->_dataCached)
146
		{
147
			$stack=$this->getPage()->getCachingStack();
148
			$stack->push($this);
149
			parent::initRecursive($namingContainer);
150
			$stack->pop();
151
		}
152
		else
153
			parent::initRecursive($namingContainer);
154
	}
155
 
156
	/**
157
	 * Performs the Load step for the control and all its child controls.
158
	 * This method overrides the parent implementation by setting up
159
	 * the stack of the output cache in the page. If the data is restored
160
	 * from cache, it also recovers the actions associated with the cached data.
161
	 * Only framework developers should use this method.
162
	 * @param TControl the naming container control
163
	 */
164
	protected function loadRecursive()
165
	{
166
		if($this->_cacheAvailable && !$this->_dataCached)
167
		{
168
			$stack=$this->getPage()->getCachingStack();
169
			$stack->push($this);
170
			parent::loadRecursive();
171
			$stack->pop();
172
		}
173
		else
174
		{
175
			if($this->_dataCached)
176
				$this->performActions();
177
			parent::loadRecursive();
178
		}
179
	}
180
 
181
	private function performActions()
182
	{
183
		$page=$this->getPage();
184
		$cs=$page->getClientScript();
185
		foreach($this->_actions as $action)
186
		{
187
			if($action[0]==='Page.ClientScript')
188
				call_user_func_array(array($cs,$action[1]),$action[2]);
189
			else if($action[0]==='Page')
190
				call_user_func_array(array($page,$action[1]),$action[2]);
191
			else
192
				call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]);
193
		}
194
	}
195
 
196
	/**
197
	 * Performs the PreRender step for the control and all its child controls.
198
	 * This method overrides the parent implementation by setting up
199
	 * the stack of the output cache in the page.
200
	 * Only framework developers should use this method.
201
	 * @param TControl the naming container control
202
	 */
203
	protected function preRenderRecursive()
204
	{
205
		if($this->_cacheAvailable && !$this->_dataCached)
206
		{
207
			$stack=$this->getPage()->getCachingStack();
208
			$stack->push($this);
209
			parent::preRenderRecursive();
210
			$stack->pop();
211
		}
212
		else
213
			parent::preRenderRecursive();
214
	}
215
 
216
	/**
217
	 * Loads state (viewstate and controlstate) into a control and its children.
218
	 * This method overrides the parent implementation by loading
219
	 * cached state if available.
220
	 * This method should only be used by framework developers.
221
	 * @param array the collection of the state
222
	 * @param boolean whether the viewstate should be loaded
223
	 */
224
	protected function loadStateRecursive(&$state,$needViewState=true)
225
	{
226
		$st=unserialize($state);
227
		parent::loadStateRecursive($st,$needViewState);
228
	}
229
 
230
	/**
231
	 * Saves all control state (viewstate and controlstate) as a collection.
232
	 * This method overrides the parent implementation by saving state
233
	 * into cache if needed.
234
	 * This method should only be used by framework developers.
235
	 * @param boolean whether the viewstate should be saved
236
	 * @return array the collection of the control state (including its children's state).
237
	 */
238
	protected function &saveStateRecursive($needViewState=true)
239
	{
240
		if($this->_dataCached)
241
			return $this->_state;
242
		else
243
		{
244
			$st=parent::saveStateRecursive($needViewState);
245
			// serialization is needed to avoid undefined classes when loading state
246
			$this->_state=serialize($st);
247
			return $this->_state;
248
		}
249
	}
250
 
251
	/**
252
	 * Registers an action associated with the content being cached.
253
	 * The registered action will be replayed if the content stored
254
	 * in the cache is served to end-users.
255
	 * @param string context of the action method. This is a property-path
256
	 * referring to the context object (e.g. Page, Page.ClientScript)
257
	 * @param string method name of the context object
258
	 * @param array list of parameters to be passed to the action method
259
	 */
260
	public function registerAction($context,$funcName,$funcParams)
261
	{
262
		$this->_actions[]=array($context,$funcName,$funcParams);
263
	}
264
 
265
	private function getCacheKey()
266
	{
267
		if($this->_cacheKey===null)
268
			$this->_cacheKey=$this->calculateCacheKey();
269
		return $this->_cacheKey;
270
	}
271
 
272
	/**
273
	 * Calculates the cache key.
274
	 * The key is calculated based on the unique ID of this control
275
	 * and the request parameters specified via {@link setVaryByParam VaryByParam}.
276
	 * If {@link getVaryBySession VaryBySession} is true, the session ID
277
	 * will also participate in the key calculation.
278
	 * This method may be overriden to support other variations in
279
	 * the calculated cache key.
280
	 * @return string cache key
281
	 */
282
	protected function calculateCacheKey()
283
	{
284
		$key=$this->getBaseCacheKey();
285
		if($this->_varyBySession)
286
			$key.=$this->getSession()->getSessionID();
287
		if($this->_varyByParam!=='')
288
		{
289
			$params=array();
290
			$request=$this->getRequest();
291
			foreach(explode(',',$this->_varyByParam) as $name)
292
			{
293
				$name=trim($name);
294
				$params[$name]=$request->itemAt($name);
295
			}
296
			$key.=serialize($params);
297
		}
298
		$param=new TOutputCacheCalculateKeyEventParameter;
299
		$this->onCalculateKey($param);
300
		$key.=$param->getCacheKey();
301
		return $key;
302
	}
303
 
304
	/**
305
	 * @return string basic cache key without variations
306
	 */
307
	protected function getBaseCacheKey()
308
	{
309
		return self::CACHE_ID_PREFIX.$this->_keyPrefix.$this->getPage()->getPagePath().$this->getUniqueID();
310
	}
311
 
312
	/**
313
	 * @return string the ID of the cache module. Defaults to '', meaning the primary cache module is used.
314
	 */
315
	public function getCacheModuleID()
316
	{
317
		return $this->_cacheModuleID;
318
	}
319
 
320
	/**
321
	 * @param string the ID of the cache module. If empty, the primary cache module will be used.
322
	 */
323
	public function setCacheModuleID($value)
324
	{
325
		$this->_cacheModuleID=$value;
326
	}
327
 
328
	/**
329
	 * Sets the prefix of the cache key.
330
	 * This method is used internally by {@link TTemplate}.
331
	 * @param string key prefix
332
	 */
333
	public function setCacheKeyPrefix($value)
334
	{
335
		$this->_keyPrefix=$value;
336
	}
337
 
338
	/**
339
	 * @return integer the timestamp of the cached content. This is only valid if the content is being cached.
340
	 * @since 3.1.1
341
	 */
342
	public function getCacheTime()
343
	{
344
		return $this->_cacheTime;
345
	}
346
 
347
	/**
348
	 * Returns the dependency of the data to be cached.
349
	 * The default implementation simply returns null, meaning no specific dependency.
350
	 * This method may be overriden to associate the data to be cached
351
	 * with additional dependencies.
352
	 * @return ICacheDependency
353
	 */
354
	protected function getCacheDependency()
355
	{
356
		return null;
357
	}
358
 
359
	/**
360
	 * @return boolean whether content enclosed is cached or not
361
	 */
362
	public function getContentCached()
363
	{
364
		return $this->_dataCached;
365
	}
366
 
367
	/**
368
	 * @return integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
369
	 * Note, if cache dependency changes or cache space is limited,
370
	 * the data may be purged out of cache earlier.
371
	 */
372
	public function getDuration()
373
	{
374
		return $this->_duration;
375
	}
376
 
377
	/**
378
	 * @param integer number of seconds that the data can remain in cache. If 0, it means data is not cached.
379
	 * @throws TInvalidDataValueException if the value is smaller than 0.
380
	 */
381
	public function setDuration($value)
382
	{
383
		if(($value=TPropertyValue::ensureInteger($value))<0)
384
			throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this));
385
		$this->_duration=$value;
386
	}
387
 
388
	/**
389
	 * @return string a semicolon-separated list of strings used to vary the output cache. Defaults to ''.
390
	 */
391
	public function getVaryByParam()
392
	{
393
		return $this->_varyByParam;
394
	}
395
 
396
	/**
397
	 * Sets the names of the request parameters that should be used in calculating the cache key.
398
	 * The names should be concatenated by semicolons.
399
	 * By setting this value, the output cache will use different cached data
400
	 * for each different set of request parameter values.
401
	 * @return string a semicolon-separated list of strings used to vary the output cache.
402
	 */
403
	public function setVaryByParam($value)
404
	{
405
		$this->_varyByParam=trim($value);
406
	}
407
 
408
	/**
409
	 * @return boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
410
	 */
411
	public function getVaryBySession()
412
	{
413
		return $this->_varyBySession;
414
	}
415
 
416
	/**
417
	 * @param boolean whether the content being cached should be differentiated according to user sessions.
418
	 */
419
	public function setVaryBySession($value)
420
	{
421
		$this->_varyBySession=TPropertyValue::ensureBoolean($value);
422
	}
423
 
424
	/**
425
	 * @return boolean whether cached output will be used on postback requests. Defaults to false.
426
	 */
427
	public function getCachingPostBack()
428
	{
429
		return $this->_cachePostBack;
430
	}
431
 
432
	/**
433
	 * Sets a value indicating whether cached output will be used on postback requests.
434
	 * By default, this is disabled. Be very cautious when enabling it.
435
	 * If the cached content including interactive user controls such as
436
	 * TTextBox, TDropDownList, your page may fail to render on postbacks.
437
	 * @param boolean whether cached output will be used on postback requests.
438
	 */
439
	public function setCachingPostBack($value)
440
	{
441
		$this->_cachePostBack=TPropertyValue::ensureBoolean($value);
442
	}
443
 
444
	/**
445
	 * This event is raised when the output cache is checking cache dependency.
446
	 * An event handler may be written to check customized dependency conditions.
447
	 * The checking result should be saved by setting {@link TOutputCacheCheckDependencyEventParameter::setIsValid IsValid}
448
	 * property of the event parameter (which defaults to true).
449
	 * @param TOutputCacheCheckDependencyEventParameter event parameter
450
	 */
451
	public function onCheckDependency($param)
452
	{
453
		$this->raiseEvent('OnCheckDependency',$this,$param);
454
	}
455
 
456
	/**
457
	 * This event is raised when the output cache is calculating cache key.
458
	 * By varying cache keys, one can obtain different versions of cached content.
459
	 * An event handler may be written to add variety of the key calculation.
460
	 * The value set in {@link TOutputCacheCalculateKeyEventParameter::setCacheKey CacheKey} of
461
	 * this event parameter will be appended to the default key calculation scheme.
462
	 * @param TOutputCacheCalculateKeyEventParameter event parameter
463
	 */
464
	public function onCalculateKey($param)
465
	{
466
		$this->raiseEvent('OnCalculateKey',$this,$param);
467
	}
468
 
469
	/**
470
	 * Renders the output cache control.
471
	 * This method overrides the parent implementation by capturing the output
472
	 * from its child controls and saving it into cache, if output cache is needed.
473
	 * @param THtmlWriter
474
	 */
475
	public function render($writer)
476
	{
477
		if($this->_dataCached)
478
			$writer->write($this->_contents);
479
		else if($this->_cacheAvailable)
480
		{
481
			$textWriter=new TTextWriter;
482
 
483
			$stack=$this->getPage()->getCachingStack();
484
			$stack->push($this);
485
			parent::render(new THtmlWriter($textWriter));
486
			$stack->pop();
487
 
488
			$content=$textWriter->flush();
489
			$data=array($content,$this->_state,$this->_actions,time());
490
			$this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency());
491
			$writer->write($content);
492
		}
493
		else
494
			parent::render($writer);
495
	}
496
}
497
 
498
/**
499
 * TOutputCacheCheckDependencyEventParameter class
500
 *
501
 * TOutputCacheCheckDependencyEventParameter encapsulates the parameter data for
502
 * <b>OnCheckDependency</b> event of {@link TOutputCache} control.
503
 *
504
 * @author Qiang Xue <qiang.xue@gmail.com>
505
 * @version $Id: TOutputCache.php 2541 2008-10-21 15:05:13Z qiang.xue $
506
 * @package System.Web.UI.WebControls
507
 * @since 3.0
508
 */
509
class TOutputCacheCheckDependencyEventParameter extends TEventParameter
510
{
511
	private $_isValid=true;
512
	private $_cacheTime=0;
513
 
514
	/**
515
	 * @return boolean whether the dependency remains valid. Defaults to true.
516
	 */
517
	public function getIsValid()
518
	{
519
		return $this->_isValid;
520
	}
521
 
522
	/**
523
	 * @param boolean whether the dependency remains valid
524
	 */
525
	public function setIsValid($value)
526
	{
527
		$this->_isValid=TPropertyValue::ensureBoolean($value);
528
	}
529
 
530
	/**
531
	 * @return integer the timestamp of the cached result. You may use this to help determine any dependency is changed.
532
	 * @since 3.1.1
533
	 */
534
	public function getCacheTime()
535
	{
536
		return $this->_cacheTime;
537
	}
538
 
539
	/**
540
	 * @param integer the timestamp of the cached result. This is used internally.
541
	 * @since 3.1.1
542
	 */
543
	public function setCacheTime($value)
544
	{
545
		$this->_cacheTime=TPropertyValue::ensureInteger($value);
546
	}
547
}
548
 
549
 
550
/**
551
 * TOutputCacheCalculateKeyEventParameter class
552
 *
553
 * TOutputCacheCalculateKeyEventParameter encapsulates the parameter data for
554
 * <b>OnCalculateKey</b> event of {@link TOutputCache} control.
555
 *
556
 * @author Qiang Xue <qiang.xue@gmail.com>
557
 * @version $Id: TOutputCache.php 2541 2008-10-21 15:05:13Z qiang.xue $
558
 * @package System.Web.UI.WebControls
559
 * @since 3.0
560
 */
561
class TOutputCacheCalculateKeyEventParameter extends TEventParameter
562
{
563
	/**
564
	 * @var string cache key to be appended to the default calculation scheme.
565
	 */
566
	private $_cacheKey='';
567
 
568
	/**
569
	 * @return string cache key to be appended to the default calculation scheme.
570
	 */
571
	public function getCacheKey()
572
	{
573
		return $this->_cacheKey;
574
	}
575
 
576
	/**
577
	 * @param string cache key to be appended to the default calculation scheme
578
	 */
579
	public function setCacheKey($value)
580
	{
581
		$this->_cacheKey=TPropertyValue::ensureString($value);
582
	}
583
}
584