Skip to content

Commit 5cb412d

Browse files
authored
Bugfix/1100 modifier called like a function compiled to modifier name not callback (#1101)
* Fixed that modifiers called like function would be compiled to modifier name instead of calling the registered callback
1 parent 272a407 commit 5cb412d

File tree

4 files changed

+122
-35
lines changed

4 files changed

+122
-35
lines changed

changelog/1100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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)

libs/sysplugins/smarty_internal_templatecompilerbase.php

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -455,28 +455,28 @@ public function compileTemplateSource(
455455
$this->smarty->_current_file = $this->template->source->filepath;
456456
// get template source
457457
if (!empty($this->template->source->components)) {
458-
$_compiled_code = '<?php $_smarty_tpl->_loadInheritance(); $_smarty_tpl->inheritance->init($_smarty_tpl, true); ?>';
459-
460-
$i = 0;
461-
$reversed_components = array_reverse($this->template->getSource()->components);
462-
foreach ($reversed_components as $source) {
463-
$i++;
464-
if ($i === count($reversed_components)) {
465-
$_compiled_code .= '<?php $_smarty_tpl->inheritance->endChild($_smarty_tpl); ?>';
466-
}
467-
$_compiled_code .= $this->compileTag(
468-
'include',
469-
[
470-
var_export($source->resource, true),
471-
['scope' => 'parent'],
472-
]
473-
);
474-
}
475-
$_compiled_code = $this->postFilter($_compiled_code, $this->template);
458+
$_compiled_code = '<?php $_smarty_tpl->_loadInheritance(); $_smarty_tpl->inheritance->init($_smarty_tpl, true); ?>';
459+
460+
$i = 0;
461+
$reversed_components = array_reverse($this->template->getSource()->components);
462+
foreach ($reversed_components as $source) {
463+
$i++;
464+
if ($i === count($reversed_components)) {
465+
$_compiled_code .= '<?php $_smarty_tpl->inheritance->endChild($_smarty_tpl); ?>';
466+
}
467+
$_compiled_code .= $this->compileTag(
468+
'include',
469+
[
470+
var_export($source->resource, true),
471+
['scope' => 'parent'],
472+
]
473+
);
474+
}
475+
$_compiled_code = $this->postFilter($_compiled_code, $this->template);
476476
} else {
477477
// get template source
478478
$_content = $this->template->source->getContent();
479-
$_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
479+
$_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
480480
}
481481
if (!empty($this->required_plugins[ 'compiled' ]) || !empty($this->required_plugins[ 'nocache' ])) {
482482
$_compiled_code = '<?php ' . $this->compileRequiredPlugins() . "?>\n" . $_compiled_code;
@@ -617,7 +617,8 @@ public function compilePHPFunctionCall($name, $parameter)
617617
{
618618
if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
619619
if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0
620-
|| strcasecmp($name, 'array') === 0 || is_callable($name)
620+
|| strcasecmp($name, 'array') === 0
621+
|| (is_callable($name) && !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name]))
621622
) {
622623
$func_name = smarty_strtolower_ascii($name);
623624

@@ -649,28 +650,42 @@ public function compilePHPFunctionCall($name, $parameter)
649650
}
650651
if ($func_name === 'empty') {
651652
return $func_name . '(' .
652-
str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')';
653+
str_replace("')->value", "',null,true,false)->value", $parameter[0]) . ')';
653654
} else {
654-
return $func_name . '(' . $parameter[ 0 ] . ')';
655+
return $func_name . '(' . $parameter[0] . ')';
655656
}
656657
} else {
657658

658-
if (
659-
!$this->smarty->loadPlugin('smarty_modifiercompiler_' . $name)
660-
&& !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name])
661-
&& !in_array($name, ['time', 'join', 'is_array', 'in_array', 'count'])
662-
) {
663-
trigger_error('Using unregistered function "' . $name . '" in a template is deprecated and will be ' .
664-
'removed in a future release. Use Smarty::registerPlugin to explicitly register ' .
665-
'a custom modifier.', E_USER_DEPRECATED);
666-
}
667-
668-
return $name . '(' . implode(',', $parameter) . ')';
659+
if (
660+
!$this->smarty->loadPlugin('smarty_modifiercompiler_' . $name)
661+
&& !isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name])
662+
&& !in_array($name, ['time', 'join', 'is_array', 'in_array', 'count'])
663+
) {
664+
trigger_error('Using unregistered function "' . $name . '" in a template is deprecated and will be ' .
665+
'removed in a future release. Use Smarty::registerPlugin to explicitly register ' .
666+
'a custom modifier.', E_USER_DEPRECATED);
667+
}
668+
669+
return $name . '(' . implode(',', $parameter) . ')';
669670
}
670-
} else {
671-
$this->trigger_template_error("unknown function '{$name}'");
671+
672672
}
673673
}
674+
675+
if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name])) {
676+
if ($name === $this->smarty->registered_plugins[Smarty::PLUGIN_MODIFIER][$name][0]) {
677+
return $name . '(' . implode(',', $parameter) . ')';
678+
}
679+
680+
return sprintf(
681+
'call_user_func_array($_smarty_tpl->registered_plugins[ \'%s\' ][ %s ][ 0 ], array( %s ))',
682+
Smarty::PLUGIN_MODIFIER,
683+
var_export($name, true),
684+
implode(',', $parameter)
685+
);
686+
}
687+
688+
$this->trigger_template_error("unknown function '{$name}'");
674689
}
675690

