diff --git a/src/Compile/Base.php b/src/Compile/Base.php index df19b136..90e1b347 100644 --- a/src/Compile/Base.php +++ b/src/Compile/Base.php @@ -5,6 +5,7 @@ */ namespace Smarty\Compile; +use Smarty\Compiler\Template; use Smarty\Data; use Smarty\Exception; @@ -49,20 +50,6 @@ abstract class Base implements CompilerInterface { */ protected $cacheable = true; - /** - * Mapping array for boolean option value - * - * @var array - */ - private $optionMap = [1 => true, 0 => false, 'true' => true, 'false' => false]; - - /** - * Mapping array with attributes as key - * - * @var array - */ - protected $mapCache = []; - public function isCacheable(): bool { return $this->cacheable; } @@ -96,14 +83,12 @@ abstract class Base implements CompilerInterface { */ protected function getAttributes($compiler, $attributes) { $_indexed_attr = []; - if (!isset($this->mapCache['option'])) { - $this->mapCache['option'] = array_fill_keys($this->option_flags, true); - } + $options = array_fill_keys($this->option_flags, true); foreach ($attributes as $key => $mixed) { // shorthand ? if (!is_array($mixed)) { - // option flag ? - if (isset($this->mapCache['option'][trim($mixed, '\'"')])) { + // options flag ? + if (isset($options[trim($mixed, '\'"')])) { $_indexed_attr[trim($mixed, '\'"')] = true; // shorthand attribute ? } elseif (isset($this->shorttag_order[$key])) { @@ -115,20 +100,24 @@ abstract class Base implements CompilerInterface { // named attribute } else { foreach ($mixed as $k => $v) { - // option flag? - if (isset($this->mapCache['option'][$k])) { + // options flag? + if (isset($options[$k])) { if (is_bool($v)) { $_indexed_attr[$k] = $v; } else { if (is_string($v)) { $v = trim($v, '\'" '); } - if (isset($this->optionMap[$v])) { - $_indexed_attr[$k] = $this->optionMap[$v]; + + // Mapping array for boolean option value + static $optionMap = [1 => true, 0 => false, 'true' => true, 'false' => false]; + + if (isset($optionMap[$v])) { + $_indexed_attr[$k] = $optionMap[$v]; } else { $compiler->trigger_template_error( "illegal value '" . var_export($v, true) . - "' for option flag '{$k}'", + "' for options flag '{$k}'", null, true ); @@ -149,32 +138,27 @@ abstract class Base implements CompilerInterface { } // check for not allowed attributes if ($this->optional_attributes !== ['_any']) { - if (!isset($this->mapCache['all'])) { - $this->mapCache['all'] = - array_fill_keys( - array_merge( - $this->required_attributes, - $this->optional_attributes, - $this->option_flags - ), - true - ); - } + $allowedAttributes = array_fill_keys( + array_merge( + $this->required_attributes, + $this->optional_attributes, + $this->option_flags + ), + true + ); foreach ($_indexed_attr as $key => $dummy) { - if (!isset($this->mapCache['all'][$key]) && $key !== 0) { + if (!isset($allowedAttributes[$key]) && $key !== 0) { $compiler->trigger_template_error("unexpected '{$key}' attribute", null, true); } } } - // default 'false' for all option flags not set + // default 'false' for all options flags not set foreach ($this->option_flags as $flag) { if (!isset($_indexed_attr[$flag])) { $_indexed_attr[$flag] = false; } } - if (isset($_indexed_attr['nocache']) && $_indexed_attr['nocache']) { - $compiler->tag_nocache = true; - } + return $_indexed_attr; } @@ -182,44 +166,25 @@ abstract class Base implements CompilerInterface { * Push opening tag name on stack * Optionally additional data can be saved on stack * - * @param object $compiler compiler object + * @param Template $compiler compiler object * @param string $openTag the opening tag's name * @param mixed $data optional data saved */ - protected function openTag($compiler, $openTag, $data = null) { - array_push($compiler->_tag_stack, [$openTag, $data]); + protected function openTag(Template $compiler, $openTag, $data = null) { + $compiler->openTag($openTag, $data); } /** * Pop closing tag * Raise an error if this stack-top doesn't match with expected opening tags * - * @param object $compiler compiler object + * @param Template $compiler compiler object * @param array|string $expectedTag the expected opening tag names * * @return mixed any type the opening tag's name or saved data */ - protected function closeTag($compiler, $expectedTag) { - if (count($compiler->_tag_stack) > 0) { - // get stacked info - [$_openTag, $_data] = array_pop($compiler->_tag_stack); - // open tag must match with the expected ones - if (in_array($_openTag, (array)$expectedTag)) { - if (is_null($_data)) { - // return opening tag - return $_openTag; - } else { - // return restored data - return $_data; - } - } - // wrong nesting of tags - $compiler->trigger_template_error("unclosed '" . $compiler->getTemplate()->getLeftDelimiter() . "{$_openTag}" . - $compiler->getTemplate()->getRightDelimiter() . "' tag"); - return; - } - // wrong nesting of tags - $compiler->trigger_template_error('unexpected closing tag', null, true); + protected function closeTag(Template $compiler, $expectedTag) { + return $compiler->closeTag($expectedTag); } /** @@ -259,11 +224,11 @@ abstract class Base implements CompilerInterface { * Compiles code for the tag * * @param array $args array with attributes from parser - * @param \Smarty\Compiler\Template $compiler compiler object + * @param Template $compiler compiler object * @param array $parameter array with compilation parameter * * @return bool|string compiled code or true if no code has been compiled * @throws \Smarty\CompilerException */ - abstract public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = array(), $tag = null, $function = null); + abstract public function compile($args, Template $compiler, $parameter = array(), $tag = null, $function = null); } diff --git a/src/Compile/BlockCompiler.php b/src/Compile/BlockCompiler.php index 373a21b2..63849c41 100644 --- a/src/Compile/BlockCompiler.php +++ b/src/Compile/BlockCompiler.php @@ -18,8 +18,6 @@ use Smarty\Smarty; /** * Smarty Internal Plugin Compile Block Plugin Class * - - */ class BlockCompiler extends Base { @@ -62,6 +60,56 @@ class BlockCompiler extends Base { return $output; } + /** + * Compiles code for the {$smarty.block.child} property + * + * @param Template $compiler compiler object + * + * @return string compiled code + * @throws CompilerException + */ + public function compileChild(\Smarty\Compiler\Template $compiler) { + + if (!isset($compiler->_cache['blockNesting'])) { + $compiler->trigger_template_error( + "{\$smarty.block.child} used outside {block} tags ", + $compiler->getParser()->lex->taglineno + ); + } + $compiler->has_code = true; + $compiler->suppressNocacheProcessing = true; + + $output = "getInheritance()->callChild($_smarty_tpl, $this' . ");\n"; + $output .= "?>\n"; + return $output; + } + + /** + * Compiles code for the {$smarty.block.parent} property + * + * @param Template $compiler compiler object + * + * @return string compiled code + * @throws CompilerException + */ + public function compileParent(\Smarty\Compiler\Template $compiler) { + + if (!isset($compiler->_cache['blockNesting'])) { + $compiler->trigger_template_error( + "{\$smarty.block.parent} used outside {block} tags ", + $compiler->getParser()->lex->taglineno + ); + } + $compiler->has_code = true; + $compiler->suppressNocacheProcessing = true; + + $output = "getInheritance()->callParent($_smarty_tpl, $this' . ");\n"; + $output .= "?>\n"; + return $output; + } + /** * Returns true if this block is cacheable. * @@ -118,6 +166,13 @@ class BlockCompiler extends Base { $compiler->tag_nocache = true; } + if ($compiler->tag_nocache) { + // push a {nocache} tag onto the stack to prevent caching of this block + $this->openTag($compiler, 'nocache'); + } + + $this->openTag($compiler, $tag, [$_params, $compiler->tag_nocache]); + // compile code $output = "getIsCallableCode($tag, $function) .") {\nthrow new \\Smarty\\Exception('block tag \'{$tag}\' not callable or registered');\n}\n @@ -125,9 +180,7 @@ echo " . $this->getFullCallbackCode($tag, $function) . "({$_params}, null, \$_sm while (\$_block_repeat) { ob_start(); ?>"; - $this->openTag($compiler, $tag, [$_params, $compiler->nocache]); - // maybe nocache because of nocache variables or nocache plugin - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + return $output; } @@ -142,13 +195,11 @@ while (\$_block_repeat) { * @throws Exception */ private function compileClosingTag(Template $compiler, string $tag, array $parameter, ?string $function): string { - // must endblock be nocache? - if ($compiler->nocache) { - $compiler->tag_nocache = true; - } + // closing tag of block plugin, restore nocache $base_tag = substr($tag, 0, -5); - [$_params, $compiler->nocache] = $this->closeTag($compiler, $base_tag); + [$_params, $nocache_pushed] = $this->closeTag($compiler, $base_tag); + // compile code if (!isset($parameter['modifier_list'])) { $mod_pre = $mod_post = $mod_content = ''; @@ -164,6 +215,13 @@ while (\$_block_repeat) { $callback = $this->getFullCallbackCode($base_tag, $function); $output .= "echo {$callback}({$_params}, {$mod_content2}, \$_smarty_tpl, \$_block_repeat);\n"; $output .= "{$mod_post}}\n?>"; + + if ($nocache_pushed) { + // pop the pushed virtual nocache tag + $this->closeTag($compiler, 'nocache'); + $compiler->tag_nocache = true; + } + return $output; } diff --git a/src/Compile/Tag/Append.php b/src/Compile/Tag/Append.php index e4a7b841..86eda99e 100644 --- a/src/Compile/Tag/Append.php +++ b/src/Compile/Tag/Append.php @@ -19,6 +19,12 @@ namespace Smarty\Compile\Tag; */ class Append extends Assign { + + /** + * @inheritdoc + */ + protected $optional_attributes = ['scope', 'index']; + /** * Compiles code for the {append} tag * @@ -31,13 +37,10 @@ class Append extends Assign */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = array(), $tag = null, $function = null) { - // the following must be assigned at runtime because it will be overwritten in parent class - $this->required_attributes = array('var', 'value'); - $this->shorttag_order = array('var', 'value'); - $this->optional_attributes = array('scope', 'index'); - $this->mapCache = array(); + // check and get attributes $_attr = $this->getAttributes($compiler, $args); + // map to compile assign attributes if (isset($_attr[ 'index' ])) { $_params[ 'smarty_internal_index' ] = '[' . $_attr[ 'index' ] . ']'; diff --git a/src/Compile/Tag/Assign.php b/src/Compile/Tag/Assign.php index f1cf1385..1b7c3ce7 100644 --- a/src/Compile/Tag/Assign.php +++ b/src/Compile/Tag/Assign.php @@ -22,6 +22,21 @@ use Smarty\Smarty; */ class Assign extends Base { + /** + * @inheritdoc + */ + protected $required_attributes = ['var', 'value']; + + /** + * @inheritdoc + */ + protected $optional_attributes = ['scope']; + + /** + * @inheritdoc + */ + protected $shorttag_order = ['var', 'value']; + /** * Attribute definition: Overwrites base class. * @@ -42,21 +57,17 @@ class Assign extends Base */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = array(), $tag = null, $function = null) { - // the following must be assigned at runtime because it will be overwritten in Append - $this->required_attributes = array('var', 'value'); - $this->shorttag_order = array('var', 'value'); - $this->optional_attributes = array('scope'); - $this->mapCache = array(); + $_nocache = false; // check and get attributes $_attr = $this->getAttributes($compiler, $args); - // nocache ? + if ($_var = $compiler->getId($_attr[ 'var' ])) { $_var = "'{$_var}'"; } else { $_var = $_attr[ 'var' ]; } - if ($compiler->tag_nocache || $compiler->nocache) { + if ($compiler->tag_nocache || $compiler->isNocacheActive()) { $_nocache = true; // create nocache var to make it know for further compiling $compiler->setNocacheInVariable($_attr[ 'var' ]); diff --git a/src/Compile/Tag/Block.php b/src/Compile/Tag/Block.php index 85fb4dbc..8bbd44f6 100644 --- a/src/Compile/Tag/Block.php +++ b/src/Compile/Tag/Block.php @@ -74,20 +74,17 @@ class Block extends Inheritance { $_attr = $this->getAttributes($compiler, $args); ++$compiler->_cache['blockNesting']; $_className = 'Block_' . preg_replace('![^\w]+!', '_', uniqid(mt_rand(), true)); - $compiler->_cache['blockName'][$compiler->_cache['blockNesting']] = $_attr['name']; - $compiler->_cache['blockClass'][$compiler->_cache['blockNesting']] = $_className; - $compiler->_cache['blockParams'][$compiler->_cache['blockNesting']] = []; - $compiler->_cache['blockParams'][1]['subBlocks'][trim($_attr['name'], '"\'')][] = $_className; + $this->openTag( $compiler, 'block', [ - $_attr, $compiler->nocache, $compiler->getParser()->current_buffer, - $compiler->getTemplate()->getCompiled()->getNocacheCode(), - $compiler->getTemplate()->caching, + $_attr, $compiler->tag_nocache, $compiler->getParser()->current_buffer, + $compiler->getTemplate()->getCompiled()->getNocacheCode(), $_className ] ); - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + + // @TODO what is this for? $compiler->getParser()->current_buffer = new Template(); $compiler->getTemplate()->getCompiled()->setNocacheCode(false); $compiler->suppressNocacheProcessing = true; diff --git a/src/Compile/Tag/BlockChild.php b/src/Compile/Tag/BlockChild.php deleted file mode 100644 index 8d6870b5..00000000 --- a/src/Compile/Tag/BlockChild.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ -class BlockChild extends Child { - - /** - * Tag name - * - * @var string - */ - protected $tag = 'block_child'; -} diff --git a/src/Compile/Tag/BlockClose.php b/src/Compile/Tag/BlockClose.php index 8458ee5f..10ef3b7b 100644 --- a/src/Compile/Tag/BlockClose.php +++ b/src/Compile/Tag/BlockClose.php @@ -20,19 +20,11 @@ class BlockClose extends Inheritance { */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = array(), $tag = null, $function = null) { - [$_attr, $_nocache, $_buffer, $_has_nocache_code, $_caching] = $this->closeTag($compiler, ['block']); - // init block parameter - $_block = $compiler->_cache['blockParams'][$compiler->_cache['blockNesting']]; - unset($compiler->_cache['blockParams'][$compiler->_cache['blockNesting']]); + [$_attr, $_nocache, $_buffer, $_has_nocache_code, $_className] = $this->closeTag($compiler, ['block']); + $_name = $_attr['name']; - $_assign = isset($_attr['assign']) ? $_attr['assign'] : null; - unset($_attr['assign'], $_attr['name']); - foreach ($_attr as $name => $stat) { - if ((is_bool($stat) && $stat !== false) || (!is_bool($stat) && $stat !== 'false')) { - $_block[$name] = 'true'; - } - } - $_className = $compiler->_cache['blockClass'][$compiler->_cache['blockNesting']]; + $_assign = $_attr['assign'] ?? null; + // get compiled block code $_functionCode = $compiler->getParser()->current_buffer; // setup buffer for template function code @@ -41,9 +33,6 @@ class BlockClose extends Inheritance { $output .= $compiler->cStyleComment(" {block {$_name}} ") . "\n"; $output .= "class {$_className} extends \\Smarty\\Runtime\\Block\n"; $output .= "{\n"; - foreach ($_block as $property => $value) { - $output .= "public \${$property} = " . var_export($value, true) . ";\n"; - } $output .= "public function callBlock(\\Smarty\\Template \$_smarty_tpl) {\n"; if ($compiler->getTemplate()->getCompiled()->getNocacheCode()) { $output .= "\$_smarty_tpl->getCached()->hashes['{$compiler->getTemplate()->getCompiled()->nocache_hash}'] = true;\n"; @@ -76,11 +65,13 @@ class BlockClose extends Inheritance { ) ); $compiler->blockOrFunctionCode .= $compiler->getParser()->current_buffer->to_smarty_php($compiler->getParser()); + $compiler->getParser()->current_buffer = new Template(); + // restore old status $compiler->getTemplate()->getCompiled()->setNocacheCode($_has_nocache_code); - $compiler->tag_nocache = $compiler->nocache; - $compiler->nocache = $_nocache; + $compiler->tag_nocache = $_nocache; + $compiler->getParser()->current_buffer = $_buffer; $output = "_cache['blockNesting'] === 1) { diff --git a/src/Compile/Tag/BlockParent.php b/src/Compile/Tag/BlockParent.php deleted file mode 100644 index 276032aa..00000000 --- a/src/Compile/Tag/BlockParent.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ -class BlockParent extends Child { - - /** - * Tag name - * - * @var string - */ - protected $tag = 'block_parent'; - - /** - * Block type - * - * @var string - */ - protected $blockType = 'Parent'; -} diff --git a/src/Compile/Tag/BreakTag.php b/src/Compile/Tag/BreakTag.php index fd562511..0ec03df2 100644 --- a/src/Compile/Tag/BreakTag.php +++ b/src/Compile/Tag/BreakTag.php @@ -93,17 +93,20 @@ class BreakTag extends Base { $levels = 1; } $level_count = $levels; - $stack_count = count($compiler->_tag_stack) - 1; + + $tagStack = $compiler->getTagStack(); + $stack_count = count($tagStack) - 1; + $foreachLevels = 0; $lastTag = ''; while ($level_count > 0 && $stack_count >= 0) { - if (isset($_is_loopy[$compiler->_tag_stack[$stack_count][0]])) { - $lastTag = $compiler->_tag_stack[$stack_count][0]; + if (isset($_is_loopy[$tagStack[$stack_count][0]])) { + $lastTag = $tagStack[$stack_count][0]; if ($level_count === 0) { break; } $level_count--; - if ($compiler->_tag_stack[$stack_count][0] === 'foreach') { + if ($tagStack[$stack_count][0] === 'foreach') { $foreachLevels++; } } diff --git a/src/Compile/Tag/Call.php b/src/Compile/Tag/Call.php index 1c691add..501460c5 100644 --- a/src/Compile/Tag/Call.php +++ b/src/Compile/Tag/Call.php @@ -59,7 +59,7 @@ class Call extends Base { $_name = $_attr['name']; unset($_attr['name'], $_attr['assign'], $_attr['nocache']); // set flag (compiled code of {function} must be included in cache file - if (!$compiler->getTemplate()->caching || $compiler->nocache || $compiler->tag_nocache) { + if (!$compiler->getTemplate()->caching || $compiler->isNocacheActive() || $compiler->tag_nocache) { $_nocache = 'true'; } else { $_nocache = 'false'; diff --git a/src/Compile/Tag/Capture.php b/src/Compile/Tag/Capture.php index d089e8c9..fc8804a0 100644 --- a/src/Compile/Tag/Capture.php +++ b/src/Compile/Tag/Capture.php @@ -55,13 +55,17 @@ class Capture extends Base { */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { // check and get attributes - $_attr = $this->getAttributes($compiler, $args, $parameter, 'capture'); + $_attr = $this->getAttributes($compiler, $args); $buffer = $_attr['name'] ?? "'default'"; $assign = $_attr['assign'] ?? 'null'; $append = $_attr['append'] ?? 'null'; - $compiler->_cache['capture_stack'][] = [$compiler->nocache]; - // maybe nocache because of nocache variables - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + + $compiler->_cache['capture_stack'][] = $compiler->tag_nocache; + if ($compiler->tag_nocache) { + // push a virtual {nocache} tag onto the stack. + $compiler->openTag('nocache'); + } + $_output = "getSmarty()->getRuntime('Capture')->open(\$_smarty_tpl, $buffer, $assign, $append);?>"; return $_output; } diff --git a/src/Compile/Tag/CaptureClose.php b/src/Compile/Tag/CaptureClose.php index a6e3aded..0d553a2b 100644 --- a/src/Compile/Tag/CaptureClose.php +++ b/src/Compile/Tag/CaptureClose.php @@ -30,13 +30,13 @@ class CaptureClose extends Base { * @return string compiled code */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - // check and get attributes - $_attr = $this->getAttributes($compiler, $args, $parameter, '/capture'); - // must endblock be nocache? - if ($compiler->nocache) { + + if (array_pop($compiler->_cache['capture_stack'])) { + // pop the virtual {nocache} tag from the stack. + $compiler->closeTag('nocache'); $compiler->tag_nocache = true; } - [$compiler->nocache] = array_pop($compiler->_cache['capture_stack']); + return "getSmarty()->getRuntime('Capture')->close(\$_smarty_tpl);?>"; } } diff --git a/src/Compile/Tag/Child.php b/src/Compile/Tag/Child.php deleted file mode 100644 index 03e045ab..00000000 --- a/src/Compile/Tag/Child.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ -class Child extends Base { - - /** - * Attribute definition: Overwrites base class. - * - * @var array - * @see BasePlugin - */ - protected $optional_attributes = ['assign']; - - /** - * Tag name - * - * @var string - */ - protected $tag = 'child'; - - /** - * Block type - * - * @var string - */ - protected $blockType = 'Child'; - - /** - * Compiles code for the {child} tag - * - * @param array $args array with attributes from parser - * @param \Smarty\Compiler\Template $compiler compiler object - * @param array $parameter array with compilation parameter - * - * @return string compiled code - * @throws \Smarty\CompilerException - */ - public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - // check and get attributes - $_attr = $this->getAttributes($compiler, $args); - $tag = isset($parameter[0]) ? "'{$parameter[0]}'" : "'{{$this->tag}}'"; - if (!isset($compiler->_cache['blockNesting'])) { - $compiler->trigger_template_error( - "{$tag} used outside {block} tags ", - $compiler->getParser()->lex->taglineno - ); - } - $compiler->has_code = true; - $compiler->suppressNocacheProcessing = true; - if ($this->blockType === 'Child') { - $compiler->_cache['blockParams'][$compiler->_cache['blockNesting']]['callsChild'] = 'true'; - } - $_assign = $_attr['assign'] ?? null; - $output = "getInheritance()->call' . $this->blockType . '($_smarty_tpl, $this' . - ($this->blockType === 'Child' ? '' : ", {$tag}") . ");\n"; - if (isset($_assign)) { - $output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean());\n"; - } - $output .= "?>\n"; - return $output; - } -} diff --git a/src/Compile/Tag/ElseIfTag.php b/src/Compile/Tag/ElseIfTag.php index c127e273..60b888a8 100644 --- a/src/Compile/Tag/ElseIfTag.php +++ b/src/Compile/Tag/ElseIfTag.php @@ -23,9 +23,9 @@ class ElseIfTag extends Base { * @throws \Smarty\CompilerException */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - // check and get attributes - $_attr = $this->getAttributes($compiler, $args); - [$nesting, $compiler->tag_nocache] = $this->closeTag($compiler, ['if', 'elseif']); + + [$nesting, $nocache_pushed] = $this->closeTag($compiler, ['if', 'elseif']); + if (!isset($parameter['if condition'])) { $compiler->trigger_template_error('missing elseif condition', null, true); } @@ -38,7 +38,7 @@ class ElseIfTag extends Base { } else { $var = $parameter['if condition']['var']; } - if ($compiler->nocache) { + if ($compiler->isNocacheActive()) { // create nocache var to make it know for further compiling $compiler->setNocacheInVariable($var); } @@ -68,12 +68,12 @@ class ElseIfTag extends Base { $_output = $compiler->appendCode("", $assignCode); return $compiler->appendCode($_output, ""); } else { - $this->openTag($compiler, 'elseif', [$nesting, $compiler->tag_nocache]); + $this->openTag($compiler, 'elseif', [$nesting, $nocache_pushed]); return ""; } } else { $_output = $compiler->appendCode("", $prefixCode); - $this->openTag($compiler, 'elseif', [$nesting + 1, $compiler->tag_nocache]); + $this->openTag($compiler, 'elseif', [$nesting + 1, $nocache_pushed]); if ($condition_by_assign) { $_output = $compiler->appendCode($_output, $assignCode); return $compiler->appendCode($_output, ""); diff --git a/src/Compile/Tag/ForClose.php b/src/Compile/Tag/ForClose.php index cfc255e6..c3646d5f 100644 --- a/src/Compile/Tag/ForClose.php +++ b/src/Compile/Tag/ForClose.php @@ -31,18 +31,20 @@ class ForClose extends Base { */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { $compiler->loopNesting--; - // check and get attributes - $this->getAttributes($compiler, $args); - // must endblock be nocache? - if ($compiler->nocache) { - $compiler->tag_nocache = true; - } - [$openTag, $compiler->nocache] = $this->closeTag($compiler, ['for', 'forelse']); + + [$openTag, $nocache_pushed] = $this->closeTag($compiler, ['for', 'forelse']); $output = ""; + + if ($nocache_pushed) { + // pop the pushed virtual nocache tag + $this->closeTag('nocache'); + $compiler->tag_nocache = true; + } + return $output; } } diff --git a/src/Compile/Tag/ForElse.php b/src/Compile/Tag/ForElse.php index 916beec7..a754a0d5 100644 --- a/src/Compile/Tag/ForElse.php +++ b/src/Compile/Tag/ForElse.php @@ -22,10 +22,8 @@ class ForElse extends Base { * @return string compiled code */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - // check and get attributes - $this->getAttributes($compiler, $args); - [$openTag, $nocache] = $this->closeTag($compiler, ['for']); - $this->openTag($compiler, 'forelse', ['forelse', $nocache]); + [$tagName, $nocache_pushed] = $this->closeTag($compiler, ['for']); + $this->openTag($compiler, 'forelse', ['forelse', $nocache_pushed]); return ""; } } \ No newline at end of file diff --git a/src/Compile/Tag/ForTag.php b/src/Compile/Tag/ForTag.php index f62d8771..38266944 100644 --- a/src/Compile/Tag/ForTag.php +++ b/src/Compile/Tag/ForTag.php @@ -37,7 +37,7 @@ class ForTag extends Base { $this->required_attributes = ['start', 'ifexp', 'var', 'step']; $this->optional_attributes = []; } - $this->mapCache = []; + // check and get attributes $_attr = $this->getAttributes($compiler, $args); $output = "tpl_vars[$var]->last = \$_smarty_tpl->tpl_vars[$var]->iteration === \$_smarty_tpl->tpl_vars[$var]->total;"; } $output .= '?>'; - $this->openTag($compiler, 'for', ['for', $compiler->nocache]); - // maybe nocache because of nocache variables - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; - // return compiled code + + if ($compiler->tag_nocache) { + // push a {nocache} tag onto the stack to prevent caching of this for loop + $this->openTag('nocache'); + } + + $this->openTag($compiler, 'for', ['for', $compiler->tag_nocache]); + return $output; } } \ No newline at end of file diff --git a/src/Compile/Tag/ForeachClose.php b/src/Compile/Tag/ForeachClose.php index 513062bd..7ec2393b 100644 --- a/src/Compile/Tag/ForeachClose.php +++ b/src/Compile/Tag/ForeachClose.php @@ -31,13 +31,15 @@ class ForeachClose extends Base { */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { $compiler->loopNesting--; - // must endblock be nocache? - if ($compiler->nocache) { + + [$openTag, $nocache_pushed, $local, $itemVar, $restore] = $this->closeTag($compiler, ['foreach', 'foreachelse']); + + if ($nocache_pushed) { + // pop the pushed virtual nocache tag + $this->closeTag('nocache'); $compiler->tag_nocache = true; } - [ - $openTag, $compiler->nocache, $local, $itemVar, $restore, - ] = $this->closeTag($compiler, ['foreach', 'foreachelse']); + $output = "getAttributes($compiler, $args); - [$openTag, $nocache, $local, $itemVar, $restore] = $this->closeTag($compiler, ['foreach']); - $this->openTag($compiler, 'foreachelse', ['foreachelse', $nocache, $local, $itemVar, 0]); + + [$openTag, $nocache_pushed, $local, $itemVar, $restore] = $this->closeTag($compiler, ['foreach']); + $this->openTag($compiler, 'foreachelse', ['foreachelse', $nocache_pushed, $local, $itemVar, 0]); $output = "tpl_vars['__smarty_foreach_{$attributes['name']}']"; } $needTotal = isset($itemAttr['total']); + + if ($compiler->tag_nocache) { + // push a {nocache} tag onto the stack to prevent caching of this block + $this->openTag('nocache'); + } + // Register tag $this->openTag( $compiler, 'foreach', - ['foreach', $compiler->nocache, $local, $itemVar, empty($itemAttr) ? 1 : 2] + ['foreach', $compiler->tag_nocache, $local, $itemVar, empty($itemAttr) ? 1 : 2] ); - // maybe nocache because of nocache variables - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + // generate output code $output = "getSmarty()->getRuntime('Foreach')->init(\$_smarty_tpl, $from, " . diff --git a/src/Compile/Tag/IfClose.php b/src/Compile/Tag/IfClose.php index 62737a3e..bf092d1f 100644 --- a/src/Compile/Tag/IfClose.php +++ b/src/Compile/Tag/IfClose.php @@ -29,11 +29,15 @@ class IfClose extends Base { * @return string compiled code */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - // must endblock be nocache? - if ($compiler->nocache) { + + [$nesting, $nocache_pushed] = $this->closeTag($compiler, ['if', 'else', 'elseif']); + + if ($nocache_pushed) { + // pop the pushed virtual nocache tag + $this->closeTag('nocache'); $compiler->tag_nocache = true; } - [$nesting, $compiler->nocache] = $this->closeTag($compiler, ['if', 'else', 'elseif']); + $tmp = ''; for ($i = 0; $i < $nesting; $i++) { $tmp .= '}'; diff --git a/src/Compile/Tag/IfTag.php b/src/Compile/Tag/IfTag.php index 1e30a1c7..a07877c3 100644 --- a/src/Compile/Tag/IfTag.php +++ b/src/Compile/Tag/IfTag.php @@ -23,11 +23,14 @@ class IfTag extends Base { * @throws \Smarty\CompilerException */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - // check and get attributes - $_attr = $this->getAttributes($compiler, $args); - $this->openTag($compiler, 'if', [1, $compiler->nocache]); - // must whole block be nocache ? - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + + if ($compiler->tag_nocache) { + // push a {nocache} tag onto the stack to prevent caching of this block + $this->openTag('nocache'); + } + + $this->openTag($compiler, 'if', [1, $compiler->tag_nocache]); + if (!isset($parameter['if condition'])) { $compiler->trigger_template_error('missing if condition', null, true); } @@ -37,7 +40,7 @@ class IfTag extends Base { } else { $var = $parameter['if condition']['var']; } - if ($compiler->nocache) { + if ($compiler->isNocacheActive()) { // create nocache var to make it know for further compiling $compiler->setNocacheInVariable($var); } diff --git a/src/Compile/Tag/IncludeTag.php b/src/Compile/Tag/IncludeTag.php index 47c716c8..99971c36 100644 --- a/src/Compile/Tag/IncludeTag.php +++ b/src/Compile/Tag/IncludeTag.php @@ -111,11 +111,13 @@ class IncludeTag extends Base { // assume caching is off $_caching = Smarty::CACHING_OFF; - $call_nocache = $compiler->tag_nocache || $compiler->nocache; + // caching was on and {include} is not in nocache mode - if ($compiler->getTemplate()->caching && !$compiler->nocache && !$compiler->tag_nocache) { + // @TODO see if we can do without this + if ($compiler->getTemplate()->caching && !$compiler->isNocacheActive()) { $_caching = \Smarty\Template::CACHING_NOCACHE_CODE; } + // flag if included template code should be merged into caller $merge_compiled_includes = ($compiler->getSmarty()->merge_compiled_includes || $_attr['inline'] === true) && !$compiler->getTemplate()->getSource()->handler->recompiled; @@ -125,12 +127,15 @@ class IncludeTag extends Base { $merge_compiled_includes = false; } } + /* * if the {include} tag provides individual parameter for caching or compile_id * the subtemplate must not be included into the common cache file and is treated like * a call in nocache mode. * */ + + $call_nocache = $compiler->isNocacheActive(); if ($_attr['nocache'] !== true && $_attr['caching']) { $_caching = $_new_caching = (int)$_attr['caching']; $call_nocache = true; @@ -161,7 +166,7 @@ class IncludeTag extends Base { // output will be stored in a smarty variable instead of being displayed if ($_assign = $compiler->getId($_attr['assign'])) { $_assign = "'{$_assign}'"; - if ($compiler->tag_nocache || $compiler->nocache || $call_nocache) { + if ($call_nocache) { // create nocache var to make it know for further compiling $compiler->setNocacheInVariable($_attr['assign']); } @@ -281,7 +286,7 @@ class IncludeTag extends Base { $compiled_code = "cStyleComment(" Start inline template \"{$sourceInfo}\" =============================") . "\n"; $compiled_code .= "function {$tpl->getCompiled()->unifunc} (\\Smarty\\Template \$_smarty_tpl) {\n"; - $compiled_code .= "?>\n" . $tpl->getCompiler()->compileTemplateSource($tpl, null, $compiler->getParentCompiler()); + $compiled_code .= "?>\n" . $tpl->getCompiler()->compileTemplateSource($tpl, $compiler->getParentCompiler()); $compiled_code .= "\n"; $compiled_code .= $tpl->getSmarty()->runPostFilters($tpl->getCompiler()->blockOrFunctionCode, $tpl); diff --git a/src/Compile/Tag/Nocache.php b/src/Compile/Tag/Nocache.php index 0f12f189..5e2cbd31 100644 --- a/src/Compile/Tag/Nocache.php +++ b/src/Compile/Tag/Nocache.php @@ -29,10 +29,7 @@ class Nocache extends Base { * @return bool */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - $this->getAttributes($compiler, $args); - $this->openTag($compiler, 'nocache', [$compiler->nocache]); - // enter nocache mode - $compiler->nocache = true; + $this->openTag($compiler, 'nocache'); // this tag does not return compiled code $compiler->has_code = false; return true; diff --git a/src/Compile/Tag/NocacheClose.php b/src/Compile/Tag/NocacheClose.php index c88c3334..cacbf701 100644 --- a/src/Compile/Tag/NocacheClose.php +++ b/src/Compile/Tag/NocacheClose.php @@ -30,9 +30,7 @@ class NocacheClose extends Base { * @return bool */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { - $_attr = $this->getAttributes($compiler, $args); - // leave nocache mode - [$compiler->nocache] = $this->closeTag($compiler, ['nocache']); + $this->closeTag($compiler, ['nocache']); // this tag does not return compiled code $compiler->has_code = false; return true; diff --git a/src/Compile/Tag/ParentTag.php b/src/Compile/Tag/ParentTag.php deleted file mode 100644 index a71ccdf0..00000000 --- a/src/Compile/Tag/ParentTag.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ -class ParentTag extends Child { - - /** - * Tag name - * - * @var string - */ - protected $tag = 'parent'; - - /** - * Block type - * - * @var string - */ - protected $blockType = 'Parent'; -} diff --git a/src/Compile/Tag/Section.php b/src/Compile/Tag/Section.php index f51cc3ff..de9202c5 100644 --- a/src/Compile/Tag/Section.php +++ b/src/Compile/Tag/Section.php @@ -95,9 +95,14 @@ class Section extends ForeachSection { } $local = "\$__section_{$attributes['name']}_" . $this->counter++ . '_'; $sectionVar = "\$_smarty_tpl->tpl_vars['__smarty_section_{$attributes['name']}']"; - $this->openTag($compiler, 'section', ['section', $compiler->nocache, $local, $sectionVar]); - // maybe nocache because of nocache variables - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + + if ($compiler->tag_nocache) { + // push a {nocache} tag onto the stack to prevent caching of this block + $this->openTag('nocache'); + } + + $this->openTag($compiler, 'section', ['section', $compiler->tag_nocache]); + $initLocal = []; $initNamedProperty = []; $initFor = []; diff --git a/src/Compile/Tag/SectionClose.php b/src/Compile/Tag/SectionClose.php index 4d8cafef..dee65bab 100644 --- a/src/Compile/Tag/SectionClose.php +++ b/src/Compile/Tag/SectionClose.php @@ -14,9 +14,6 @@ use Smarty\Compile\Base; /** * Smarty Internal Plugin Compile Sectionclose Class - * - - */ class SectionClose extends Base { @@ -30,12 +27,14 @@ class SectionClose extends Base { */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { $compiler->loopNesting--; - // must endblock be nocache? - if ($compiler->nocache) { - $compiler->tag_nocache = true; + + [$openTag, $nocache_pushed] = $this->closeTag($compiler, ['section', 'sectionelse']); + + if ($nocache_pushed) { + // pop the pushed virtual nocache tag + $this->closeTag('nocache'); } - [$openTag, $compiler->nocache, $local, $sectionVar] = - $this->closeTag($compiler, ['section', 'sectionelse']); + $output = "getAttributes($compiler, $args); - [$openTag, $nocache, $local, $sectionVar] = $this->closeTag($compiler, ['section']); - $this->openTag($compiler, 'sectionelse', ['sectionelse', $nocache, $local, $sectionVar]); + [$openTag, $nocache_pushed] = $this->closeTag($compiler, ['section']); + $this->openTag($compiler, 'sectionelse', ['sectionelse', $nocache_pushed]); return ""; } } \ No newline at end of file diff --git a/src/Compile/Tag/WhileClose.php b/src/Compile/Tag/WhileClose.php index 912170d4..fcb690ed 100644 --- a/src/Compile/Tag/WhileClose.php +++ b/src/Compile/Tag/WhileClose.php @@ -30,11 +30,15 @@ class WhileClose extends Base { */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { $compiler->loopNesting--; - // must endblock be nocache? - if ($compiler->nocache) { + + $nocache_pushed = $this->closeTag($compiler, ['while']); + + if ($nocache_pushed) { + // pop the pushed virtual nocache tag + $this->closeTag('nocache'); $compiler->tag_nocache = true; } - $compiler->nocache = $this->closeTag($compiler, ['while']); + return "\n"; } } diff --git a/src/Compile/Tag/WhileTag.php b/src/Compile/Tag/WhileTag.php index 680c3de2..8b7aac5f 100644 --- a/src/Compile/Tag/WhileTag.php +++ b/src/Compile/Tag/WhileTag.php @@ -24,16 +24,20 @@ class WhileTag extends Base { */ public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { $compiler->loopNesting++; - // check and get attributes - $_attr = $this->getAttributes($compiler, $args); - $this->openTag($compiler, 'while', $compiler->nocache); + + if ($compiler->tag_nocache) { + // push a {nocache} tag onto the stack to prevent caching of this block + $this->openTag('nocache'); + } + + $this->openTag($compiler, 'while', $compiler->tag_nocache); + if (!array_key_exists('if condition', $parameter)) { $compiler->trigger_template_error('missing while condition', null, true); } - // maybe nocache because of nocache variables - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + if (is_array($parameter['if condition'])) { - if ($compiler->nocache) { + if ($compiler->isNocacheActive()) { // create nocache var to make it know for further compiling if (is_array($parameter['if condition']['var'])) { $var = $parameter['if condition']['var']['var']; diff --git a/src/Compiler/Template.php b/src/Compiler/Template.php index 32a5db3a..0c8a49b5 100644 --- a/src/Compiler/Template.php +++ b/src/Compiler/Template.php @@ -17,6 +17,7 @@ use Smarty\Compile\ModifierCompiler; use Smarty\Compile\ObjectMethodBlockCompiler; use Smarty\Compile\ObjectMethodCallCompiler; use Smarty\Compile\FunctionCallCompiler; +use Smarty\Compile\PrintExpressionCompiler; use Smarty\Lexer\TemplateLexer; use Smarty\Parser\TemplateParser; use Smarty\Smarty; @@ -76,14 +77,14 @@ class Template extends BaseCompiler { * * @var array */ - public $_tag_stack = []; + private $_tag_stack = []; /** * tag stack count * * @var array */ - public $_tag_stack_count = []; + private $_tag_stack_count = []; /** * current template @@ -308,6 +309,17 @@ class Template extends BaseCompiler { * @var ModifierCompiler */ private $modifierCompiler; + /** + * @var PrintExpressionCompiler + */ + private $printExpressionCompiler; + + /** + * Depth of nested {nocache}{/nocache} blocks. If outside, this is 0. If inside, this is 1 or higher (if nested). + * @var int + */ + private $noCacheStackDepth = 0; + /** * Initialize compiler @@ -332,53 +344,38 @@ class Template extends BaseCompiler { $this->defaultHandlerBlockCompiler = new DefaultHandlerBlockCompiler(); $this->objectMethodBlockCompiler = new ObjectMethodBlockCompiler(); $this->objectMethodCallCompiler = new ObjectMethodCallCompiler(); + $this->printExpressionCompiler = new PrintExpressionCompiler(); } /** * Method to compile a Smarty template * - * @param Smarty\Template $template template object to compile - * @param null $nocache true is shall be compiled in nocache mode - * @param null|Template $parent_compiler + * @param \Smarty\Template $template template object to compile * * @return bool true if compiling succeeded, false if it failed * @throws Exception */ - public function compileTemplate( - \Smarty\Template $template, - $nocache = null, - \Smarty\Compiler\Template $parent_compiler = null - ) { - // get code frame of compiled template - $_compiled_code = $template->createCodeFrame( - $this->compileTemplateSource( - $template, - $nocache, - $parent_compiler - ), + public function compileTemplate(\Smarty\Template $template) { + return $template->createCodeFrame( + $this->compileTemplateSource($template), $this->smarty->runPostFilters($this->blockOrFunctionCode, $this->template) . join('', $this->mergedSubTemplatesCode), false, $this ); - return $_compiled_code; } /** * Compile template source and run optional post filter * * @param \Smarty\Template $template - * @param null|bool $nocache flag if template must be compiled in nocache mode - * @param \Smarty\Compiler\Template $parent_compiler + * @param Template|null $parent_compiler * * @return string - * @throws \Exception + * @throws CompilerException + * @throws Exception */ - public function compileTemplateSource( - \Smarty\Template $template, - $nocache = null, - \Smarty\Compiler\Template $parent_compiler = null - ) { + public function compileTemplateSource(\Smarty\Template $template, \Smarty\Compiler\Template $parent_compiler = null) { try { // save template object in compiler class $this->template = $template; @@ -386,18 +383,20 @@ class Template extends BaseCompiler { $this->smarty->getDebug()->start_compile($this->template); } $this->parent_compiler = $parent_compiler ? $parent_compiler : $this; - $nocache = isset($nocache) ? $nocache : false; + if (empty($template->getCompiled()->nocache_hash)) { $template->getCompiled()->nocache_hash = $this->nocache_hash; } else { $this->nocache_hash = $template->getCompiled()->nocache_hash; } $this->caching = $template->caching; + // flag for nocache sections - $this->nocache = $nocache; + $this->nocache = false; $this->tag_nocache = false; // reset has nocache code flag $this->template->getCompiled()->setNocacheCode(false); + $this->has_variable_string = false; $this->prefix_code = []; // add file dependency @@ -645,8 +644,8 @@ class Template extends BaseCompiler { if (!is_callable($defaultPluginHandlerFunc)) { return null; } - - + + $callback = null; $script = null; $cacheable = true; @@ -715,8 +714,7 @@ class Template extends BaseCompiler { if (!empty($content) && !($this->template->getSource()->handler->recompiled) && $this->caching - && !$this->suppressNocacheProcessing - && ($this->nocache || $this->tag_nocache) + && $this->isNocacheActive() ) { $this->template->getCompiled()->setNocacheCode(true); $_output = addcslashes($content, '\'\\'); @@ -1075,21 +1073,14 @@ class Template extends BaseCompiler { return '/*' . str_replace('*/', '* /', $string) . '*/'; } - private function argsContainNocache(array $args): bool { - foreach ($args as $arg) { - if (!is_array($arg)) { - if ($arg === "'nocache'" || $arg === 'nocache') { - return true; - } - } else { - foreach ($arg as $k => $v) { - if (($k === "'nocache'" || $k === 'nocache') && (trim($v, "'\" ") === 'true')) { - return true; - } - } - } - } - return false; + public function compileChildBlock() { + $this->has_code = true; + return $this->blockCompiler->compileChild($this); + } + + public function compileParentBlock() { + $this->has_code = true; + return $this->blockCompiler->compileParent($this); } /** @@ -1108,17 +1099,16 @@ class Template extends BaseCompiler { // assume that tag does compile into code, but creates no HTML output $this->has_code = true; - // check nocache option flag - if ($this->argsContainNocache($args)) { - $this->tag_nocache = true; - } + $this->handleNocacheFlag($args); // compile built-in tags if ($tagCompiler = $this->getTagCompiler($tag)) { if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) { $this->tag_nocache = !$tagCompiler->isCacheable(); $_output = $tagCompiler->compile($args, $this, $parameter); - return $this->has_code ? $_output : null; + if ($_output !== false) { + return $this->has_code && $_output !== true ? $_output : null; + } } } @@ -1190,6 +1180,21 @@ class Template extends BaseCompiler { $this->trigger_template_error("unknown tag '{$tag}'", null, true); } + /** + * Sets $this->tag_nocache if attributes contain the 'nocache' flag. + * + * @param array $attributes + * + * @return void + */ + private function handleNocacheFlag(array $attributes) { + foreach ($attributes as $value) { + if (is_string($value) && trim($value, '\'" ') == 'nocache') { + $this->tag_nocache = true; + } + } + } + private function getBaseTag($tag) { if (strlen($tag) < 6 || substr($tag, -5) !== 'close') { return $tag; @@ -1198,6 +1203,24 @@ class Template extends BaseCompiler { } } + /** + * Compiles the output of a variable or expression. + * + * @param $value + * @param $attributes + * @param $modifiers + * + * @return string + * @throws Exception + */ + public function compilePrintExpression($value, $attributes = [], $modifiers = null) { + $this->handleNocacheFlag($attributes); + return $this->printExpressionCompiler->compile($attributes, $this, [ + 'value'=> $value, + 'modifierlist' => $modifiers, + ]); + } + /** * method to compile a Smarty template * @@ -1256,7 +1279,7 @@ class Template extends BaseCompiler { mb_internal_encoding($mbEncoding); } // check for unclosed tags - if (count($this->_tag_stack) > 0) { + if ($this->getTagStackCount() > 0) { // get stacked info [$openTag, $_data] = array_pop($this->_tag_stack); $this->trigger_template_error( @@ -1405,4 +1428,72 @@ class Template extends BaseCompiler { $this->parent_compiler = $parent_compiler; } + + /** + * Push opening tag name on stack + * Optionally additional data can be saved on stack + * + * @param string $openTag the opening tag's name + * @param mixed $data optional data saved + */ + public function openTag($openTag, $data = null) { + $this->_tag_stack[] = [$openTag, $data]; + if ($openTag == 'nocache') { + $this->noCacheStackDepth++; + } + } + + /** + * Pop closing tag + * Raise an error if this stack-top doesn't match with expected opening tags + * + * @param array|string $expectedTag the expected opening tag names + * + * @return mixed any type the opening tag's name or saved data + * @throws CompilerException + */ + public function closeTag($expectedTag) { + if ($this->getTagStackCount() > 0) { + // get stacked info + [$_openTag, $_data] = array_pop($this->_tag_stack); + // open tag must match with the expected ones + if (in_array($_openTag, (array)$expectedTag)) { + + if ($_openTag == 'nocache') { + $this->noCacheStackDepth--; + } + + if (is_null($_data)) { + // return opening tag + return $_openTag; + } else { + // return restored data + return $_data; + } + } + // wrong nesting of tags + $this->trigger_template_error("unclosed '" . $this->getTemplate()->getLeftDelimiter() . "{$_openTag}" . + $this->getTemplate()->getRightDelimiter() . "' tag"); + return; + } + // wrong nesting of tags + $this->trigger_template_error('unexpected closing tag', null, true); + } + + /** + * Returns true if we are in a {nocache}...{/nocache} block, but false if inside {block} tag inside a {nocache} block... + * @return bool + */ + public function isNocacheActive(): bool { + return !$this->suppressNocacheProcessing && ($this->noCacheStackDepth > 0 || $this->tag_nocache); + } + + /** + * Returns the full tag stack, used in the compiler for {break} + * @return array + */ + public function getTagStack(): array { + return $this->_tag_stack; + } + } diff --git a/src/Extension/CoreExtension.php b/src/Extension/CoreExtension.php index e5b4663b..a7c658d3 100644 --- a/src/Extension/CoreExtension.php +++ b/src/Extension/CoreExtension.php @@ -13,9 +13,6 @@ class CoreExtension extends Base { case 'call': return new \Smarty\Compile\Tag\Call(); case 'capture': return new \Smarty\Compile\Tag\Capture(); case 'captureclose': return new \Smarty\Compile\Tag\CaptureClose(); - case 'child': return new \Smarty\Compile\Tag\Child(); - case 'block_child': return new \Smarty\Compile\Tag\BlockChild(); - case 'block_parent': return new \Smarty\Compile\Tag\BlockParent(); case 'config_load': return new \Smarty\Compile\Tag\ConfigLoad(); case 'continue': return new \Smarty\Compile\Tag\ContinueTag(); case 'debug': return new \Smarty\Compile\Tag\Debug(); @@ -38,7 +35,6 @@ class CoreExtension extends Base { case 'rdelim': return new \Smarty\Compile\Tag\Rdelim(); case 'nocache': return new \Smarty\Compile\Tag\Nocache(); case 'nocacheclose': return new \Smarty\Compile\Tag\NocacheClose(); - case 'parent': return new \Smarty\Compile\Tag\ParentTag(); case 'section': return new \Smarty\Compile\Tag\Section(); case 'sectionelse': return new \Smarty\Compile\Tag\SectionElse(); case 'sectionclose': return new \Smarty\Compile\Tag\SectionClose(); diff --git a/src/ParseTree/Dq.php b/src/ParseTree/Dq.php index c8db13fa..27c3db9f 100644 --- a/src/ParseTree/Dq.php +++ b/src/ParseTree/Dq.php @@ -28,7 +28,7 @@ class Dq extends Base { $this->subtrees[] = $subtree; if ($subtree instanceof Tag) { - $parser->block_nesting_level = count($parser->compiler->_tag_stack); + $parser->block_nesting_level = $parser->compiler->getTagStackCount(); } } @@ -64,7 +64,7 @@ class Dq extends Base $this->subtrees[] = $subtree; } if ($subtree instanceof Tag) { - $parser->block_nesting_level = count($parser->compiler->_tag_stack); + $parser->block_nesting_level = $parser->compiler->getTagStackCount(); } } diff --git a/src/ParseTree/Tag.php b/src/ParseTree/Tag.php index 9c52044a..f4bdee25 100644 --- a/src/ParseTree/Tag.php +++ b/src/ParseTree/Tag.php @@ -64,7 +64,7 @@ class Tag extends Base $var = $parser->compiler->getNewPrefixVariable(); $tmp = $parser->compiler->appendCode('', $this->data); $tmp = $parser->compiler->appendCode($tmp, ""); - $parser->compiler->prefix_code[] = sprintf('%s', $tmp); + $parser->compiler->appendPrefixCode((string) $tmp); return $var; } } diff --git a/src/Parser/TemplateParser.y b/src/Parser/TemplateParser.y index 9caa595b..9ea9bde4 100644 --- a/src/Parser/TemplateParser.y +++ b/src/Parser/TemplateParser.y @@ -162,8 +162,8 @@ class TemplateParser { $this->lex = $lex; $this->compiler = $compiler; - $this->template = $this->compiler->template; - $this->smarty = $this->template->smarty; + $this->template = $this->compiler->getTemplate(); + $this->smarty = $this->template->getSmarty(); $this->security = $this->smarty->security_policy ?? false; $this->current_buffer = $this->root_buffer = new TemplateParseTree(); } @@ -207,7 +207,7 @@ class TemplateParser } $this->compiler->prefix_code = array(); $tmp .= $code; - return new Tag($this, $this->compiler->processNocacheCode($tmp, true)); + return new Tag($this, $this->compiler->processNocacheCode($tmp)); } } @@ -294,7 +294,7 @@ template ::= template smartytag(B). { $this->current_buffer->append_subtree($this, $this->mergePrefixCode(B)); } $this->compiler->has_variable_string = false; - $this->block_nesting_level = count($this->compiler->_tag_stack); + $this->block_nesting_level = $this->compiler->getTagStackCount(); } @@ -303,11 +303,12 @@ template ::= . smartytag(A) ::= SIMPELOUTPUT(B). { $var = trim(substr(B, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); + $attributes = []; if (preg_match('/^(.*)(\s+nocache)$/', $var, $match)) { - A = (new \Smarty\Compile\PrintExpressionCompiler())->compile(array('nocache'),$this->compiler,array('value'=>$this->compiler->compileVariable('\''.$match[1].'\''))); - } else { - A = (new \Smarty\Compile\PrintExpressionCompiler())->compile(array(),$this->compiler,array('value'=>$this->compiler->compileVariable('\''.$var.'\''))); + $attributes[] = 'nocache'; + $var = $match[1]; } + A = $this->compiler->compilePrintExpression($this->compiler->compileVariable('\''.$var.'\''), $attributes); } // simple tag like {name} @@ -321,7 +322,7 @@ smartytag(A)::= SIMPLETAG(B). { if ($this->security) { $this->security->isTrustedConstant($tag, $this->compiler); } - A = (new \Smarty\Compile\PrintExpressionCompiler())->compile(array(),$this->compiler,array('value'=>$tag)); + A = $this->compiler->compilePrintExpression($tag); } else { if (preg_match('/^(.*)(\s+nocache)$/', $tag, $match)) { A = $this->compiler->compileTag($match[1],array('\'nocache\'')); @@ -336,10 +337,10 @@ smartytag(A) ::= SMARTYBLOCKCHILDPARENT(i). { $j = strrpos(i,'.'); if (i[$j+1] == 'c') { // {$smarty.block.child} - A = $this->compiler->compileTag('child',array(),array(i)); + A = $this->compiler->compileChildBlock(); } else { // {$smarty.block.parent} - A = $this->compiler->compileTag('parent',array(),array(i)); + A = $this->compiler->compileParentBlock(); } } @@ -352,7 +353,7 @@ smartytag(A) ::= LDEL tagbody(B) RDEL. { } // output with optional attributes tagbody(A) ::= outattr(B). { - A = (new \Smarty\Compile\PrintExpressionCompiler())->compile(B[1],$this->compiler,array('value'=>B[0])); + A = $this->compiler->compilePrintExpression(B[0], B[1]); } // @@ -392,7 +393,7 @@ tag(res) ::= LDEL ID(i) attributes(a). { if ($this->security) { $this->security->isTrustedConstant(i, $this->compiler); } - res = (new \Smarty\Compile\PrintExpressionCompiler())->compile(a,$this->compiler,array('value'=>i)); + res = $this->compiler->compilePrintExpression(i, a); } else { res = $this->compiler->compileTag(i,a); } @@ -402,7 +403,7 @@ tag(res) ::= LDEL ID(i). { if ($this->security) { $this->security->isTrustedConstant(i, $this->compiler); } - res = (new \Smarty\Compile\PrintExpressionCompiler())->compile(array(),$this->compiler,array('value'=>i)); + res = $this->compiler->compilePrintExpression(i); } else { res = $this->compiler->compileTag(i,array()); } @@ -415,7 +416,7 @@ tag(res) ::= LDEL ID(i) modifierlist(l)attributes(a). { if ($this->security) { $this->security->isTrustedConstant(i, $this->compiler); } - res = (new \Smarty\Compile\PrintExpressionCompiler())->compile(a,$this->compiler,array('value'=>i, 'modifierlist'=>l)); + res = $this->compiler->compilePrintExpression(i, a, l); } else { res = $this->compiler->compileTag(i,a, array('modifierlist'=>l)); } diff --git a/src/Runtime/InheritanceRuntime.php b/src/Runtime/InheritanceRuntime.php index b0ca670d..1d4f0218 100644 --- a/src/Runtime/InheritanceRuntime.php +++ b/src/Runtime/InheritanceRuntime.php @@ -222,11 +222,11 @@ class InheritanceRuntime { * @return null|string block content * @throws Exception */ - public function callParent(Template $tpl, \Smarty\Runtime\Block $block, $tag) { + public function callParent(Template $tpl, \Smarty\Runtime\Block $block) { if (isset($block->parent)) { $this->callBlock($block->parent, $tpl); } else { - throw new Exception("inheritance: illegal '{$tag}' used in child template '" . + throw new Exception("inheritance: illegal '{\$smarty.block.parent}' used in child template '" . "{$tpl->getInheritance()->sources[$block->tplIndex]->filepath}' block '{$block->name}'"); } } diff --git a/tests/UnitTests/TemplateSource/TagTests/BockExtend/CompileBlockExtendsTest.php b/tests/UnitTests/TemplateSource/TagTests/BockExtend/CompileBlockExtendsTest.php index 595afeae..c6a0699e 100644 --- a/tests/UnitTests/TemplateSource/TagTests/BockExtend/CompileBlockExtendsTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/BockExtend/CompileBlockExtendsTest.php @@ -1343,7 +1343,7 @@ class CompileBlockExtendsTest extends PHPUnit_Smarty "blockNocache - {$file}"); } /* - * Data provider für TestBlockNocache + * Data provider for TestBlockNocache */ public function dataTestBlockNocache() {