diff options
author | xue <> | 2006-04-09 01:50:08 +0000 |
---|---|---|
committer | xue <> | 2006-04-09 01:50:08 +0000 |
commit | d8122a0f98137822b0ea20f7602d105fcb2fe962 (patch) | |
tree | 7cd4b748842c1bf23a5da770d082f84267d4de24 | |
parent | c370ddb843c418f550879c2edc0aac0fe90a1b2d (diff) |
Fixed a few bugs in TOutputCache. Documentation for TOutputCache is completed.
-rw-r--r-- | framework/Exceptions/messages.txt | 6 | ||||
-rw-r--r-- | framework/Web/UI/TClientScriptManager.php | 24 | ||||
-rw-r--r-- | framework/Web/UI/TControl.php | 16 | ||||
-rw-r--r-- | framework/Web/UI/TPage.php | 4 | ||||
-rw-r--r-- | framework/Web/UI/TTemplateManager.php | 10 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TOutputCache.php | 308 |
6 files changed, 289 insertions, 79 deletions
diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index 7cf25c75..237145df 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -165,7 +165,7 @@ templatecontrol_placeholder_inexistent = TContent '{0}' does not have a matching page_form_duplicated = A page can contain at most one TForm. Use regular HTML form tags for the rest forms.
page_isvalid_unknown = TPage.IsValid has not been evaluated yet.
page_postbackcontrol_invalid = Unable to determine postback control '{0}'.
-page_control_outofform = Control '{0}' must be enclosed within TForm.
+page_control_outofform = {0} '{1}' must be enclosed within TForm.
page_head_duplicated = A page can contain at most one THead.
page_statepersister_invalid = Page state persister must implement IPageStatePersister interface.
@@ -280,4 +280,6 @@ parametermodule_parameterfile_unchangeable = TParameterModule.ParameterFile is n parametermodule_parameterfile_invalid = TParameterModule.ParameterFile '{0}' is invalid. Make sure it is in namespace format and the file extension is '.xml'.
parametermodule_parameterid_required = Parameter element must have 'id' attribute.
-datagridcolumn_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2}
\ No newline at end of file +datagridcolumn_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2}
+
+outputcache_duration_invalid = {0}.Duration must be an integer no less than 0.
\ No newline at end of file diff --git a/framework/Web/UI/TClientScriptManager.php b/framework/Web/UI/TClientScriptManager.php index 34f17bf3..cb945fbf 100644 --- a/framework/Web/UI/TClientScriptManager.php +++ b/framework/Web/UI/TClientScriptManager.php @@ -120,7 +120,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerPradoScript',$params); + $item->registerAction('Page.ClientScript','registerPradoScript',$params); } private function registerPradoScriptInternal($name) @@ -183,7 +183,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerPostBackControl',$params); + $item->registerAction('Page.ClientScript','registerPostBackControl',$params); } /** @@ -202,7 +202,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerDefaultButton',$params); + $item->registerAction('Page.ClientScript','registerDefaultButton',$params); } /** @@ -216,7 +216,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerFocusControl',$params); + $item->registerAction('Page.ClientScript','registerFocusControl',$params); } /** @@ -243,7 +243,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerStyleSheetFile',$params); + $item->registerAction('Page.ClientScript','registerStyleSheetFile',$params); } /** @@ -257,7 +257,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerStyleSheet',$params); + $item->registerAction('Page.ClientScript','registerStyleSheet',$params); } /** @@ -271,7 +271,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerHeadScriptFile',$params); + $item->registerAction('Page.ClientScript','registerHeadScriptFile',$params); } /** @@ -285,7 +285,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerHeadScript',$params); + $item->registerAction('Page.ClientScript','registerHeadScript',$params); } /** @@ -299,7 +299,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerScriptFile',$params); + $item->registerAction('Page.ClientScript','registerScriptFile',$params); } /** @@ -313,7 +313,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerBeginScript',$params); + $item->registerAction('Page.ClientScript','registerBeginScript',$params); } /** @@ -327,7 +327,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerEndScript',$params); + $item->registerAction('Page.ClientScript','registerEndScript',$params); } /** @@ -342,7 +342,7 @@ class TClientScriptManager extends TApplicationComponent $params=func_get_args(); foreach($this->_page->getCachingStack() as $item) - $item->registerAction('registerHiddenField',$params); + $item->registerAction('Page.ClientScript','registerHiddenField',$params); } /** diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index 3c3c7060..f5f77513 100644 --- a/framework/Web/UI/TControl.php +++ b/framework/Web/UI/TControl.php @@ -1446,7 +1446,8 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable /**
* Loads state (viewstate and controlstate) into a control and its children.
- * @param TMap the collection of the state
+ * This method should only be used by framework developers.
+ * @param array the collection of the state
* @param boolean whether the viewstate should be loaded
*/
protected function loadStateRecursive(&$state,$needViewState=true)
@@ -1502,6 +1503,7 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable /**
* Saves all control state (viewstate and controlstate) as a collection.
+ * This method should only be used by framework developers.
* @param boolean whether the viewstate should be saved
* @return array the collection of the control state (including its children's state).
*/
@@ -1718,6 +1720,18 @@ class TEmptyControlCollection extends TControlCollection {
parent::__construct($owner,true);
}
+
+ /**
+ * Inserts an item at the specified position.
+ * This overrides the parent implementation by ignoring new addition.
+ * @param integer the speicified position.
+ * @param mixed new item
+ */
+ public function insertAt($index,$item)
+ {
+ if(!is_string($item)) // string is possible if property tag is used. we simply ignore it in this case
+ parent::insertAt($index,$item); // this will generate an exception in parent implementation
+ }
}
/**
diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index 7f4c490c..6a4083b0 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -609,7 +609,7 @@ class TPage extends TTemplateControl $this->_controlsRegisteredForPostData[$id]=true;
$params=func_get_args();
foreach($this->getCachingStack() as $item)
- $item->registerAction('registerRequiresPostData',$id);
+ $item->registerAction('Page','registerRequiresPostData',$id);
}
/**
@@ -727,7 +727,7 @@ class TPage extends TTemplateControl public function ensureRenderInForm($control)
{
if(!$this->_inFormRender)
- throw new TConfigurationException('page_control_outofform',$control->getUniqueID());
+ throw new TConfigurationException('page_control_outofform',get_class($control),$control->getUniqueID());
}
/**
diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index fa4cbcbf..73a336c8 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -262,8 +262,11 @@ class TTemplate extends TApplicationComponent implements ITemplate $controls=array();
foreach($this->_tpl as $key=>$object)
{
- $parent=isset($controls[$object[0]])?$controls[$object[0]]:$tplControl;
- if(!$parent->getAllowChildControls())
+ if($object[0]===-1)
+ $parent=$tplControl;
+ else if(isset($controls[$object[0]]))
+ $parent=$controls[$object[0]];
+ else
continue;
if(isset($object[2])) // component
{
@@ -271,7 +274,6 @@ class TTemplate extends TApplicationComponent implements ITemplate $properties=&$object[2];
if($component instanceof TControl)
{
- $controls[$key]=$component;
$component->setTemplateControl($tplControl);
if(isset($properties['id']))
{
@@ -292,6 +294,8 @@ class TTemplate extends TApplicationComponent implements ITemplate foreach($properties as $name=>$value)
$this->configureControl($component,$name,$value);
$component->createdOnTemplate($parent);
+ if($component->getAllowChildControls())
+ $controls[$key]=$component;
}
else if($component instanceof TComponent)
{
diff --git a/framework/Web/UI/WebControls/TOutputCache.php b/framework/Web/UI/WebControls/TOutputCache.php index 9708c033..08f60f90 100644 --- a/framework/Web/UI/WebControls/TOutputCache.php +++ b/framework/Web/UI/WebControls/TOutputCache.php @@ -1,21 +1,109 @@ <?php
+/**
+ * TOutputCache class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2006 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ */
+/**
+ * TOutputCache class.
+ *
+ * TOutputCache enables caching a portion of a Web page, also known as
+ * partial caching. The content being cached can be either static or
+ * dynamic.
+ *
+ * To use TOutputCache, simply enclose the content to be cached
+ * within the TOutputCache component tag on a template, e.g.,
+ * <code>
+ * <com:TOutputCache>
+ * content to be cached
+ * </com:TOutputCache>
+ * </code>
+ * where content to be cached can be static text and/or component tags.
+ *
+ * The validity of the cached content is determined based on two factors:
+ * the {@link setDuration Duration} and the {@link getCacheDependency CacheDependency}.
+ * The former specifies the number of seconds that the data can remain
+ * valid in cache (defaults to 60s), while the latter specifies a dependency
+ * that the data depends on. If the dependency changes, the cached content
+ * is invalidated. By default, TOutputCache doesn't specify a dependency.
+ * Derived classes may override {@link getCacheDependency()} method to
+ * enforce a dependency (such as system state change, etc.)
+ *
+ * The content fetched from cache may be variated with respect to
+ * some parameters. It supports variation with respect to request parameters,
+ * which is specified by {@link setVaryByParam VaryByParam} property.
+ * If a specified request parameter is different, a different version of
+ * cached content is used. This is extremely useful if a page's content
+ * may be variated according to some GET parameters. To variate the cached
+ * content by other factors, override {@link calculateCacheKey()} method.
+ *
+ * Output caches can be nested. An outer cache takes precedence over an
+ * inner cache. This means, if the content cached by the inner cache expires
+ * or is invalidated, while that by the outer cache not, the outer cached
+ * content will be used.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
class TOutputCache extends TControl implements INamingContainer
{
const CACHE_ID_PREFIX='prado:outputcache';
- private $_useCache=false;
- private $_cacheReady=false;
+ private $_dataCached=false;
+ private $_cacheAvailable=false;
private $_cacheChecked=false;
- private $_expiry=60;
+ private $_cacheKey=null;
+ private $_duration=60;
private $_cache=null;
private $_contents;
private $_state;
- private $_enableCaching=true;
private $_actions=array();
+ private $_varyByParam='';
+ /**
+ * Returns a value indicating whether body contents are allowed for this control.
+ * This method overrides the parent implementation by checking if cached
+ * content is available or not. If yes, it returns false, otherwise true.
+ * @param boolean whether body contents are allowed for this control.
+ */
+ public function getAllowChildControls()
+ {
+ $this->determineCacheability();
+ return !$this->_dataCached;
+ }
+
+ private function determineCacheability()
+ {
+ if(!$this->_cacheChecked)
+ {
+ $this->_cacheChecked=true;
+ if(!$this->getPage()->getIsPostBack() && ($this->_cache=$this->getApplication()->getCache())!==null && $this->_duration>0)
+ {
+ $this->_cacheAvailable=true;
+ $data=$this->_cache->get($this->getCacheKey());
+ if(($this->_dataCached=($data!==false)))
+ list($this->_contents,$this->_state,$this->_actions)=$data;
+ }
+ }
+ }
+
+ /**
+ * Performs the Init step for the control and all its child controls.
+ * This method overrides the parent implementation by setting up
+ * the stack of the output cache in the page.
+ * Only framework developers should use this method.
+ * @param TControl the naming container control
+ */
protected function initRecursive($namingContainer=null)
{
- if($this->_cacheReady && !$this->_useCache)
+ if($this->_cacheAvailable && !$this->_dataCached)
{
$stack=$this->getPage()->getCachingStack();
$stack->push($this);
@@ -26,9 +114,17 @@ class TOutputCache extends TControl implements INamingContainer parent::initRecursive($namingContainer);
}
+ /**
+ * Performs the Load step for the control and all its child controls.
+ * This method overrides the parent implementation by setting up
+ * the stack of the output cache in the page. If the data is restored
+ * from cache, it also recovers the actions associated with the cached data.
+ * Only framework developers should use this method.
+ * @param TControl the naming container control
+ */
protected function loadRecursive()
{
- if($this->_cacheReady && !$this->_useCache)
+ if($this->_cacheAvailable && !$this->_dataCached)
{
$stack=$this->getPage()->getCachingStack();
$stack->push($this);
@@ -37,24 +133,37 @@ class TOutputCache extends TControl implements INamingContainer }
else
{
- if($this->_useCache)
- {
- $cs=$this->getPage()->getClientScript();
- foreach($this->_actions as $action)
- {
- if($action[0]==='registerRequiresPostData')
- $this->getPage()->registerRequiresPostData($action[1]);
- else
- call_user_func_array(array($cs,$action[0]),$action[1]);
- }
- }
+ if($this->_dataCached)
+ $this->performActions();
parent::loadRecursive();
}
}
+ private function performActions()
+ {
+ $page=$this->getPage();
+ $cs=$page->getClientScript();
+ foreach($this->_actions as $action)
+ {
+ if($action[0]==='Page.ClientScript')
+ call_user_func_array(array($cs,$action[1]),$action[2]);
+ else if($action[0]==='Page')
+ call_user_func_array(array($page,$action[1]),$action[2]);
+ else
+ call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]);
+ }
+ }
+
+ /**
+ * Performs the PreRender step for the control and all its child controls.
+ * This method overrides the parent implementation by setting up
+ * the stack of the output cache in the page.
+ * Only framework developers should use this method.
+ * @param TControl the naming container control
+ */
protected function preRenderRecursive()
{
- if($this->_cacheReady && !$this->_useCache)
+ if($this->_cacheAvailable && !$this->_dataCached)
{
$stack=$this->getPage()->getCachingStack();
$stack->push($this);
@@ -65,80 +174,161 @@ class TOutputCache extends TControl implements INamingContainer parent::preRenderRecursive();
}
- public function registerAction($funcName,$funcParams)
+ /**
+ * Loads state (viewstate and controlstate) into a control and its children.
+ * This method overrides the parent implementation by loading
+ * cached state if available.
+ * This method should only be used by framework developers.
+ * @param array the collection of the state
+ * @param boolean whether the viewstate should be loaded
+ */
+ protected function loadStateRecursive(&$state,$needViewState=true)
{
- $this->_actions[]=array($funcName,$funcParams);
+ if($this->_dataCached)
+ parent::loadStateRecursive($this->_state,$needViewState);
+ else
+ parent::loadStateRecursive($state,$needViewState);
}
- public function getAllowChildControls()
+ /**
+ * Saves all control state (viewstate and controlstate) as a collection.
+ * This method overrides the parent implementation by saving state
+ * into cache if needed.
+ * This method should only be used by framework developers.
+ * @param boolean whether the viewstate should be saved
+ * @return array the collection of the control state (including its children's state).
+ */
+ protected function &saveStateRecursive($needViewState=true)
{
- if(!$this->_cacheChecked)
+ if($this->_dataCached)
+ return $this->_state;
+ else if($this->_cacheAvailable)
{
- $this->_cacheChecked=true;
- if(!$this->getPage()->getIsPostBack() && ($this->_cache=$this->getApplication()->getCache())!==null && $this->getEnableCaching())
- {
- $this->_cacheReady=true;
- $data=$this->_cache->get($this->getCacheKey());
- if(($this->_useCache=($data!==false)))
- list($this->_contents,$this->_state,$this->_actions)=$data;
- }
+ $this->_state=parent::saveStateRecursive($needViewState);
+ return $this->_state;
}
- return !$this->_useCache;
+ else
+ return parent::saveStateRecursive($needViewState);
}
- public function getEnableCaching()
+ /**
+ * Registers an action associated with the content being cached.
+ * The registered action will be replayed if the content stored
+ * in the cache is served to end-users.
+ * @param string context of the action method. This is a property-path
+ * referring to the context object (e.g. Page, Page.ClientScript)
+ * @param string method name of the context object
+ * @param array list of parameters to be passed to the action method
+ */
+ public function registerAction($context,$funcName,$funcParams)
{
- return $this->_enableCaching;
+ $this->_actions[]=array($context,$funcName,$funcParams);
}
- public function setEnableCaching($value)
+ private function getCacheKey()
{
- $this->_enableCaching=TPropertyValue::ensureBoolean($value);
+ if($this->_cacheKey===null)
+ $this->_cacheKey=$this->calculateCacheKey();
+ return $this->_cacheKey;
}
- protected function loadStateRecursive(&$state,$needViewState=true)
+ /**
+ * Calculates the cache key.
+ * The key is calculated based on the unique ID of this control
+ * and the request parameters specified via {@link setVaryByParam VaryByParam}.
+ * This method may be overriden to support other variations in
+ * the calculated cache key.
+ * @return string cache key
+ */
+ protected function calculateCacheKey()
{
- if($this->_useCache)
- parent::loadStateRecursive($this->_state,$needViewState);
+ if($this->_varyByParam!=='')
+ {
+ $params=array();
+ $request=$this->getRequest();
+ foreach(explode(',',$this->_varyByParam) as $name)
+ {
+ $name=trim($name);
+ $params[$name]=$request->itemAt($name);
+ }
+ return self::CACHE_ID_PREFIX.$this->getUniqueID().serialize($params);
+ }
else
- parent::loadStateRecursive($state,$needViewState);
+ return self::CACHE_ID_PREFIX.$this->getUniqueID();
}
- protected function &saveStateRecursive($needViewState=true)
+ /**
+ * Returns the dependency of the data to be cached.
+ * The default implementation simply returns null, meaning no specific dependency.
+ * This method may be overriden to associate the data to be cached
+ * with additional dependencies.
+ * @return ICacheDependency
+ */
+ protected function getCacheDependency()
{
- if($this->_useCache)
- return $this->_state;
- else if($this->_cacheReady)
- {
- $this->_state=parent::saveStateRecursive($needViewState);
- return $this->_state;
- }
- else
- return parent::saveStateRecursive($needViewState);
+ return null;
}
- protected function getCacheKey()
+ /**
+ * @return boolean whether content enclosed is cached or not
+ */
+ public function getContentCached()
{
- return self::CACHE_ID_PREFIX.$this->getUniqueID();
+ return $this->_dataCached;
}
- public function getExpiry()
+ /**
+ * @return integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
+ * Note, if cache dependency changes or cache space is limited,
+ * the data may be purged out of cache earlier.
+ */
+ public function getDuration()
{
- return $this->_expiry;
+ return $this->_duration;
}
- public function setExpiry($value)
+ /**
+ * @param integer number of seconds that the data can remain in cache. If 0, it means data is not cached.
+ * @throws TInvalidDataValueException if the value is smaller than 0.
+ */
+ public function setDuration($value)
{
if(($value=TPropertyValue::ensureInteger($value))<0)
- throw new TInvalidDataValueException('outputcache_expiry_invalid');
- $this->_expiry=$value;
+ throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this));
+ $this->_duration=$value;
+ }
+
+ /**
+ * @return string a semicolon-separated list of strings used to vary the output cache. Defaults to ''.
+ */
+ public function getVaryByParam()
+ {
+ return $this->_varyByParam;
+ }
+
+ /**
+ * Sets the names of the request parameters that should be used in calculating the cache key.
+ * The names should be concatenated by semicolons.
+ * By setting this value, the output cache will use different cached data
+ * for each different set of request parameter values.
+ * @return string a semicolon-separated list of strings used to vary the output cache.
+ */
+ public function setVaryByParam($value)
+ {
+ $this->_varyByParam=trim($value);
}
+ /**
+ * Renders the output cache control.
+ * This method overrides the parent implementation by capturing the output
+ * from its child controls and saving it into cache, if output cache is needed.
+ * @param THtmlWriter
+ */
public function render($writer)
{
- if($this->_useCache)
+ if($this->_dataCached)
$writer->write($this->_contents);
- else if($this->_cacheReady)
+ else if($this->_cacheAvailable)
{
$textWriter=new TTextWriter;
@@ -149,7 +339,7 @@ class TOutputCache extends TControl implements INamingContainer $content=$textWriter->flush();
$data=array($content,$this->_state,$this->_actions);
- $this->_cache->set($this->getCacheKey(),$data,$this->getExpiry());
+ $this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency());
$writer->write($content);
}
else
|