676691
/**
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
// first class callables where introduced in PHP 8.1
3+
if (PHP_VERSION_ID >= 80100) {
4+
5+
/**
6+
* class for register modifier with (first class) callables tests
7+
*
8+
* @runTestsInSeparateProcess
9+
* @preserveGlobalState disabled
10+
* @backupStaticAttributes enabled
11+
*/
12+
class RegisterModifierFirstClassCallablesTest extends PHPUnit_Smarty
13+
{
14+
public function setUp(): void
15+
{
16+
$this->setUpSmarty(__DIR__);
17+
}
18+
19+
20+
public function testInit()
21+
{
22+
$this->cleanDirs();
23+
}
24+
25+
public function testRegisterFirstClassCallable()
26+
{
27+
$this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'testmodifier', eval('return strrev(...);'));
28+
$this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|testmodifier}'));
29+
}
30+
31+
public function testRegisterFirstClassCallableSameName()
32+
{
33+
$this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'mymodifier', eval('return strrev(...);'));
34+
$this->assertEquals('mosredna', $this->smarty->fetch('string:{"andersom"|mymodifier}'));
35+
}
36+
37+
public function testRegisterFirstClassCallableAsFunc()
38+
{
39+
$this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'kprint_r_out', eval('return strrev(...);'));
40+
$this->smarty->assign('myVar', 'andersom');
41+
$this->assertEquals('mosredna', $this->smarty->fetch('string:{kprint_r_out($myVar)}'));
42+
}
43+
44+
public function testRegisterFirstClassCallableSameNameAsPhpFunc()
45+
{
46+
$this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'mymodifierfcc', eval('return strrev(...);'));
47+
$this->assertEquals('mosredna', $this->smarty->fetch('string:{mymodifierfcc("andersom")}'));
48+
}
49+
50+
}
51+
}
52+
function mymodifierfcc($a, $b, $c)
53+
{
54+
return "$a function $b $c";
55+
}

tests/UnitTests/SmartyMethodsTests/RegisterModifier/RegisterModifierTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ public function testUnregisterModifierOtherRegistered()
8888
$this->smarty->unregisterPlugin(Smarty::PLUGIN_MODIFIER, 'testmodifier');
8989
$this->assertTrue(isset($this->smarty->registered_plugins[Smarty::PLUGIN_BLOCK]['testmodifier']));
9090
}
91+
92+
93+
public function testRegisterNativePhpFuncAsString()
94+
{
95+
$this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'strrev', 'strrev');
96+
$this->smarty->assign('myVar', 'andersom');
97+
$this->assertEquals('mosredna', $this->smarty->fetch('string:{strrev($myVar)}'));
98+
}
99+
100+
public function testRegisterNativePhpFuncUnderDifferentName()
101+
{
102+
$this->smarty->registerPlugin(Smarty::PLUGIN_MODIFIER, 'k_xyz_a', 'strrev');
103+
$this->smarty->assign('myVar', 'andersom');
104+
$this->assertEquals('mosredna', $this->smarty->fetch('string:{k_xyz_a($myVar)}'));
105+
}
106+
91107
}
92108

93109
function mymodifier($a, $b, $c)

0 commit comments

Comments
 (0)