diff --git a/src/Compile/Tag/Assign.php b/src/Compile/Tag/Assign.php index e4c6edfe..f1cf1385 100644 --- a/src/Compile/Tag/Assign.php +++ b/src/Compile/Tag/Assign.php @@ -65,7 +65,7 @@ class Assign extends Base if ($_attr[ 'noscope' ]) { $_scope = -1; } else { - $_scope = isset($_attr['scope']) ? $this->convertScope($_attr['scope']) : 0; + $_scope = isset($_attr['scope']) ? $this->convertScope($_attr['scope']) : null; } // optional parameter $_params = ''; @@ -80,9 +80,9 @@ class Assign extends Base $output .= "settype(\$_tmp_array, 'array');\n"; $output .= "}\n"; $output .= "\$_tmp_array{$parameter['smarty_internal_index']} = {$_attr['value']};\n"; - $output .= "\$_smarty_tpl->assign({$_var}, \$_tmp_array{$_params}, false, {$_scope});?>"; + $output .= "\$_smarty_tpl->assign({$_var}, \$_tmp_array{$_params}, false, " . var_export($_scope, true) . ");?>"; } else { - $output = "assign({$_var}, {$_attr['value']}{$_params}, false, {$_scope});?>"; + $output = "assign({$_var}, {$_attr['value']}{$_params}, false, " . var_export($_scope, true) . ");?>"; } return $output; } diff --git a/src/Compile/Tag/IncludeTag.php b/src/Compile/Tag/IncludeTag.php index 6d2bcd52..47c716c8 100644 --- a/src/Compile/Tag/IncludeTag.php +++ b/src/Compile/Tag/IncludeTag.php @@ -217,7 +217,9 @@ class IncludeTag extends Base { if (isset($_assign)) { $_output .= "ob_start();\n"; } - $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, {$_cache_id}, \$_smarty_tpl->compile_id, {$_caching}, {$_cache_lifetime}, {$_vars}, '{$compiler->getParentCompiler()->mergedSubTemplatesData[$uid][$t_hash]['uid']}', '{$compiler->getParentCompiler()->mergedSubTemplatesData[$uid][$t_hash]['func']}');\n"; + $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, {$_cache_id}, \$_smarty_tpl->compile_id, + {$_caching}, {$_cache_lifetime}, {$_vars}, '{$compiler->getParentCompiler()->mergedSubTemplatesData[$uid][$t_hash]['uid']}', + '{$compiler->getParentCompiler()->mergedSubTemplatesData[$uid][$t_hash]['func']}', (int) {$_scope});\n"; if (isset($_assign)) { $_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean(), false, {$_scope});\n"; } @@ -232,7 +234,7 @@ class IncludeTag extends Base { if (isset($_assign)) { $_output .= "ob_start();\n"; } - $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, $_cache_id, \$_smarty_tpl->compile_id, $_caching, $_cache_lifetime, $_vars);\n"; + $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, $_cache_id, \$_smarty_tpl->compile_id, $_caching, $_cache_lifetime, $_vars, null, null, (int) {$_scope});\n"; if (isset($_assign)) { $_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean(), false, {$_scope});\n"; } diff --git a/src/Compiler/CodeFrame.php b/src/Compiler/CodeFrame.php index 38dac6d8..b371e58d 100644 --- a/src/Compiler/CodeFrame.php +++ b/src/Compiler/CodeFrame.php @@ -72,8 +72,10 @@ class CodeFrame $output .= ");\n"; } if ($cache && $this->_template->getSmarty()->hasRuntime('TplFunction')) { - $output .= "\$_smarty_tpl->getSmarty()->getRuntime('TplFunction')->registerTplFunctions(\$_smarty_tpl, " . - var_export($this->_template->getSmarty()->getRuntime('TplFunction')->getTplFunction($this->_template), true) . ");\n"; + if ($tplfunctions = $this->_template->getSmarty()->getRuntime('TplFunction')->getTplFunction($this->_template)) { + $output .= "\$_smarty_tpl->getSmarty()->getRuntime('TplFunction')->registerTplFunctions(\$_smarty_tpl, " . + var_export($tplfunctions, true) . ");\n"; + } } $output .= "?>"; $output .= $content; diff --git a/src/Data.php b/src/Data.php index 4b44969a..94ed9f74 100644 --- a/src/Data.php +++ b/src/Data.php @@ -47,6 +47,12 @@ class Data */ public $config_vars = array(); + /** + * Default scope for new variables + * @var int + */ + private $defaultScope = self::SCOPE_LOCAL; + /** * create Smarty data object * @@ -83,7 +89,7 @@ class Data * @return Data current Data (or Smarty or \Smarty\Template) instance for * chaining */ - public function assign($tpl_var, $value = null, $nocache = false, $scope = 0) + public function assign($tpl_var, $value = null, $nocache = false, $scope = null) { if (is_array($tpl_var)) { foreach ($tpl_var as $_key => $_val) { @@ -91,8 +97,7 @@ class Data } return; } - - switch ($scope) { + switch ($scope ?? $this->getDefaultScope()) { case self::SCOPE_GLOBAL: case self::SCOPE_SMARTY: $this->getSmarty()->assign($tpl_var, $value); @@ -119,6 +124,7 @@ class Data $this->assign($tpl_var, $value); } break; + case self::SCOPE_LOCAL: default: $this->tpl_vars[ $tpl_var ] = new Variable($value, $nocache); } @@ -439,4 +445,21 @@ class Data return $this; } + /** + * Sets the default scope for new variables assigned in this template. + * @param int $scope + * + * @return void + */ + protected function setDefaultScope(int $scope) { + $this->defaultScope = $scope; + } + + /** + * Returns the default scope for new variables assigned in this template. + * @return int + */ + public function getDefaultScope(): int { + return $this->defaultScope; + } } diff --git a/src/Template.php b/src/Template.php index e1316cfe..467be43e 100644 --- a/src/Template.php +++ b/src/Template.php @@ -105,6 +105,7 @@ class Template extends TemplateBase { */ private $right_delimiter = null; + /** * Create template data object * Some of the global Smarty settings copied to template scope @@ -233,13 +234,13 @@ class Template extends TemplateBase { * @param mixed $cache_id cache id * @param mixed $compile_id compile id * @param integer $caching cache mode - * @param integer $cache_lifetime life time of cache data + * @param integer $cache_lifetime lifetime of cache data * @param array $extra_vars passed parameter template variables - * @param string $uid file dependency uid - * @param string $content_func function name + * @param null $uid file dependency uid + * @param null $content_func function name + * @param int|null $scope * - * @throws \Exception - * @throws \Smarty\Exception + * @throws Exception */ public function _subTemplateRender( $template_name, @@ -249,12 +250,18 @@ class Template extends TemplateBase { $cache_lifetime, array $extra_vars, $uid = null, - $content_func = null + $content_func = null, + int $scope = null ) { $baseFilePath = $this->source && $this->getSource()->filepath ? dirname($this->getSource()->filepath) : null; $tpl = $this->getSmarty()->createTemplate($template_name, $cache_id, $compile_id, $this, $caching, $cache_lifetime, $baseFilePath); + $tpl->setCached($this->getCached()); // re-use the same Cache object across subtemplates to gather hashes and file dependencies. + + if ($scope) { + $tpl->setDefaultScope($scope); + } // copy variables $tpl->tpl_vars = $this->tpl_vars; @@ -313,9 +320,6 @@ class Template extends TemplateBase { // $tpl->render(); // } } - - // Merge the hashes... @TODO refactor this? - $this->getCached()->hashes = array_merge($this->getCached()->hashes, $tpl->getCached()->hashes); } /** @@ -327,7 +331,7 @@ class Template extends TemplateBase { return isset($this->parent) && $this->parent instanceof Template; } - public function assign($tpl_var, $value = null, $nocache = false, $scope = 0) { + public function assign($tpl_var, $value = null, $nocache = false, $scope = null) { return parent::assign($tpl_var, $value, $nocache || $this->isRenderingCache, $scope); } @@ -810,4 +814,16 @@ class Template extends TemplateBase { public function setSource($source): void { $this->source = $source; } + + /** + * Sets the Cached object, so subtemplates can share one Cached object to gather meta-data. + * + * @param Cached $cached + * + * @return void + */ + private function setCached(Cached $cached) { + $this->cached = $cached; + } + } diff --git a/tests/UnitTests/ResourceTests/Extends/ExtendsResourceTest.php b/tests/UnitTests/ResourceTests/Extends/ExtendsResourceTest.php index 8b0f1cd9..6368ffef 100644 --- a/tests/UnitTests/ResourceTests/Extends/ExtendsResourceTest.php +++ b/tests/UnitTests/ResourceTests/Extends/ExtendsResourceTest.php @@ -89,6 +89,23 @@ class ExtendsResourceTest extends PHPUnit_Smarty $this->assertStringContainsString("test:{$testNumber} compiled:{$compileTestNumber} rendered:{$renderTestNumber}", $result, $testName . ' - fetch() failure'); } + /** + * @dataProvider data + */ + public function testCompileBlockIncreaseInChild_050($caching, $merge, $testNumber, $compileTestNumber, $renderTestNumber, $testName) + { + $this->smarty->registerFilter('pre', array($this, 'compiledPrefilter')); + $this->smarty->assign('test', $testNumber); + $this->smarty->caching = $caching; + $this->smarty->merge_compiled_includes = $merge; + if ($merge) { + $this->smarty->compile_id = 1; + } + $result = $this->smarty->fetch('extends:050_parent.tpl|050_child.tpl|050_grandchild.tpl'); + $this->assertStringContainsString("var-bar-var", $result, $testName . ' - content'); + $this->assertStringContainsString("test:{$testNumber} compiled:{$compileTestNumber} rendered:{$renderTestNumber}", $result, $testName . ' - fetch() failure'); + } + /** * test grandchild/child/parent dependency test1 */ diff --git a/tests/UnitTests/ResourceTests/Extends/templates/050_child.tpl b/tests/UnitTests/ResourceTests/Extends/templates/050_child.tpl new file mode 100644 index 00000000..887c4ad5 --- /dev/null +++ b/tests/UnitTests/ResourceTests/Extends/templates/050_child.tpl @@ -0,0 +1 @@ +{$notfoo="notbar"} diff --git a/tests/UnitTests/ResourceTests/Extends/templates/050_grandchild.tpl b/tests/UnitTests/ResourceTests/Extends/templates/050_grandchild.tpl new file mode 100644 index 00000000..2f8199de --- /dev/null +++ b/tests/UnitTests/ResourceTests/Extends/templates/050_grandchild.tpl @@ -0,0 +1 @@ +{$foo="bar"} diff --git a/tests/UnitTests/ResourceTests/Extends/templates/050_parent.tpl b/tests/UnitTests/ResourceTests/Extends/templates/050_parent.tpl new file mode 100644 index 00000000..85d2795a --- /dev/null +++ b/tests/UnitTests/ResourceTests/Extends/templates/050_parent.tpl @@ -0,0 +1,2 @@ +test:{$test nocache} compiled:# rendered:{$test} +{block name='test'}var-{$foo}-var{/block} diff --git a/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php b/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php index 1082b6d7..62a4e674 100644 --- a/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php +++ b/tests/UnitTests/__shared/PHPunitplugins/function.checkvar.php @@ -36,7 +36,7 @@ function smarty_function_checkvar($params, \Smarty\Template $template) $i ++; } $ptr = $ptr->parent; - } elseif (in_array('data', $types) && !($ptr instanceof Template || $ptr instanceof Smarty)) { + } elseif (in_array('data', $types) && !($ptr instanceof Template || $ptr instanceof \Smarty\Smarty)) { $output .= "#data:\${$var} ="; $output .= $ptr->hasVariable($var) ? preg_replace('/\s/', '', var_export($ptr->getValue($var), true)) : '>unassigned<'; $ptr = $ptr->parent;