diff --git a/changelog/1100.md b/changelog/1100.md new file mode 100644 index 00000000..d1b69006 --- /dev/null +++ b/changelog/1100.md @@ -0,0 +1 @@ +- Fixed that modifiers called like function would be compiled to modifier name instead of calling the registered callback [#1100](https://github.com/smarty-php/smarty/issues/1100) diff --git a/libs/sysplugins/smarty_internal_templatecompilerbase.php b/libs/sysplugins/smarty_internal_templatecompilerbase.php index 254e1bf2..4635e141 100644 --- a/libs/sysplugins/smarty_internal_templatecompilerbase.php +++ b/libs/sysplugins/smarty_internal_templatecompilerbase.php @@ -455,28 +455,28 @@ abstract class Smarty_Internal_TemplateCompilerBase $this->smarty->_current_file = $this->template->source->filepath; // get template source if (!empty($this->template->source->components)) { - $_compiled_code = '_loadInheritance(); $_smarty_tpl->inheritance->init($_smarty_tpl, true); ?>'; + $_compiled_code = '_loadInheritance(); $_smarty_tpl->inheritance->init($_smarty_tpl, true); ?>'; - $i = 0; - $reversed_components = array_reverse($this->template->getSource()->components); - foreach ($reversed_components as $source) { - $i++; - if ($i === count($reversed_components)) { - $_compiled_code .= 'inheritance->endChild($_smarty_tpl); ?>'; - } - $_compiled_code .= $this->compileTag( - 'include', - [ - var_export($source->resource, true), - ['scope' => 'parent'], - ] - ); - } - $_compiled_code = $this->postFilter($_compiled_code, $this->template); + $i = 0; + $reversed_components = array_reverse($this->template->getSource()->components); + foreach ($reversed_components as $source) { + $i++; + if ($i === count($reversed_components)) { + $_compiled_code .= 'inheritance->endChild($_smarty_tpl); ?>'; + } + $_compiled_code .= $this->compileTag( + 'include', + [ + var_export($source->resource, true), + ['scope' => 'parent'], + ] + ); + } + $_compiled_code = $this->postFilter($_compiled_code, $this->template); } else { // get template source $_content = $this->template->source->getContent(); - $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true)); + $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true)); } if (!empty($this->required_plugins[ 'compiled' ]) || !empty($this->required_plugins[ 'nocache' ])) { $_compiled_code = 'compileRequiredPlugins() . "?>\n" . $_compiled_code; @@ -617,7 +617,8 @@ abstract class Smarty_Internal_TemplateCompilerBase { if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) { if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0 - || strcasecmp($name, 'array') === 0 || is_callable($name) + || strcasecmp($name, 'array') === 0 + || (is_callable($name) && !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name])) ) { $func_name = smarty_strtolower_ascii($name); @@ -649,28 +650,42 @@ abstract class Smarty_Internal_TemplateCompilerBase } if ($func_name === 'empty') { return $func_name . '(' . - str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')'; + str_replace("')->value", "',null,true,false)->value", $parameter[0]) . ')'; } else { - return $func_name . '(' . $parameter[ 0 ] . ')'; + return $func_name . '(' . $parameter[0] . ')'; } } else { - if ( - !$this->smarty->loadPlugin('smarty_modifiercompiler_' . $name) - && !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name]) - && !in_array($name, ['time', 'join', 'is_array', 'in_array', 'count']) - ) { - trigger_error('Using unregistered function "' . $name . '" in a template is deprecated and will be ' . - 'removed in a future release. Use Smarty::registerPlugin to explicitly register ' . - 'a custom modifier.', E_USER_DEPRECATED); - } + if ( + !$this->smarty->loadPlugin('smarty_modifiercompiler_' . $name) + && !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name]) + && !in_array($name, ['time', 'join', 'is_array', 'in_array', 'count']) + ) { + trigger_error('Using unregistered function "' . $name . '" in a template is deprecated and will be ' . + 'removed in a future release. Use Smarty::registerPlugin to explicitly register ' . + 'a custom modifier.', E_USER_DEPRECATED); + } - return $name . '(' . implode(',', $parameter) . ')'; + return $name . '(' . implode(',', $parameter) . ')'; } - } else { - $this->trigger_template_error("unknown function '{$name}'"); + } } + + if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name])) { + if ($name === $this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name][0]) { + return $name . '(' . implode(',', $parameter) . ')'; + } + + return sprintf( + 'call_user_func_array($_smarty_tpl->registered_plugins[ \'%s\' ][ %s ][ 0 ], array( %s ))', + Smarty::PLUGIN_MODIFIER, + var_export($name, true), + implode(',', $parameter) + ); + } + + $this->trigger_template_error("unknown function '{$name}'"); } /** diff --git a/tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierFirstClassCallablesTest.php b/tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierFirstClassCallablesTest.php new file mode 100644 index 00000000..7e600017 --- /dev/null +++ b/tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierFirstClassCallablesTest.php @@ -0,0 +1,55 @@ += 80100) { + + /** + * class for register modifier with (first class) callables tests + * + * @runTestsInSeparateProcess + * @preserveGlobalState disabled + * @backupStaticAttributes enabled + */ + class RegisterModifierFirstClassCallablesTest extends PHPUnit_Smarty + { + public function setUp(): void + { + $this->setUpSmarty(__DIR__); + } + + + public function testInit() + { + $this->cleanDirs(); + } + + public function testRegisterFirstClassCallable() + { + $this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'testmodifier', eval('return strrev(...);')); + $this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|testmodifier}')); + } + + public function testRegisterFirstClassCallableSameName() + { + $this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'mymodifier', eval('return strrev(...);')); + $this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|mymodifier}')); + } + + public function testRegisterFirstClassCallableAsFunc() + { + $this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'kprint_r_out', eval('return strrev(...);')); + $this->smarty->assign('myVar', 'andersom'); + $this->assertEquals('mosredna', $this->smarty->fetch('string:{kprint_r_out($myVar)}')); + } + + public function testRegisterFirstClassCallableSameNameAsPhpFunc() + { + $this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'mymodifierfcc', eval('return strrev(...);')); + $this->assertEquals('mosredna', $this->smarty->fetch('string:{mymodifierfcc("andersom")}')); + } + + } +} +function mymodifierfcc($a, $b, $c) +{ + return "$a function $b $c"; +} diff --git a/tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierTest.php b/tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierTest.php index 50b25086..ca80062a 100644 --- a/tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierTest.php +++ b/tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierTest.php @@ -88,6 +88,22 @@ class RegisterModifierTest extends PHPUnit_Smarty $this->smarty->unregisterPlugin(Smarty::PLUGIN_MODIFIER, 'testmodifier'); $this->assertTrue(isset($this->smarty->registered_plugins[Smarty::PLUGIN_BLOCK]['testmodifier'])); } + + + public function testRegisterNativePhpFuncAsString() + { + $this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'strrev', 'strrev'); + $this->smarty->assign('myVar', 'andersom'); + $this->assertEquals('mosredna', $this->smarty->fetch('string:{strrev($myVar)}')); + } + + public function testRegisterNativePhpFuncUnderDifferentName() + { + $this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'k_xyz_a', 'strrev'); + $this->smarty->assign('myVar', 'andersom'); + $this->assertEquals('mosredna', $this->smarty->fetch('string:{k_xyz_a($myVar)}')); + } + } function mymodifier($a, $b, $c)