Commit de5c304f by Qiang Xue

finished fragment caching.

parent feebb130
...@@ -298,7 +298,7 @@ class YiiBase ...@@ -298,7 +298,7 @@ class YiiBase
} }
} }
if (isset($classFile, $alias)) { if (isset($classFile, $alias) && is_file($classFile)) {
if (!YII_DEBUG || basename(realpath($classFile)) === basename($alias) . '.php') { if (!YII_DEBUG || basename(realpath($classFile)) === basename($alias) . '.php') {
include($classFile); include($classFile);
return true; return true;
......
...@@ -58,7 +58,7 @@ class Component extends \yii\base\Object ...@@ -58,7 +58,7 @@ class Component extends \yii\base\Object
} }
} }
} }
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
} }
/** /**
...@@ -105,9 +105,9 @@ class Component extends \yii\base\Object ...@@ -105,9 +105,9 @@ class Component extends \yii\base\Object
} }
} }
if (method_exists($this, 'get' . $name)) { if (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '.' . $name); throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
} else { } else {
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
} }
} }
......
...@@ -65,7 +65,7 @@ class Object ...@@ -65,7 +65,7 @@ class Object
if (method_exists($this, $getter)) { if (method_exists($this, $getter)) {
return $this->$getter(); return $this->$getter();
} else { } else {
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
} }
} }
...@@ -86,9 +86,9 @@ class Object ...@@ -86,9 +86,9 @@ class Object
if (method_exists($this, $setter)) { if (method_exists($this, $setter)) {
$this->$setter($value); $this->$setter($value);
} elseif (method_exists($this, 'get' . $name)) { } elseif (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '.' . $name); throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
} else { } else {
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '.' . $name); throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
} }
} }
...@@ -129,7 +129,7 @@ class Object ...@@ -129,7 +129,7 @@ class Object
if (method_exists($this, $setter)) { if (method_exists($this, $setter)) {
$this->$setter(null); $this->$setter(null);
} elseif (method_exists($this, 'get' . $name)) { } elseif (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '.' . $name); throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '::' . $name);
} }
} }
......
...@@ -45,11 +45,21 @@ class View extends Component ...@@ -45,11 +45,21 @@ class View extends Component
* through this property. * through this property.
*/ */
public $clips; public $clips;
/** /**
* @var Widget[] the widgets that are currently not ended * @var Widget[] the widgets that are currently being rendered (not ended). This property
* is maintained by [[beginWidget()]] and [[endWidget()]] methods. Do not modify it directly.
*/
public $widgetStack = array();
/**
* @var array a list of currently active fragment cache widgets. This property
* is used internally to implement the content caching feature. Do not modify it.
*/
public $cacheStack = array();
/**
* @var array a list of placeholders for embedding dynamic contents. This property
* is used internally to implement the content caching feature. Do not modify it.
*/ */
private $_widgetStack = array(); public $dynamicPlaceholders = array();
/** /**
...@@ -156,20 +166,47 @@ class View extends Component ...@@ -156,20 +166,47 @@ class View extends Component
return ob_get_clean(); return ob_get_clean();
} }
/**
* Renders dynamic content returned by the given PHP statements.
* This method is mainly used together with content caching (fragment caching and page caching)
* when some portions of the content (called *dynamic content*) should not be cached.
* The dynamic content must be returned by some PHP statements.
* @param string $statements the PHP statements for generating the dynamic content.
* @return string the placeholder of the dynamic content, or the dynamic content if there is no
* active content cache currently.
*/
public function renderDynamic($statements) public function renderDynamic($statements)
{ {
if (!empty($this->cachingStack)) { if (!empty($this->cacheStack)) {
$n = count($this->_dynamicOutput); $n = count($this->dynamicPlaceholders);
$placeholder = "<![CDATA[YDP-$n]]>"; $placeholder = "<![CDATA[YDP-$n]]>";
foreach ($this->cachingStack as $cache) { $this->addDynamicPlaceholder($placeholder, $statements);
$cache->dynamicPlaceholders[$placeholder] = $statements;
}
return $placeholder; return $placeholder;
} else { } else {
return $this->evaluateDynamicContent($statements); return $this->evaluateDynamicContent($statements);
} }
} }
/**
* Adds a placeholder for dynamic content.
* This method is internally used.
* @param string $placeholder the placeholder name
* @param string $statements the PHP statements for generating the dynamic content
*/
public function addDynamicPlaceholder($placeholder, $statements)
{
foreach ($this->cacheStack as $cache) {
$cache->dynamicPlaceholders[$placeholder] = $statements;
}
$this->dynamicPlaceholders[$placeholder] = $statements;
}
/**
* Evaluates the given PHP statements.
* This method is mainly used internally to implement dynamic content feature.
* @param string $statements the PHP statements to be evaluated.
* @return mixed the return value of the PHP statements.
*/
public function evaluateDynamicContent($statements) public function evaluateDynamicContent($statements)
{ {
return eval($statements); return eval($statements);
...@@ -267,7 +304,7 @@ class View extends Component ...@@ -267,7 +304,7 @@ class View extends Component
public function beginWidget($class, $properties = array()) public function beginWidget($class, $properties = array())
{ {
$widget = $this->createWidget($class, $properties); $widget = $this->createWidget($class, $properties);
$this->_widgetStack[] = $widget; $this->widgetStack[] = $widget;
return $widget; return $widget;
} }
...@@ -281,7 +318,7 @@ class View extends Component ...@@ -281,7 +318,7 @@ class View extends Component
*/ */
public function endWidget() public function endWidget()
{ {
$widget = array_pop($this->_widgetStack); $widget = array_pop($this->widgetStack);
if ($widget instanceof Widget) { if ($widget instanceof Widget) {
$widget->run(); $widget->run();
return $widget; return $widget;
...@@ -363,8 +400,9 @@ class View extends Component ...@@ -363,8 +400,9 @@ class View extends Component
public function beginCache($id, $properties = array()) public function beginCache($id, $properties = array())
{ {
$properties['id'] = $id; $properties['id'] = $id;
$properties['view'] = $this;
/** @var $cache \yii\widgets\FragmentCache */ /** @var $cache \yii\widgets\FragmentCache */
$cache = $this->beginWidget('yii\widgets\OutputCache', $properties); $cache = $this->beginWidget('yii\widgets\FragmentCache', $properties);
if ($cache->getCachedContent() !== false) { if ($cache->getCachedContent() !== false) {
$this->endCache(); $this->endCache();
return false; return false;
......
...@@ -67,7 +67,8 @@ class FragmentCache extends Widget ...@@ -67,7 +67,8 @@ class FragmentCache extends Widget
*/ */
public $view; public $view;
/** /**
* @var array * @var array a list of placeholders for embedding dynamic contents. This property
* is used internally to implement the content caching feature. Do not modify it.
*/ */
public $dynamicPlaceholders; public $dynamicPlaceholders;
...@@ -83,8 +84,8 @@ class FragmentCache extends Widget ...@@ -83,8 +84,8 @@ class FragmentCache extends Widget
if ($this->view === null) { if ($this->view === null) {
$this->view = Yii::$app->getView(); $this->view = Yii::$app->getView();
} }
if ($this->getCachedContent() === false && $this->getCache() !== null) { if ($this->getCache() !== null && $this->getCachedContent() === false) {
array_push($this->view->cachingStack, $this); $this->view->cacheStack[] = $this;
ob_start(); ob_start();
ob_implicit_flush(false); ob_implicit_flush(false);
} }
...@@ -100,14 +101,18 @@ class FragmentCache extends Widget ...@@ -100,14 +101,18 @@ class FragmentCache extends Widget
{ {
if (($content = $this->getCachedContent()) !== false) { if (($content = $this->getCachedContent()) !== false) {
echo $content; echo $content;
} elseif (($cache = $this->getCache()) !== false) { } elseif (($cache = $this->getCache()) !== null) {
$content = ob_get_clean(); $content = ob_get_clean();
array_pop($this->view->cachingStack); array_pop($this->view->cacheStack);
if (is_array($this->dependency)) { if (is_array($this->dependency)) {
$this->dependency = Yii::createObject($this->dependency); $this->dependency = Yii::createObject($this->dependency);
} }
$data = array($content, $this->dynamicPlaceholders); $data = array($content, $this->dynamicPlaceholders);
$cache->set($this->calculateKey(), $data, $this->duration, $this->dependency); $cache->set($this->calculateKey(), $data, $this->duration, $this->dependency);
if ($this->view->cacheStack === array() && !empty($this->dynamicPlaceholders)) {
$content = $this->updateDynamicContent($content, $this->dynamicPlaceholders);
}
echo $content; echo $content;
} }
} }
...@@ -131,10 +136,13 @@ class FragmentCache extends Widget ...@@ -131,10 +136,13 @@ class FragmentCache extends Widget
if (is_array($data) && count($data) === 2) { if (is_array($data) && count($data) === 2) {
list ($content, $placeholders) = $data; list ($content, $placeholders) = $data;
if (is_array($placeholders) && count($placeholders) > 0) { if (is_array($placeholders) && count($placeholders) > 0) {
if ($this->view->cacheStack === array()) {
// outermost cache: replace placeholder with dynamic content
$content = $this->updateDynamicContent($content, $placeholders);
}
foreach ($placeholders as $name => $statements) { foreach ($placeholders as $name => $statements) {
$placeholders[$name] = $this->view->evaluateDynamicContent($statements); $this->view->addDynamicPlaceholder($name, $statements);
} }
$content = strtr($content, $placeholders);
} }
$this->_content = $content; $this->_content = $content;
} }
...@@ -143,6 +151,14 @@ class FragmentCache extends Widget ...@@ -143,6 +151,14 @@ class FragmentCache extends Widget
return $this->_content; return $this->_content;
} }
protected function updateDynamicContent($content, $placeholders)
{
foreach ($placeholders as $name => $statements) {
$placeholders[$name] = $this->view->evaluateDynamicContent($statements);
}
return strtr($content, $placeholders);
}
/** /**
* Generates a unique key used for storing the content in cache. * Generates a unique key used for storing the content in cache.
* The key generated depends on both [[id]] and [[variations]]. * The key generated depends on both [[id]] and [[variations]].
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment