From 51075e04214050c562548dde636e25f064a7639e Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Tue, 10 Jan 2023 23:49:33 +0100 Subject: [PATCH] WIP fixing the complicated variables scopes architecture. Right now more tests are failing than before... :( --- src/Compile/Base.php | 36 ++++ src/Compile/Tag/Assign.php | 23 +-- src/Compile/Tag/ConfigLoad.php | 15 +- src/Compile/Tag/IncludeTag.php | 31 +-- src/Compiler/Configfile.php | 2 +- src/Compiler/Template.php | 27 --- src/Data.php | 166 ++++++++-------- src/DataObject.php | 1 - src/Debug.php | 5 +- src/Runtime/InheritanceRuntime.php | 1 - src/Smarty.php | 11 +- src/Template.php | 179 +----------------- src/Template/Config.php | 7 - src/TemplateBase.php | 11 +- .../UndefinedTemplateVarTest.php | 26 +-- .../AppendByRef/AppendByRefTest.php | 62 ------ .../AppendByRef/cache/.gitignore | 2 - .../AppendByRef/templates_c/.gitignore | 2 - .../AssignByRef/AssignByRefTest.php | 33 ---- .../AssignByRef/cache/.gitignore | 2 - .../AssignByRef/templates_c/.gitignore | 2 - .../TemplateSource/X_Scopes/ScopeTest.php | 9 +- 22 files changed, 160 insertions(+), 493 deletions(-) delete mode 100644 tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php delete mode 100644 tests/UnitTests/SmartyMethodsTests/AppendByRef/cache/.gitignore delete mode 100644 tests/UnitTests/SmartyMethodsTests/AppendByRef/templates_c/.gitignore delete mode 100644 tests/UnitTests/SmartyMethodsTests/AssignByRef/AssignByRefTest.php delete mode 100644 tests/UnitTests/SmartyMethodsTests/AssignByRef/cache/.gitignore delete mode 100644 tests/UnitTests/SmartyMethodsTests/AssignByRef/templates_c/.gitignore diff --git a/src/Compile/Base.php b/src/Compile/Base.php index 2069b9e5..0c216c78 100644 --- a/src/Compile/Base.php +++ b/src/Compile/Base.php @@ -5,6 +5,9 @@ */ namespace Smarty\Compile; +use Smarty\Data; +use Smarty\Exception; + /** * This class does extend all internal compile plugins * @@ -225,6 +228,39 @@ abstract class Base implements CompilerInterface { $compiler->trigger_template_error('unexpected closing tag', null, true); } + /** + * @param array $_attr tag attributes + * @param array $invalidScopes + * + * @return int + * @throws Exception + */ + protected function convertScope($_attr, $invalidScopes = []): int { + + static $scopes = [ + 'local' => Data::SCOPE_LOCAL, 'parent' => Data::SCOPE_PARENT, + 'root' => Data::SCOPE_ROOT, 'global' => Data::SCOPE_GLOBAL, + 'tpl_root' => Data::SCOPE_TPL_ROOT, 'smarty' => Data::SCOPE_SMARTY + ]; + + if (!isset($_attr['scope'])) { + return 0; + } + + $_scopeName = trim($_attr['scope'], '\'"'); + if (is_numeric($_scopeName) && in_array($_scopeName, $scopes)) { + return (int) $_scopeName; + } + + $_scopeName = trim($_scopeName, '\'"'); + if (isset($scopes[$_scopeName])) { + return $scopes[$_scopeName]; + } + + $err = var_export($_scopeName, true); + throw new Exception("illegal value '{$err}' for \"scope\" attribute"); + } + /** * Compiles code for the tag * diff --git a/src/Compile/Tag/Assign.php b/src/Compile/Tag/Assign.php index 02589063..57301a3d 100644 --- a/src/Compile/Tag/Assign.php +++ b/src/Compile/Tag/Assign.php @@ -30,17 +30,6 @@ class Assign extends Base */ protected $option_flags = array('nocache', 'noscope'); - /** - * Valid scope names - * - * @var array - */ - protected $valid_scopes = array( - 'local' => Smarty::SCOPE_LOCAL, 'parent' => Smarty::SCOPE_PARENT, - 'root' => Smarty::SCOPE_ROOT, 'global' => Smarty::SCOPE_GLOBAL, - 'tpl_root' => Smarty::SCOPE_TPL_ROOT, 'smarty' => Smarty::SCOPE_SMARTY - ); - /** * Compiles code for the {assign} tag * @@ -76,16 +65,14 @@ class Assign extends Base if ($_attr[ 'noscope' ]) { $_scope = -1; } else { - $_scope = $compiler->convertScope($_attr, $this->valid_scopes); + $_scope = $this->convertScope($_attr); } // optional parameter $_params = ''; - if ($_nocache || $_scope) { + if ($_nocache) { $_params .= ' ,' . var_export($_nocache, true); } - if ($_scope) { - $_params .= ' ,' . $_scope; - } + if (isset($parameter[ 'smarty_internal_index' ])) { $output = "getValue({$_var}) ?? [];\n"; @@ -93,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->_assignInScope({$_var}, \$_tmp_array{$_params});?>"; + $output .= "\$_smarty_tpl->assign({$_var}, \$_tmp_array{$_params}, false, {$_scope});?>"; } else { - $output = "_assignInScope({$_var}, {$_attr['value']}{$_params});?>"; + $output = "assign({$_var}, {$_attr['value']}{$_params}, false, {$_scope});?>"; } return $output; } diff --git a/src/Compile/Tag/ConfigLoad.php b/src/Compile/Tag/ConfigLoad.php index dc68bbdd..cc38a427 100644 --- a/src/Compile/Tag/ConfigLoad.php +++ b/src/Compile/Tag/ConfigLoad.php @@ -53,17 +53,6 @@ class ConfigLoad extends Base { */ protected $option_flags = ['nocache', 'noscope']; - /** - * Valid scope names - * - * @var array - */ - protected $valid_scopes = [ - 'local' => Smarty::SCOPE_LOCAL, 'parent' => Smarty::SCOPE_PARENT, - 'root' => Smarty::SCOPE_ROOT, 'tpl_root' => Smarty::SCOPE_TPL_ROOT, - 'smarty' => Smarty::SCOPE_SMARTY, 'global' => Smarty::SCOPE_SMARTY, - ]; - /** * Compiles code for the {config_load} tag * @@ -86,9 +75,9 @@ class ConfigLoad extends Base { if ($_attr['noscope']) { $_scope = -1; } else { - $_scope = $compiler->convertScope($_attr, $this->valid_scopes); + $_scope = $this->convertScope($_attr); } // create config object - return "_loadConfigfile({$conf_file}, {$section}, {$_scope});\n?>\n"; + return "_loadConfigfile({$conf_file}, {$section});\n?>\n"; } } diff --git a/src/Compile/Tag/IncludeTag.php b/src/Compile/Tag/IncludeTag.php index 4fc18a21..eee6bc5b 100644 --- a/src/Compile/Tag/IncludeTag.php +++ b/src/Compile/Tag/IncludeTag.php @@ -12,6 +12,7 @@ namespace Smarty\Compile\Tag; use Smarty\Compile\Base; use Smarty\Compiler\Template; +use Smarty\Data; use Smarty\Smarty; use Smarty\Template\Compiled; @@ -60,17 +61,6 @@ class IncludeTag extends Base { */ protected $optional_attributes = ['_any']; - /** - * Valid scope names - * - * @var array - */ - protected $valid_scopes = [ - 'parent' => Smarty::SCOPE_PARENT, 'root' => Smarty::SCOPE_ROOT, - 'global' => Smarty::SCOPE_GLOBAL, 'tpl_root' => Smarty::SCOPE_TPL_ROOT, - 'smarty' => Smarty::SCOPE_SMARTY, - ]; - /** * Compiles code for the {include} tag * @@ -125,13 +115,8 @@ class IncludeTag extends Base { $variable_template = true; } // scope setup - $_scope = $compiler->convertScope($_attr, $this->valid_scopes); - // set flag to cache subtemplate object when called within loop or template name is variable. - if ($cache_tpl || $variable_template || $compiler->loopNesting > 0) { - $_cache_tpl = 'true'; - } else { - $_cache_tpl = 'false'; - } + $_scope = $this->convertScope($_attr, [Data::SCOPE_LOCAL]); + // assume caching is off $_caching = Smarty::CACHING_OFF; $call_nocache = $compiler->tag_nocache || $compiler->nocache; @@ -244,16 +229,16 @@ class IncludeTag extends Base { } if (!empty($_attr) && $_caching === 9999 && $compiler->template->caching) { $_vars_nc = "foreach ($_vars as \$ik => \$iv) {\n"; - $_vars_nc .= "\$_smarty_tpl->tpl_vars[\$ik] = new \\Smarty\\Variable(\$iv);\n"; + $_vars_nc .= "\$_smarty_tpl->assign(\$ik, \$iv);\n"; $_vars_nc .= "}\n"; $_output .= substr($compiler->processNocacheCode('\n", true), 6, -3); } if (isset($_assign)) { $_output .= "ob_start();\n"; } - $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, {$_cache_id}, {$_compile_id}, {$_caching}, {$_cache_lifetime}, {$_vars}, {$_scope}, {$_cache_tpl}, '{$compiler->parent_compiler->mergedSubTemplatesData[$uid][$t_hash]['uid']}', '{$compiler->parent_compiler->mergedSubTemplatesData[$uid][$t_hash]['func']}');\n"; + $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, {$_cache_id}, {$_compile_id}, {$_caching}, {$_cache_lifetime}, {$_vars}, '{$compiler->parent_compiler->mergedSubTemplatesData[$uid][$t_hash]['uid']}', '{$compiler->parent_compiler->mergedSubTemplatesData[$uid][$t_hash]['func']}');\n"; if (isset($_assign)) { - $_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean());\n"; + $_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean(), false, {$_scope});\n"; } if ($update_compile_id) { $_output .= $compiler->makeNocacheCode("\$_smarty_tpl->compile_id = array_pop(\$_compile_id_save);\n"); @@ -272,9 +257,9 @@ class IncludeTag extends Base { if (isset($_assign)) { $_output .= "ob_start();\n"; } - $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, $_cache_id, $_compile_id, $_caching, $_cache_lifetime, $_vars, $_scope, {$_cache_tpl});\n"; + $_output .= "\$_smarty_tpl->_subTemplateRender({$fullResourceName}, $_cache_id, $_compile_id, $_caching, $_cache_lifetime, $_vars);\n"; if (isset($_assign)) { - $_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean());\n"; + $_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean(), false, {$_scope});\n"; } if ($update_compile_id) { $_output .= "\$_smarty_tpl->compile_id = array_pop(\$_compile_id_save);\n"; diff --git a/src/Compiler/Configfile.php b/src/Compiler/Configfile.php index 75fc6a03..e016cb43 100644 --- a/src/Compiler/Configfile.php +++ b/src/Compiler/Configfile.php @@ -140,7 +140,7 @@ class Configfile extends BaseCompiler { date("Y-m-d H:i:s"), str_replace('*/', '* /', $this->template->source->filepath) ); - $code = '_loadConfigVars(' . + $code = 'assignConfigVars(' . var_export($this->config_data, true) . '); ?>'; return $template_header . $this->template->createCodeFrame($code); } diff --git a/src/Compiler/Template.php b/src/Compiler/Template.php index 4f04aab5..e2a34cc2 100644 --- a/src/Compiler/Template.php +++ b/src/Compiler/Template.php @@ -770,33 +770,6 @@ class Template extends BaseCompiler { } } - /** - * @param array $_attr tag attributes - * @param array $validScopes - * - * @return int|string - * @throws \Smarty\CompilerException - */ - public function convertScope($_attr, $validScopes) { - $_scope = 0; - if (isset($_attr['scope'])) { - $_scopeName = trim($_attr['scope'], '\'"'); - if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) { - $_scope = $_scopeName; - } elseif (is_string($_scopeName)) { - $_scopeName = trim($_scopeName, '\'"'); - $_scope = isset($validScopes[$_scopeName]) ? $validScopes[$_scopeName] : false; - } else { - $_scope = false; - } - if ($_scope === false) { - $err = var_export($_scopeName, true); - $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true); - } - } - return $_scope; - } - /** * Generate nocache code string * diff --git a/src/Data.php b/src/Data.php index 73801c0c..b5c7e095 100644 --- a/src/Data.php +++ b/src/Data.php @@ -4,15 +4,7 @@ namespace Smarty; /** * Smarty Internal Plugin Data - * This file contains the basic classes and methods for template and variable creation - * - - - * @author Uwe Tews - */ - -/** - * Base class with template and variable methods + * This file contains the basic properties and methods for holding config and template variables */ abstract class Data { @@ -55,25 +47,47 @@ abstract class Data */ public $config_vars = array(); - /** - * assigns a Smarty variable - * - * @param array|string $tpl_var the template variable name(s) - * @param mixed $value the value to assign - * @param boolean $nocache if true any output of this variable will be not cached - * - * @return Data current Data (or Smarty or \Smarty\Template) instance for - * chaining - */ - public function assign($tpl_var, $value = null, $nocache = false) + /** + * assigns a Smarty variable + * + * @param array|string $tpl_var the template variable name(s) + * @param mixed $value the value to assign + * @param boolean $nocache if true any output of this variable will be not cached + * @param int $scope one of self::SCOPE_* constants + * + * @return Data current Data (or Smarty or \Smarty\Template) instance for + * chaining + */ + public function assign($tpl_var, $value = null, $nocache = false, $scope = 0) { if (is_array($tpl_var)) { foreach ($tpl_var as $_key => $_val) { - $this->assign($_key, $_val, $nocache); + $this->assign($_key, $_val, $nocache, $scope); } - } else { - $this->tpl_vars[ $tpl_var ] = new Variable($value, $nocache); } + + switch ($scope) { + case self::SCOPE_GLOBAL: + case self::SCOPE_SMARTY: + $this->_getSmartyObj()->assign($tpl_var, $value); + break; + case self::SCOPE_TPL_ROOT: + case self::SCOPE_ROOT: + $ptr = $this; + while (isset($ptr->parent) && !($ptr->parent instanceof Smarty)) { + $ptr = $ptr->parent; + } + $ptr->assign($tpl_var, $value); + break; + case self::SCOPE_PARENT: + if ($this->parent) { + $this->parent->assign($tpl_var, $value); + } + break; + default: + $this->tpl_vars[ $tpl_var ] = new Variable($value, $nocache); + } + return $this; } @@ -125,9 +139,12 @@ abstract class Data * @param boolean $nocache if true any output of this variable will be not cached * * @return Data + * @deprecated since 5.0 */ public function assignGlobal($varName, $value = null, $nocache = false) { + trigger_error(__METHOD__ . " is deprecated. Use \\Smarty\\Smarty::assign() to assign a variable " . + " at the Smarty level.", E_USER_DEPRECATED); return $this->_getSmartyObj()->assign($varName, $value, $nocache); } @@ -146,39 +163,9 @@ abstract class Data { if (isset($varName)) { return $this->getValue($varName, $searchParents); - $_var = $_ptr->getVariable($varName, $searchParents, false); - if (is_object($_var)) { - return $_var->value; - } else { - return null; - } - } else { - $_result = array(); - if ($_ptr === null) { - $_ptr = $this; - } - while ($_ptr !== null) { - foreach ($_ptr->tpl_vars as $key => $var) { - if (!array_key_exists($key, $_result)) { - $_result[ $key ] = $var->value; - } - } - // not found, try at parent - if ($searchParents && isset($_ptr->parent)) { - $_ptr = $_ptr->parent; - } else { - $_ptr = null; - } - } - if ($searchParents) { - foreach ($this->_getSmartyObj()->getAllGlobalTemplateVars() as $key => $var) { - if (!array_key_exists($key, $_result)) { - $_result[ $key ] = $var->value; - } - } - } - return $_result; } + + return array_merge($this->parent && $searchParents ? $this->parent->getTemplateVars() : [], $this->tpl_vars); } /** @@ -246,6 +233,38 @@ abstract class Data return isset($variable) ? $variable->getValue() : null; } + /** + * load config variables into template object + * + * @param array $new_config_vars + */ + public function assignConfigVars($new_config_vars) { + + // copy global config vars + foreach ($new_config_vars['vars'] as $variable => $value) { + if ($this->smarty->config_overwrite || !isset($this->config_vars[$variable])) { + $this->config_vars[$variable] = $value; + } else { + $this->config_vars[$variable] = array_merge((array)$this->config_vars[$variable], (array)$value); + } + } + // scan sections + $sections = $this->source->config_sections; + if (!empty($sections)) { + foreach ((array)$sections as $tpl_section) { + if (isset($new_config_vars['sections'][$tpl_section])) { + foreach ($new_config_vars['sections'][$tpl_section]['vars'] as $variable => $value) { + if ($this->smarty->config_overwrite || !isset($this->config_vars[$variable])) { + $this->config_vars[$variable] = $value; + } else { + $this->config_vars[$variable] = array_merge((array)$this->config_vars[$variable], (array)$value); + } + } + } + } + } + } + /** * Get Smarty object * @@ -312,19 +331,15 @@ abstract class Data return $this; } - - - - /** * Gets a config variable value * - * @param null $varName the name of the config variable + * @param string $varName the name of the config variable * * @return mixed the value of the config variable * @throws Exception */ - public function getConfigVariable($varName = null) + public function getConfigVariable($varName) { if (isset($this->config_vars[$varName])) { @@ -343,12 +358,13 @@ abstract class Data /** * Returns a single or all config variables * - * @api Smarty::getConfigVars() - * @link https://www.smarty.net/docs/en/api.get.config.vars.tpl - * - * @param string $varname variable name or null + * @param string $varname variable name or null * * @return mixed variable value or or array of variables + * @throws Exception + * @link https://www.smarty.net/docs/en/api.get.config.vars.tpl + * + * @api Smarty::getConfigVars() */ public function getConfigVars($varname = null) { @@ -367,24 +383,14 @@ abstract class Data * @param string $varName variable name or null * * @return string|array variable value or or array of variables + * + * @deprecated since 5.0 */ public function getGlobal($varName = null) { - if (isset($varName)) { - if ($this->_getSmartyObj()->getGlobalVariable($varName)) { - return $this->_getSmartyObj()->getGlobalVariable($varName)->getValue(); - } else { - return ''; - } - } else { - $_result = []; - foreach ($this->_getSmartyObj()->getAllGlobalTemplateVars() as $key => $var) { - $_result[ $key ] = $var->value; - } - return $_result; - } + trigger_error(__METHOD__ . " is deprecated. Use \\Smarty\\Smarty::getValue() to retrieve a variable " . + " at the Smarty level.", E_USER_DEPRECATED); + return $this->_getSmartyObj()->getValue($varName); } - - } diff --git a/src/DataObject.php b/src/DataObject.php index 945fa8c0..033ac0d0 100644 --- a/src/DataObject.php +++ b/src/DataObject.php @@ -36,7 +36,6 @@ class DataObject extends Data { * @throws Exception */ public function __construct($_parent = null, $smarty = null, $name = null) { - parent::__construct(); $this->smarty = $smarty; if (is_object($_parent)) { diff --git a/src/Debug.php b/src/Debug.php index fd1efa38..756aa674 100644 --- a/src/Debug.php +++ b/src/Debug.php @@ -226,9 +226,8 @@ class Debug extends Data $_template = new \Smarty\Template($debObj->debug_tpl, $debObj); if ($obj instanceof \Smarty\Template) { $_template->assign('template_name', $obj->source->type . ':' . $obj->source->name); - } - if ($obj->_objType === 1 || $full) { - $_template->assign('template_data', $this->template_data[ $this->index ]); + } elseif ($obj instanceof Smarty || $full) { + $_template->assign('template_data', $this->template_data[$this->index]); } else { $_template->assign('template_data', null); } diff --git a/src/Runtime/InheritanceRuntime.php b/src/Runtime/InheritanceRuntime.php index ed7e1fdc..f7587830 100644 --- a/src/Runtime/InheritanceRuntime.php +++ b/src/Runtime/InheritanceRuntime.php @@ -120,7 +120,6 @@ class InheritanceRuntime { $tpl->caching ? 9999 : 0, $tpl->cache_lifetime, [], - 2, false, $uid, $func diff --git a/src/Smarty.php b/src/Smarty.php index bd62c190..f77a8b6d 100644 --- a/src/Smarty.php +++ b/src/Smarty.php @@ -454,13 +454,6 @@ class Smarty extends \Smarty\TemplateBase */ public $_parserdebug = false; - /** - * This object type (Smarty = 1, template = 2, data = 4) - * - * @var int - */ - public $_objType = 1; - /** * Debug object * @@ -550,7 +543,7 @@ class Smarty extends \Smarty\TemplateBase */ public function __construct() { - parent::__construct(); + if (is_callable('mb_internal_encoding')) { mb_internal_encoding(\Smarty\Smarty::$_CHARSET); } @@ -2294,7 +2287,7 @@ class Smarty extends \Smarty\TemplateBase * @return Template * @throws Exception */ - private function returnOrCreateTemplate($template, $cache_id, $compile_id, $parent) { + private function returnOrCreateTemplate($template, $cache_id = null, $compile_id = null, $parent = null) { if (!($template instanceof Template)) { $template = $this->createTemplate($template, $cache_id, $compile_id, $parent ?: $this, false); $template->caching = $this->caching; diff --git a/src/Template.php b/src/Template.php index 2642e5ec..8a35d630 100644 --- a/src/Template.php +++ b/src/Template.php @@ -27,13 +27,6 @@ use Smarty\Template\Config; #[\AllowDynamicProperties] class Template extends TemplateBase { - /** - * This object type (Smarty = 1, template = 2, data = 4) - * - * @var int - */ - public $_objType = 2; - /** * Source instance * @@ -69,13 +62,6 @@ class Template extends TemplateBase { */ public $templateId = null; - /** - * Scope in which variables shall be assigned - * - * @var int - */ - public $scope = 0; - /** * Flag which is set while rending a cache file * @@ -148,7 +134,7 @@ class Template extends TemplateBase { // Template resource $this->template_resource = $template_resource; $this->source = $_isConfig ? Config::load($this) : Source::load($this); - parent::__construct(); + if ($smarty->security_policy && method_exists($smarty->security_policy, 'registerCallBacks')) { $smarty->security_policy->registerCallBacks($this); } @@ -244,8 +230,6 @@ class Template extends TemplateBase { * @param integer $caching cache mode * @param integer $cache_lifetime life time of cache data * @param array $data passed parameter template variables - * @param int $scope scope in which {include} should execute - * @param bool $forceTplCache cache template object * @param string $uid file dependency uid * @param string $content_func function name * @@ -259,8 +243,6 @@ class Template extends TemplateBase { $caching, $cache_lifetime, $data, - $scope, - $forceTplCache, $uid = null, $content_func = null ) { @@ -296,8 +278,6 @@ class Template extends TemplateBase { } $tpl->caching = $caching; $tpl->cache_lifetime = $cache_lifetime; - // set template scope - $tpl->scope = $scope; if (!empty($data)) { // set up variable values @@ -341,8 +321,8 @@ class Template extends TemplateBase { return isset($this->parent) && $this->parent instanceof Template; } - public function assign($tpl_var, $value = null, $nocache = false) { - return parent::assign($tpl_var, $value, $nocache || $this->isRenderingCache); + public function assign($tpl_var, $value = null, $nocache = false, $scope = 0) { + return parent::assign($tpl_var, $value, $nocache || $this->isRenderingCache, $scope); } /** @@ -612,82 +592,7 @@ class Template extends TemplateBase { } } - /** - * load config variables into template object - * - * @param array $new_config_vars - */ - public function _loadConfigVars($new_config_vars) { - $this->_assignConfigVars($this->parent->config_vars, $new_config_vars); - $tagScope = $this->source->scope; - if ($tagScope >= 0) { - if ($tagScope === \Smarty\Smarty::SCOPE_LOCAL) { - $this->_updateConfigVarStack($new_config_vars); - $tagScope = 0; - if (!$this->scope) { - return; - } - } - if ($this->parent instanceof Template && ($tagScope || $this->parent->scope)) { - $mergedScope = $tagScope | $this->scope; - if ($mergedScope) { - // update scopes - /* @var \Smarty\Data $ptr */ - foreach ($this->parent->_getAffectedScopes($mergedScope) as $ptr) { - $this->_assignConfigVars($ptr->config_vars, $new_config_vars); - if ($tagScope && $ptr instanceof Template && isset($this->_var_stack)) { - $this->_updateConfigVarStack($new_config_vars); - } - } - } - } - } - } - /** - * Assign all config variables in given scope - * - * @param array $config_vars config variables in scope - * @param array $new_config_vars loaded config variables - */ - private function _assignConfigVars(&$config_vars, $new_config_vars) { - // copy global config vars - foreach ($new_config_vars['vars'] as $variable => $value) { - if ($this->smarty->config_overwrite || !isset($config_vars[$variable])) { - $config_vars[$variable] = $value; - } else { - $config_vars[$variable] = array_merge((array)$config_vars[$variable], (array)$value); - } - } - // scan sections - $sections = $this->source->config_sections; - if (!empty($sections)) { - foreach ((array)$sections as $tpl_section) { - if (isset($new_config_vars['sections'][$tpl_section])) { - foreach ($new_config_vars['sections'][$tpl_section]['vars'] as $variable => $value) { - if ($this->smarty->config_overwrite || !isset($config_vars[$variable])) { - $config_vars[$variable] = $value; - } else { - $config_vars[$variable] = array_merge((array)$config_vars[$variable], (array)$value); - } - } - } - } - } - } - - /** - * Update config variables in template local variable stack - * - * @param array $config_vars - */ - private function _updateConfigVarStack($config_vars) { - $i = 0; - while (isset($this->_var_stack[$i])) { - $this->_assignConfigVars($this->_var_stack[$i]['config'], $config_vars); - $i++; - } - } /** * Returns if the current template must be compiled by the Smarty compiler @@ -716,74 +621,6 @@ class Template extends TemplateBase { return $this->mustCompile; } - /** - * Update new assigned template or config variable in other effected scopes - * - * @param string|null $varName variable name - * @param int $tagScope tag scope to which bubble up variable value - */ - protected function _updateScope($varName, $tagScope = 0) { - if ($tagScope) { - $this->_updateVarStack($this, $varName); - $tagScope = $tagScope & ~\Smarty\Smarty::SCOPE_LOCAL; - if (!$this->scope && !$tagScope) { - return; - } - } - $mergedScope = $tagScope | $this->scope; - if ($mergedScope) { - if ($mergedScope & \Smarty\Smarty::SCOPE_GLOBAL && $varName) { - $this->_getSmartyObj()->setGlobalVariable($varName, $this->tpl_vars[$varName]); - } - // update scopes - foreach ($this->_getAffectedScopes($mergedScope) as $ptr) { - $this->_updateVariableInOtherScope($ptr->tpl_vars, $varName); - if ($tagScope && $ptr instanceof Template && isset($this->_var_stack)) { - $this->_updateVarStack($ptr, $varName); - } - } - } - } - - /** - * Get array of objects which needs to be updated by given scope value - * - * @param int $mergedScope merged tag and template scope to which bubble up variable value - * - * @return array - */ - private function _getAffectedScopes($mergedScope) { - $_stack = []; - $ptr = $this->parent; - if ($mergedScope && isset($ptr) && $ptr instanceof Template) { - $_stack[] = $ptr; - $mergedScope = $mergedScope & ~\Smarty\Smarty::SCOPE_PARENT; - if (!$mergedScope) { - // only parent was set, we are done - return $_stack; - } - $ptr = $ptr->parent; - } - while (isset($ptr) && $ptr instanceof Template) { - $_stack[] = $ptr; - $ptr = $ptr->parent; - } - if ($mergedScope & \Smarty\Smarty::SCOPE_SMARTY) { - if (isset($this->smarty)) { - $_stack[] = $this->smarty; - } - } elseif ($mergedScope & \Smarty\Smarty::SCOPE_ROOT) { - while (isset($ptr)) { - if (!$ptr instanceof Template) { - $_stack[] = $ptr; - break; - } - $ptr = $ptr->parent; - } - } - return $_stack; - } - private function getFrameCompiler(): Compiler\CodeFrame { return new \Smarty\Compiler\CodeFrame($this); } @@ -856,9 +693,9 @@ class Template extends TemplateBase { /** * @inheritdoc */ - public function _loadConfigfile($config_file, $sections = null, $scope = 0) + public function _loadConfigfile($config_file, $sections = null) { - $confObj = parent::_loadConfigfile($config_file, $sections, $scope); + $confObj = parent::_loadConfigfile($config_file, $sections); $this->compiled->file_dependency[ $confObj->source->uid ] = array($confObj->source->filepath, $confObj->source->getTimeStamp(), $confObj->source->type); @@ -922,10 +759,8 @@ class Template extends TemplateBase { $errorHandler->activate(); } - /* @var Template $parent */ - if (isset($parent->_objType) && ($parent->_objType === 2) && !empty($parent->tplFunctions)) { - $this->tplFunctions = array_merge($parent->tplFunctions, $this->tplFunctions); - } + $this->tplFunctions = array_merge($parent->tplFunctions ?? [], $this->tplFunctions ?? []); + if ($function === 2) { if ($this->caching) { // return cache status of template diff --git a/src/Template/Config.php b/src/Template/Config.php index a26e8af9..2a6e7b7a 100644 --- a/src/Template/Config.php +++ b/src/Template/Config.php @@ -21,13 +21,6 @@ class Config extends Source { */ public $config_sections = null; - /** - * scope into which the config variables shall be loaded - * - * @var int - */ - public $scope = 0; - /** * Flag that source is a config file * diff --git a/src/TemplateBase.php b/src/TemplateBase.php index 4ad90512..ced74e36 100644 --- a/src/TemplateBase.php +++ b/src/TemplateBase.php @@ -12,9 +12,6 @@ namespace Smarty; /** * Class with shared smarty/template methods - * - * @property int $_objType - * */ abstract class TemplateBase extends Data { @@ -452,7 +449,7 @@ abstract class TemplateBase extends Data { */ public function configLoad($config_file, $sections = null) { - $this->_loadConfigfile($config_file, $sections, null); + $this->_loadConfigfile($config_file, $sections); return $this; } @@ -462,22 +459,20 @@ abstract class TemplateBase extends Data { * @param string $config_file filename * @param mixed $sections array of section names, single * section or null - * @param int $scope scope into which config variables - * shall be loaded + * @returns Template * @throws \Exception * @link https://www.smarty.net/docs/en/api.config.load.tpl * * @api Smarty::configLoad() */ - public function _loadConfigfile($config_file, $sections = null, $scope = 0) + public function _loadConfigfile($config_file, $sections = null) { $smarty = $this->_getSmartyObj(); $confObj = new Template($config_file, $smarty, $this, null, null, null, null, true); $confObj->caching = Smarty::CACHING_OFF; $confObj->source->config_sections = $sections; - $confObj->source->scope = $scope; $confObj->compiled = \Smarty\Template\Compiled::load($confObj); $confObj->compiled->render($confObj); return $confObj; diff --git a/tests/UnitTests/A_2/UndefinedTemplateVar/UndefinedTemplateVarTest.php b/tests/UnitTests/A_2/UndefinedTemplateVar/UndefinedTemplateVarTest.php index 13bcd0f7..a1611b46 100644 --- a/tests/UnitTests/A_2/UndefinedTemplateVar/UndefinedTemplateVarTest.php +++ b/tests/UnitTests/A_2/UndefinedTemplateVar/UndefinedTemplateVarTest.php @@ -65,26 +65,12 @@ class UndefinedTemplateVarTest extends PHPUnit_Smarty */ public function testError() { - $exceptionThrown = false; - - try { - $e1 = error_reporting(); - $this->assertEquals('undefined = ', $this->smarty->fetch('001_main.tpl')); - $e2 = error_reporting(); - $this->assertEquals($e1, $e2); - } catch (Exception $e) { - - $exceptionThrown = true; - $this->assertStringStartsWith('Undefined ', $e->getMessage()); - $this->assertTrue(in_array( - get_class($e), - [ - 'PHPUnit\Framework\Error\Warning', - 'PHPUnit\Framework\Error\Notice', - ] - )); - } - $this->assertTrue($exceptionThrown); + $this->expectException(PHPUnit\Framework\Error\Error::class); + $this->expectExceptionMessage('Undefined '); + $e1 = error_reporting(); + $this->assertEquals('undefined = ', $this->smarty->fetch('001_main.tpl')); + $e2 = error_reporting(); + $this->assertEquals($e1, $e2); } public function testUndefinedSimpleVar() { diff --git a/tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php b/tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php deleted file mode 100644 index 5755b19a..00000000 --- a/tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php +++ /dev/null @@ -1,62 +0,0 @@ -setUpSmarty(__DIR__); - } - - /** - * test appendByRef - */ - public function testAppendByRef() - { - $bar = 'bar'; - $bar2 = 'bar2'; - $this->smarty->appendByRef('foo', $bar); - $this->smarty->appendByRef('foo', $bar2); - $bar = 'newbar'; - $bar2 = 'newbar2'; - $this->assertEquals('newbar newbar2', $this->smarty->fetch('eval:{$foo[0]} {$foo[1]}')); - } - - /** - * test appendByRef to unassigned variable - */ - public function testAppendByRefUnassigned() - { - $bar2 = 'bar2'; - $this->smarty->appendByRef('foo', $bar2); - $bar2 = 'newbar2'; - $this->assertEquals('newbar2', $this->smarty->fetch('eval:{$foo[0]}')); - } - - /** - * test appendByRef merge - */ - public function testAppendByRefMerge() - { - $foo = array('a' => 'a', 'b' => 'b', 'c' => 'c'); - $bar = array('b' => 'd'); - $this->smarty->assignByRef('foo', $foo); - $this->smarty->appendByRef('foo', $bar, true); - $this->assertEquals('a d c', $this->smarty->fetch('eval:{$foo["a"]} {$foo["b"]} {$foo["c"]}')); - $bar = array('b' => 'newd'); - $this->smarty->appendByRef('foo', $bar, true); - $this->assertEquals('a newd c', $this->smarty->fetch('eval:{$foo["a"]} {$foo["b"]} {$foo["c"]}')); - } -} diff --git a/tests/UnitTests/SmartyMethodsTests/AppendByRef/cache/.gitignore b/tests/UnitTests/SmartyMethodsTests/AppendByRef/cache/.gitignore deleted file mode 100644 index d88cc144..00000000 --- a/tests/UnitTests/SmartyMethodsTests/AppendByRef/cache/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Ignore anything in here, but keep this directory -* diff --git a/tests/UnitTests/SmartyMethodsTests/AppendByRef/templates_c/.gitignore b/tests/UnitTests/SmartyMethodsTests/AppendByRef/templates_c/.gitignore deleted file mode 100644 index d88cc144..00000000 --- a/tests/UnitTests/SmartyMethodsTests/AppendByRef/templates_c/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Ignore anything in here, but keep this directory -* diff --git a/tests/UnitTests/SmartyMethodsTests/AssignByRef/AssignByRefTest.php b/tests/UnitTests/SmartyMethodsTests/AssignByRef/AssignByRefTest.php deleted file mode 100644 index 2cb9e1a7..00000000 --- a/tests/UnitTests/SmartyMethodsTests/AssignByRef/AssignByRefTest.php +++ /dev/null @@ -1,33 +0,0 @@ -setUpSmarty(__DIR__); - } - - /** - * test simple assignByRef - */ - public function testSimpleAssignByRef() - { - $bar = 'bar'; - $this->smarty->assignByRef('foo', $bar); - $bar = 'newbar'; - $this->assertEquals('newbar', $this->smarty->fetch('eval:{$foo}')); - } -} diff --git a/tests/UnitTests/SmartyMethodsTests/AssignByRef/cache/.gitignore b/tests/UnitTests/SmartyMethodsTests/AssignByRef/cache/.gitignore deleted file mode 100644 index d88cc144..00000000 --- a/tests/UnitTests/SmartyMethodsTests/AssignByRef/cache/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Ignore anything in here, but keep this directory -* diff --git a/tests/UnitTests/SmartyMethodsTests/AssignByRef/templates_c/.gitignore b/tests/UnitTests/SmartyMethodsTests/AssignByRef/templates_c/.gitignore deleted file mode 100644 index d88cc144..00000000 --- a/tests/UnitTests/SmartyMethodsTests/AssignByRef/templates_c/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Ignore anything in here, but keep this directory -* diff --git a/tests/UnitTests/TemplateSource/X_Scopes/ScopeTest.php b/tests/UnitTests/TemplateSource/X_Scopes/ScopeTest.php index a0dd933a..847406c8 100644 --- a/tests/UnitTests/TemplateSource/X_Scopes/ScopeTest.php +++ b/tests/UnitTests/TemplateSource/X_Scopes/ScopeTest.php @@ -31,8 +31,6 @@ class ScopeTest extends PHPUnit_Smarty /** * Test scope * - * - * * @dataProvider dataTestAppendScope */ public function testAppendScope($code, $useSmarty, $result, $testName, $testNumber) @@ -50,8 +48,8 @@ class ScopeTest extends PHPUnit_Smarty "test - {$code} - {$testName}"); } - /* - * Data provider für testAppendScope + /** + * Data provider for testAppendScope */ public function dataTestAppendScope() { @@ -78,9 +76,6 @@ class ScopeTest extends PHPUnit_Smarty /** * Test scope - * - * @not runInSeparateProcess - * * @dataProvider dataTestAssignScope */ public function testAssignScope($code, $useSmarty, $result, $testName, $testNumber)