mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API CHANGE: Partial cache adjustments - now supports nested cache blocks (which are independant of their containing cache block), conditionals to control if a given cache block is active, and includes hash of template code in key (so template changes mean cache is invalidated). Changes template control for cache block to <% cached %>, to which the now deprecated <% cacheblock %> is aliased, and an additional template control <% uncached %> has been added.
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.4@101137 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
b37fede7d1
commit
267de8887a
@ -462,13 +462,13 @@ class SSViewer {
|
||||
$content = preg_replace(array_keys($replacements), array_values($replacements), $content);
|
||||
$content = str_replace('{dlr}','$',$content);
|
||||
|
||||
// Cache block
|
||||
$content = SSViewer_PartialParser::process($template, $content);
|
||||
|
||||
// legacy
|
||||
$content = ereg_replace('<!-- +pc +([A-Za-z0-9_(),]+) +-->', '<' . '% control \\1 %' . '>', $content);
|
||||
$content = ereg_replace('<!-- +pc_end +-->', '<' . '% end_control %' . '>', $content);
|
||||
|
||||
// < % cacheblock key, key.. % >
|
||||
$content = SSViewer_PartialParser::parse($template, $content);
|
||||
|
||||
// < % control Foo % >
|
||||
$content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+) +%' . '>', '<? array_push($itemStack, $item); if($loop = $item->obj("\\1")) foreach($loop as $key => $item) { ?>', $content);
|
||||
// < % control Foo.Bar % >
|
||||
@ -666,63 +666,128 @@ class SSViewer_FromString extends SSViewer {
|
||||
*/
|
||||
class SSViewer_PartialParser {
|
||||
|
||||
static $opening_tag = '/< % [ \t]+ cacheblock [ \t]+ ([^%]+ [ \t]+)? % >/xS';
|
||||
static $tag = '/< % [ \t]+ (cached|cacheblock|uncached|end_cached|end_cacheblock|end_uncached) [ \t]+ ([^%]+ [ \t]+)? % >/xS';
|
||||
|
||||
static $argument_splitter = '/^\s*
|
||||
( (\w+) \s* ( \( ([^\)]*) \) )? ) | # A property lookup or a function call
|
||||
( \' [^\']+ \' ) | # A string surrounded by \'
|
||||
( " [^"]+ " ) # A string surrounded by "
|
||||
\s*/xS';
|
||||
# The argument itself
|
||||
(
|
||||
(?P<conditional> if | unless ) | # The if or unless keybreak
|
||||
(?P<property> (?P<identifier> \w+) \s* # A property lookup or a function call
|
||||
( \( (?P<arguments> [^\)]*) \) )?
|
||||
) |
|
||||
(?P<sqstring> \' (\\\'|[^\'])+ \' ) | # A string surrounded by \'
|
||||
(?P<dqstring> " (\\"|[^"])+ " ) # A string surrounded by "
|
||||
)
|
||||
# Some seperator after the argument
|
||||
(
|
||||
\s*(?P<comma>,)\s* | # A comma (maybe with whitespace before or after)
|
||||
(?P<fullstop>\.) # A period (no whitespace before)
|
||||
)?
|
||||
/xS';
|
||||
|
||||
static $closing_tag = '/< % [ \t]+ end_cacheblock [ \t]+ % >/xS';
|
||||
|
||||
static function parse($template, $content) {
|
||||
$parser = new SSViewer_PartialParser($template);
|
||||
|
||||
$content = $parser->replaceOpeningTags($content);
|
||||
$content = $parser->replaceClosingTags($content);
|
||||
return $content;
|
||||
static function process($template, $content) {
|
||||
$parser = new SSViewer_PartialParser($template, $content, 0, array(), 'if', 'false');
|
||||
$parser->parse();
|
||||
return $parser->generate();
|
||||
}
|
||||
|
||||
function __construct($template) {
|
||||
function __construct($template, $content, $offset, $keyparts, $conditional, $condition) {
|
||||
$this->template = $template;
|
||||
$this->cacheblocks = 0;
|
||||
$this->content = $content;
|
||||
$this->offset = $offset;
|
||||
|
||||
$this->keyparts = $keyparts;
|
||||
$this->conditional = $conditional;
|
||||
$this->condition = $condition;
|
||||
|
||||
$this->blocks = array();
|
||||
}
|
||||
|
||||
function replaceOpeningTags($content) {
|
||||
return preg_replace_callback(self::$opening_tag, array($this, 'replaceOpeningTagsCallback'), $content);
|
||||
function controlcheck($text) {
|
||||
$ifs = preg_match_all('/<'.'% +if +/', $text, $matches);
|
||||
$end_ifs = preg_match_all('/<'.'% +end_if +/', $text, $matches);
|
||||
|
||||
if ($ifs != $end_ifs) throw new Exception('You can\'t have cached or uncached blocks within condition structures');
|
||||
|
||||
$controls = preg_match_all('/<'.'% +control +/', $text, $matches);
|
||||
$end_controls = preg_match_all('/<'.'% +end_control +/', $text, $matches);
|
||||
|
||||
if ($controls != $end_controls) throw new Exception('You can\'t have cached or uncached blocks within control structures');
|
||||
}
|
||||
|
||||
function replaceOpeningTagsCallback($matches) {
|
||||
$this->cacheblocks += 1;
|
||||
$key = $this->key($matches);
|
||||
function parse() {
|
||||
$current_tag_offset = 0;
|
||||
|
||||
return '<? if ($partial = $cache->load('.$key.')) { $val .= $partial; } else { $valStack[] = $val; $val = ""; ?>';
|
||||
while (preg_match(self::$tag, $this->content, $matches, PREG_OFFSET_CAPTURE, $this->offset)) {
|
||||
$tag = $matches[1][0];
|
||||
|
||||
$startpos = $matches[0][1];
|
||||
$endpos = $matches[0][1] + strlen($matches[0][0]);
|
||||
|
||||
switch($tag) {
|
||||
case 'cached':
|
||||
case 'uncached':
|
||||
case 'cacheblock':
|
||||
|
||||
$pretext = substr($this->content, $this->offset, $startpos - $this->offset);
|
||||
$this->controlcheck($pretext);
|
||||
$this->blocks[] = $pretext;
|
||||
|
||||
if ($tag == 'cached' || $tag == 'cacheblock') {
|
||||
list($keyparts, $conditional, $condition) = $this->parseargs(@$matches[2][0]);
|
||||
}
|
||||
else {
|
||||
$keyparts = array(); $conditional = 'if'; $condition = 'false';
|
||||
}
|
||||
|
||||
function key($matches) {
|
||||
$parser = new SSViewer_PartialParser($this->template, $this->content, $endpos, $keyparts, $conditional, $condition);
|
||||
$parser->parse();
|
||||
$this->blocks[] = $parser;
|
||||
$this->offset = $parser->offset;
|
||||
break;
|
||||
|
||||
case 'end_cached':
|
||||
case 'end_cacheblock':
|
||||
case 'end_uncached':
|
||||
$this->blocks[] = substr($this->content, $this->offset, $startpos - $this->offset);
|
||||
$this->content = null;
|
||||
|
||||
$this->offset = $endpos;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
$this->blocks[] = substr($this->content, $this->offset);
|
||||
$this->content = null;
|
||||
}
|
||||
|
||||
function parseargs($string) {
|
||||
preg_match_all(self::$argument_splitter, $string, $matches, PREG_SET_ORDER);
|
||||
|
||||
$parts = array();
|
||||
$parts[] = "'".preg_replace('/[^\w+]/', '_', $this->template)."'";
|
||||
$conditional = null; $condition = null;
|
||||
|
||||
// If there weren't any arguments, that'll do
|
||||
if (!@$matches[1]) return $parts[0];
|
||||
$current = '$item->';
|
||||
|
||||
$current = 'preg_replace(\'/[^\w+]/\', \'_\', $item->';
|
||||
$keyspec = $matches[1];
|
||||
while (strlen($string) && preg_match(self::$argument_splitter, $string, $match)) {
|
||||
|
||||
while (strlen($keyspec) && preg_match(self::$argument_splitter, $keyspec, $submatch)) {
|
||||
$joiner = substr($keyspec, strlen($submatch[0]), 1);
|
||||
$keyspec = substr($keyspec, strlen($submatch[0]) + 1);
|
||||
$string = substr($string, strlen($match[0]));
|
||||
|
||||
// If this is a conditional keyword, break, and the next loop will grab the conditional
|
||||
if (@$match['conditional']) {
|
||||
$conditional = $match['conditional'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's a property lookup or a function call
|
||||
if ($submatch[1]) {
|
||||
if (@$match['property']) {
|
||||
// Get the property
|
||||
$what = $submatch[2];
|
||||
$what = $match['identifier'];
|
||||
$args = array();
|
||||
|
||||
// Extract any arguments passed to the function call
|
||||
if (@$submatch[3]) {
|
||||
foreach (explode(',', $submatch[4]) as $arg) {
|
||||
if (@$match['arguments']) {
|
||||
foreach (explode(',', $match['arguments']) as $arg) {
|
||||
$args[] = is_numeric($arg) ? (string)$arg : '"'.$arg.'"';
|
||||
}
|
||||
}
|
||||
@ -730,27 +795,90 @@ class SSViewer_PartialParser {
|
||||
$args = empty($args) ? 'null' : 'array('.implode(',',$args).')';
|
||||
|
||||
// If this fragment ended with '.', then there's another lookup coming, so return an obj for that lookup
|
||||
if ($joiner == '.') {
|
||||
$current .= "obj(\"$what\", $args, true)->";
|
||||
if (@$match['fullstop']) {
|
||||
$current .= "obj('$what', $args, true)->";
|
||||
}
|
||||
// Otherwise this is the end of the lookup chain, so add the resultant value to the key array and reset the key-get php fragement
|
||||
else {
|
||||
$parts[] = $current . "XML_val(\"$what\", $args, true))"; $current = 'preg_replace(\'/[^\w+]/\', \'_\', $item->';
|
||||
$accessor = $current . "XML_val('$what', $args, true)"; $current = '$item->';
|
||||
|
||||
// If we've hit a conditional already, this is the condition. Set it and be done.
|
||||
if ($conditional) {
|
||||
$condition = $accessor;
|
||||
break;
|
||||
}
|
||||
// Otherwise we're another key component. Add it to array.
|
||||
else $parts[] = $accessor;
|
||||
}
|
||||
}
|
||||
|
||||
// Else it's a quoted string of some kind
|
||||
else if ($submatch[5] || $submatch[6]) {
|
||||
$parts[] = $submatch[5] ? $submatch[5] : $submatch[6];
|
||||
else if (@$match['sqstring']) $parts[] = $match['sqstring'];
|
||||
else if (@$match['dqstring']) $parts[] = $match['dqstring'];
|
||||
}
|
||||
|
||||
if ($conditional && !$condition) {
|
||||
throw new Exception("You need to have a condition after the conditional $conditional in your cache block");
|
||||
}
|
||||
|
||||
return implode(".'_'.", $parts);
|
||||
return array($parts, $conditional, $condition);
|
||||
}
|
||||
|
||||
function replaceClosingTags($content) {
|
||||
return preg_replace(self::$closing_tag, '<? $cache->save($val); $val = array_pop($valStack) . $val; } ?>', $content);
|
||||
function key() {
|
||||
if (empty($this->keyparts)) return "''";
|
||||
return 'sha1(' . implode(".'_'.", $this->keyparts) . ')';
|
||||
}
|
||||
|
||||
function generate() {
|
||||
|
||||
$res = array();
|
||||
$key = $this->key();
|
||||
|
||||
$condition = "";
|
||||
|
||||
switch ($this->conditional) {
|
||||
case 'if':
|
||||
$condition = "{$this->condition} && ";
|
||||
break;
|
||||
case 'unless':
|
||||
$condition = "!({$this->condition}) && ";
|
||||
break;
|
||||
}
|
||||
|
||||
/* Output this set of blocks */
|
||||
|
||||
foreach ($this->blocks as $i => $block) {
|
||||
if ($block instanceof SSViewer_PartialParser)
|
||||
$res[] = $block->generate();
|
||||
else {
|
||||
// Include the template name and this cache block's current contents as a sha hash, so we get auto-seperation
|
||||
// of cache blocks, and invalidation of the cache when the template changes
|
||||
$partialkey = "'".sha1($this->template . $block)."_'.$key.'_$i'";
|
||||
|
||||
$knownUncached = array(
|
||||
'if' => array('false', '0'),
|
||||
'unless' => array('true', '1')
|
||||
);
|
||||
|
||||
// Optimized version if we know condition is false
|
||||
if ($this->conditional && in_array($this->condition, $knownUncached[$this->conditional])) {
|
||||
$res[] = $block;
|
||||
}
|
||||
else {
|
||||
// Try to load from cache
|
||||
$res[] = "<?\n".'if ('.$condition.' ($partial = $cache->load('.$partialkey.'))) $val .= $partial;'."\n";
|
||||
|
||||
// Cache miss - regenerate
|
||||
$res[] = "else {\n";
|
||||
$res[] = '$oldval = $val; $val = "";'."\n";
|
||||
$res[] = "\n?>" . $block . "<?\n";
|
||||
$res[] = $condition . ' $cache->save($val); $val = $oldval . $val ;'."\n";
|
||||
$res[] = "}\n?>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode('', $res);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,13 @@ class SSViewerCacheBlockTest_Model extends DataObject implements TestOnly {
|
||||
return 'Bar';
|
||||
}
|
||||
|
||||
function True() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function False() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class SSViewerCacheBlockTest extends SapphireTest {
|
||||
@ -28,45 +35,179 @@ class SSViewerCacheBlockTest extends SapphireTest {
|
||||
|
||||
protected function _runtemplate($template, $data = null) {
|
||||
if ($data === null) $data = $this->data;
|
||||
if (is_array($data)) $data = new ArrayData($data);
|
||||
if (is_array($data)) $data = $this->data->customise($data);
|
||||
|
||||
$viewer = SSViewer::fromString($template);
|
||||
return $viewer->process($data);
|
||||
}
|
||||
|
||||
function testParsing() {
|
||||
// Make sure an empty cacheblock parses
|
||||
|
||||
// ** Trivial checks **
|
||||
|
||||
// Make sure an empty cached block parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cached %><% end_cached %>'), '');
|
||||
|
||||
// Make sure an empty cacheblock block parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock %><% end_cacheblock %>'), '');
|
||||
|
||||
// Make sure an empty uncached block parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% uncached %><% end_uncached %>'), '');
|
||||
|
||||
// ** Argument checks **
|
||||
|
||||
// Make sure a simple cacheblock parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock %>Yay<% end_cacheblock %>'), 'Yay');
|
||||
$this->assertEquals($this->_runtemplate('<% cached %>Yay<% end_cached %>'), 'Yay');
|
||||
|
||||
// Make sure a moderately complicated cacheblock parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock \'block\', Foo, "jumping" %>Yay<% end_cacheblock %>'), 'Yay');
|
||||
$this->assertEquals($this->_runtemplate('<% cached \'block\', Foo, "jumping" %>Yay<% end_cached %>'), 'Yay');
|
||||
|
||||
// Make sure a complicated cacheblock parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock \'block\', Foo, Test.Test(4).Test(jumping).Foo %>Yay<% end_cacheblock %>'), 'Yay');
|
||||
$this->assertEquals($this->_runtemplate('<% cached \'block\', Foo, Test.Test(4).Test(jumping).Foo %>Yay<% end_cached %>'), 'Yay');
|
||||
|
||||
// ** Conditional Checks **
|
||||
|
||||
// Make sure a cacheblock with a simple conditional parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cached if true %>Yay<% end_cached %>'), 'Yay');
|
||||
|
||||
// Make sure a cacheblock with a complex conditional parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cached if Test.Test(yank).Foo %>Yay<% end_cached %>'), 'Yay');
|
||||
|
||||
// Make sure a cacheblock with a complex conditional and arguments parses
|
||||
$this->_reset();
|
||||
$this->assertEquals($this->_runtemplate('<% cached Foo, Test.Test(4).Test(jumping).Foo if Test.Test(yank).Foo %>Yay<% end_cached %>'), 'Yay');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cacheblocks actually cache
|
||||
*/
|
||||
function testBlocksCache() {
|
||||
// First, run twice without caching, to prove $Increment actually increments
|
||||
// First, run twice without caching, to prove we get two different values
|
||||
$this->_reset(false);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock %>$Foo<% end_cacheblock %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock %>$Foo<% end_cacheblock %>', array('Foo' => 2)), '2');
|
||||
$this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
|
||||
|
||||
// Then twice with caching, should get same result each time
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock %>$Foo<% end_cacheblock %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cacheblock %>$Foo<% end_cacheblock %>', array('Foo' => 2)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached %>$Foo<% end_cached %>', array('Foo' => 2)), '1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cacheblocks conditionally cache with if
|
||||
*/
|
||||
function testBlocksConditionallyCacheWithIf() {
|
||||
// First, run twice with caching
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached if True %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached if True %>$Foo<% end_cached %>', array('Foo' => 2)), '1');
|
||||
|
||||
// Then twice without caching
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached if False %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached if False %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
|
||||
|
||||
// Then once cached, once not (and the opposite)
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 1, 'Cache' => true )), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 2, 'Cache' => false)), '2');
|
||||
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 1, 'Cache' => false)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 2, 'Cache' => true )), '2');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cacheblocks conditionally cache with unless
|
||||
*/
|
||||
function testBlocksConditionallyCacheWithUnless() {
|
||||
// First, run twice with caching
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached unless False %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached unless False %>$Foo<% end_cached %>', array('Foo' => 2)), '1');
|
||||
|
||||
// Then twice without caching
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached unless True %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
|
||||
$this->assertEquals($this->_runtemplate('<% cached unless True %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that nested uncached blocks work
|
||||
*/
|
||||
function testNestedUncachedBlocks() {
|
||||
// First, run twice with caching, to prove we get the same result back normally
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached %> A $Foo B <% end_cached %>', array('Foo' => 1)), ' A 1 B ');
|
||||
$this->assertEquals($this->_runtemplate('<% cached %> A $Foo B <% end_cached %>', array('Foo' => 2)), ' A 1 B ');
|
||||
|
||||
// Then add uncached to the nested block
|
||||
$this->_reset(true);
|
||||
|
||||
$this->assertEquals($this->_runtemplate('<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>', array('Foo' => 1)), ' A 1 B ');
|
||||
$this->assertEquals($this->_runtemplate('<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>', array('Foo' => 2)), ' A 2 B ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that nested blocks with different keys works
|
||||
*/
|
||||
function testNestedBlocks() {
|
||||
$this->_reset(true);
|
||||
|
||||
$template = '<% cached Foo %> $Fooa <% cached Bar %>$Bara<% end_cached %> $Foob <% end_cached %>';
|
||||
|
||||
// Do it the first time to load the cache
|
||||
$this->assertEquals($this->_runtemplate($template, array('Foo' => 1, 'Fooa' => 1, 'Foob' => 3, 'Bar' => 1, 'Bara' => 2)), ' 1 2 3 ');
|
||||
|
||||
// Do it again, the input values are ignored as the cache is hit for both elements
|
||||
$this->assertEquals($this->_runtemplate($template, array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 1, 'Bara' => 9)), ' 1 2 3 ');
|
||||
|
||||
// Do it again with a new key for Bar, Bara is picked up, Fooa and Foob are not
|
||||
$this->assertEquals($this->_runtemplate($template, array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 9)), ' 1 9 3 ');
|
||||
|
||||
// Do it again with a new key for Foo, Fooa and Foob are picked up, Bara are not
|
||||
$this->assertEquals($this->_runtemplate($template, array('Foo' => 2, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 1)), ' 9 9 9 ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
function testErrorMessageForCacheWithinControl() {
|
||||
$this->_reset(true);
|
||||
$this->_runtemplate('<% control Foo %><% cached %>$Bar<% end_cached %><% end_control %>');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
function testErrorMessageForCacheWithinIf() {
|
||||
$this->_reset(true);
|
||||
$this->_runtemplate('<% if Foo %><% cached %>$Bar<% end_cached %><% end_if %>');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
function testErrorMessageForInvalidConditional() {
|
||||
$this->_reset(true);
|
||||
$this->_runtemplate('<% cached Foo if %>$Bar<% end_cached %>');
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user