diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a924ca3..80367524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Include docs and demo in the releases [#799](https://github.com/smarty-php/smarty/issues/799) +- Using PHP functions as modifiers now triggers a deprecation notice because we will drop support for this in the next major release [#813](https://github.com/smarty-php/smarty/issues/813) ### Fixed - Output buffer is now cleaned for internal PHP errors as well, not just for Exceptions [#514](https://github.com/smarty-php/smarty/issues/514) diff --git a/libs/plugins/modifier.count.php b/libs/plugins/modifier.count.php new file mode 100644 index 00000000..ca35fc11 --- /dev/null +++ b/libs/plugins/modifier.count.php @@ -0,0 +1,36 @@ + Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface, + * > 1 would be returned, unless value was null, in which case 0 would be returned. + */ + + if ($arrayOrObject instanceof Countable || is_array($arrayOrObject)) { + return count($arrayOrObject, (int) $mode); + } elseif ($arrayOrObject === null) { + return 0; + } + return 1; +} diff --git a/libs/plugins/modifiercompiler.nl2br.php b/libs/plugins/modifiercompiler.nl2br.php new file mode 100644 index 00000000..308c00e4 --- /dev/null +++ b/libs/plugins/modifiercompiler.nl2br.php @@ -0,0 +1,23 @@ +smarty->security_policy) || $compiler->smarty->security_policy->isTrustedPhpModifier($modifier, $compiler) ) { + trigger_error('Using php-function "' . $modifier . '" as a modifier is deprecated and will be ' . + 'removed in a future release. Use Smarty::registerPlugin to explicitly register ' . + 'a custom modifier.', E_USER_DEPRECATED); $output = "{$modifier}({$params})"; } $compiler->known_modifier_type[ $modifier ] = $type; diff --git a/libs/sysplugins/smarty_security.php b/libs/sysplugins/smarty_security.php index 3c29c813..42c2a766 100644 --- a/libs/sysplugins/smarty_security.php +++ b/libs/sysplugins/smarty_security.php @@ -105,7 +105,7 @@ class Smarty_Security * * @var array */ - public $php_modifiers = array('escape', 'count', 'nl2br',); + public $php_modifiers = array('escape', 'count', 'sizeof', 'nl2br',); /** * This is an array of allowed tags. @@ -328,7 +328,7 @@ class Smarty_Security * * @param string $modifier_name * @param object $compiler compiler object - * + * @deprecated * @return boolean true if modifier is trusted */ public function isTrustedPhpModifier($modifier_name, $compiler) diff --git a/tests/UnitTests/SecurityTests/SecurityTest.php b/tests/UnitTests/SecurityTests/SecurityTest.php index d68ac3d4..60d80d67 100644 --- a/tests/UnitTests/SecurityTests/SecurityTest.php +++ b/tests/UnitTests/SecurityTests/SecurityTest.php @@ -52,7 +52,7 @@ class SecurityTest extends PHPUnit_Smarty */ public function testTrustedPHPFunction() { - $this->assertEquals("5", $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{count($foo)}')); + $this->assertEquals("5", $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{sizeof($foo)}')); } /** @@ -63,9 +63,9 @@ class SecurityTest extends PHPUnit_Smarty public function testNotTrustedPHPFunction() { $this->expectException('SmartyException'); - $this->expectExceptionMessage('PHP function \'count\' not allowed by security setting'); + $this->expectExceptionMessage('PHP function \'sizeof\' not allowed by security setting'); $this->smarty->security_policy->php_functions = array('null'); - $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{count($foo)}'); + $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{sizeof($foo)}'); } /** @@ -75,38 +75,41 @@ class SecurityTest extends PHPUnit_Smarty { $this->smarty->security_policy->php_functions = array('null'); $this->smarty->disableSecurity(); - $this->assertEquals("5", $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{count($foo)}')); + $this->assertEquals("5", $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{sizeof($foo)}')); } /** * test trusted modifier + * @deprecated */ public function testTrustedModifier() { - $this->assertEquals("5", $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{$foo|@count}')); + $this->assertEquals("5", @$this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{$foo|@sizeof}')); } /** * test not trusted modifier * @runInSeparateProcess * @preserveGlobalState disabled + * @deprecated */ public function testNotTrustedModifier() { $this->expectException('SmartyException'); - $this->expectExceptionMessage('modifier \'count\' not allowed by security setting'); + $this->expectExceptionMessage('modifier \'sizeof\' not allowed by security setting'); $this->smarty->security_policy->php_modifiers = array('null'); - $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{$foo|@count}'); + @$this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{$foo|@sizeof}'); } /** * test not trusted modifier at disabled security + * @deprecated */ public function testDisabledTrustedModifier() { $this->smarty->security_policy->php_modifiers = array('null'); $this->smarty->disableSecurity(); - $this->assertEquals("5", $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{$foo|@count}')); + @$this->assertEquals("5", $this->smarty->fetch('string:{assign var=foo value=[1,2,3,4,5]}{$foo|@sizeof}')); } /** diff --git a/tests/UnitTests/SmartyMethodsTests/RegisterBlock/RegisterBlockTest.php b/tests/UnitTests/SmartyMethodsTests/RegisterBlock/RegisterBlockTest.php index 7d72c73d..b79ce9fa 100644 --- a/tests/UnitTests/SmartyMethodsTests/RegisterBlock/RegisterBlockTest.php +++ b/tests/UnitTests/SmartyMethodsTests/RegisterBlock/RegisterBlockTest.php @@ -40,14 +40,14 @@ class RegisterBlockTest extends PHPUnit_Smarty { $this->smarty->registerPlugin(Smarty::PLUGIN_BLOCK, 'testblock', 'myblock'); $this->smarty->assign('value', 1); - $this->assertEquals(strtoupper('function hello world 1 1 function hello world 1 2 function hello world 1 3 '), $this->smarty->fetch('eval:{testblock}hello world {$value}{/testblock|strtoupper}')); + $this->assertEquals(strtoupper('function hello world 1 1 function hello world 1 2 function hello world 1 3 '), $this->smarty->fetch('eval:{testblock}hello world {$value}{/testblock|upper}')); } public function testRegisterBlockFunctionModifier2() { $this->smarty->registerPlugin(Smarty::PLUGIN_BLOCK, 'testblock', 'myblock'); $this->smarty->assign('value', 1); - $this->assertEquals(strtoupper('function hello world 1 1 function hello world 1 2 function hello world 1 3 '), $this->smarty->fetch('eval:{testblock}hello world {$value}{/testblock|default:""|strtoupper}')); + $this->assertEquals(strtoupper('function hello world 1 1 function hello world 1 2 function hello world 1 3 '), $this->smarty->fetch('eval:{testblock}hello world {$value}{/testblock|default:""|upper}')); } public function testRegisterBlockFunctionWrapper() diff --git a/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php b/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php index 59bd3cb8..94a8ef55 100644 --- a/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php +++ b/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php @@ -60,13 +60,13 @@ class CompileRegisteredObjectFunctionTest extends PHPUnit_Smarty public function testRegisteredObjectBlockFunctionModifier1() { - $tpl = $this->smarty->createTemplate('eval:{objecttest->myblock}hello world{/objecttest->myblock|strtoupper}'); + $tpl = $this->smarty->createTemplate('eval:{objecttest->myblock}hello world{/objecttest->myblock|upper}'); $this->assertEquals(strtoupper('block test'), $this->smarty->fetch($tpl)); } public function testRegisteredObjectBlockFunctionModifier2() { - $tpl = $this->smarty->createTemplate('eval:{objecttest->myblock}hello world{/objecttest->myblock|default:""|strtoupper}'); + $tpl = $this->smarty->createTemplate('eval:{objecttest->myblock}hello world{/objecttest->myblock|default:""|upper}'); $this->assertEquals(strtoupper('block test'), $this->smarty->fetch($tpl)); } // TODO diff --git a/tests/UnitTests/TemplateSource/TagTests/Append/CompileAppendTest.php b/tests/UnitTests/TemplateSource/TagTests/Append/CompileAppendTest.php index 76b11052..9fab4fae 100644 --- a/tests/UnitTests/TemplateSource/TagTests/Append/CompileAppendTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/Append/CompileAppendTest.php @@ -21,6 +21,7 @@ class CompileAppendTest extends PHPUnit_Smarty $this->smarty->addPluginsDir("../../../__shared/PHPunitplugins/"); $this->smarty->addTemplateDir("../../../__shared/templates/"); $this->smarty->addTemplateDir("./templates_tmp"); + $this->smarty->registerPlugin('modifier', 'var_export', 'var_export'); } public function testInit() diff --git a/tests/UnitTests/TemplateSource/TagTests/Assign/CompileAssignTest.php b/tests/UnitTests/TemplateSource/TagTests/Assign/CompileAssignTest.php index fefdd72d..fb02564f 100644 --- a/tests/UnitTests/TemplateSource/TagTests/Assign/CompileAssignTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/Assign/CompileAssignTest.php @@ -21,6 +21,7 @@ class CompileAssignTest extends PHPUnit_Smarty $this->smarty->addPluginsDir("../../../__shared/PHPunitplugins/"); $this->smarty->addTemplateDir("../../../__shared/templates/"); $this->smarty->addTemplateDir("./templates_tmp"); + $this->smarty->registerPlugin('modifier', 'var_export', 'var_export'); } public function testInit() diff --git a/tests/UnitTests/TemplateSource/TagTests/If/CompileIfTest.php b/tests/UnitTests/TemplateSource/TagTests/If/CompileIfTest.php index 595ff55c..addd9999 100644 --- a/tests/UnitTests/TemplateSource/TagTests/If/CompileIfTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/If/CompileIfTest.php @@ -21,6 +21,7 @@ class CompileIfTest extends PHPUnit_Smarty $this->smarty->addPluginsDir("../../../__shared/PHPunitplugins/"); $this->smarty->addTemplateDir("../../../__shared/templates/"); $this->smarty->addTemplateDir("./templates_tmp"); + $this->smarty->registerPlugin('modifier', 'var_export', 'var_export'); } public function testInit() diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierCountTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierCountTest.php new file mode 100644 index 00000000..c736356e --- /dev/null +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierCountTest.php @@ -0,0 +1,48 @@ +setUpSmarty(dirname(__FILE__)); + } + + public function testArray() + { + $tpl = $this->smarty->createTemplate('string:count:{$v|count}'); + $tpl->assign("v", array(1, 2, 3)); + $this->assertEquals("count:3", $this->smarty->fetch($tpl)); + } + + public function testEmptyArray() + { + $tpl = $this->smarty->createTemplate('string:count:{$v|count}'); + $tpl->assign("v", array()); + $this->assertEquals("count:0", $this->smarty->fetch($tpl)); + } + + public function testNull() + { + $tpl = $this->smarty->createTemplate('string:count:{$v|count}'); + $tpl->assign("v", null); + $this->assertEquals("count:0", $this->smarty->fetch($tpl)); + } + + public function testString() + { + $tpl = $this->smarty->createTemplate('string:count:{$v|count}'); + $tpl->assign("v", "string"); + $this->assertEquals("count:1", $this->smarty->fetch($tpl)); + } + +} diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierExplodeTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierExplodeTest.php index b3b9d50e..5cd13dd3 100644 --- a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierExplodeTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierExplodeTest.php @@ -14,6 +14,7 @@ class PluginModifierExplodeTest extends \PHPUnit_Smarty public function setUp(): void { $this->setUpSmarty(__DIR__); + $this->smarty->registerPlugin('modifier', 'json_encode', 'json_encode'); } /** diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierNl2brTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierNl2brTest.php new file mode 100644 index 00000000..ed263019 --- /dev/null +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierNl2brTest.php @@ -0,0 +1,33 @@ +setUpSmarty(dirname(__FILE__)); + } + + public function testDefault() + { + $tpl = $this->smarty->createTemplate('string:{$v|nl2br}'); + $tpl->assign("v", "Line1\nLine2"); + $this->assertEquals("Line1
\nLine2", $this->smarty->fetch($tpl)); + } + + public function testNoXHTML() + { + $tpl = $this->smarty->createTemplate('string:{$v|nl2br:false}'); + $tpl->assign("v", "Line1\nLine2"); + $this->assertEquals("Line1
\nLine2", $this->smarty->fetch($tpl)); + } +} diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierStrRepeatTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierStrRepeatTest.php new file mode 100644 index 00000000..ef5bcfde --- /dev/null +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierStrRepeatTest.php @@ -0,0 +1,33 @@ +setUpSmarty(dirname(__FILE__)); + } + + public function testDefault() + { + $tpl = $this->smarty->createTemplate('string:{$v|str_repeat:2}'); + $tpl->assign("v", "foo"); + $this->assertEquals("foofoo", $this->smarty->fetch($tpl)); + } + + public function testZeroTimes() + { + $tpl = $this->smarty->createTemplate('string:{$v|str_repeat:0}'); + $tpl->assign("v", "foo"); + $this->assertEquals("", $this->smarty->fetch($tpl)); + } +} diff --git a/tests/UnitTests/TemplateSource/ValueTests/Math/MathTest.php b/tests/UnitTests/TemplateSource/ValueTests/Math/MathTest.php index 3be6ad2e..b29d5beb 100644 --- a/tests/UnitTests/TemplateSource/ValueTests/Math/MathTest.php +++ b/tests/UnitTests/TemplateSource/ValueTests/Math/MathTest.php @@ -18,6 +18,7 @@ class MathTest extends PHPUnit_Smarty public function setUp(): void { $this->setUpSmarty(dirname(__FILE__)); + $this->smarty->registerPlugin('modifier', 'sin', 'sin'); } public function testInit() @@ -104,7 +105,7 @@ class MathTest extends PHPUnit_Smarty { $this->smarty->disableSecurity(); $expected = "22.00 -- 4.10"; - $tpl = $this->smarty->createTemplate('eval:{$x = 4}{$y = 5.5}{$z = $x * $y}{"%0.2f"|sprintf:$z} -- {$x = 20.5}{$y = 5}{$z = $x / $y}{"%0.2f"|sprintf:$z}'); + $tpl = $this->smarty->createTemplate('eval:{$x = 4}{$y = 5.5}{$z = $x * $y}{$z|string_format:"%0.2f"} -- {$x = 20.5}{$y = 5}{$z = $x / $y}{$z|string_format:"%0.2f"}'); $this->assertEquals($expected, $this->smarty->fetch($tpl)); } @@ -120,7 +121,7 @@ class MathTest extends PHPUnit_Smarty { $this->smarty->disableSecurity(); $expected = "22.00 -- 4.10"; - $tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{$z = $x * $y}{"%0.2f"|sprintf:$z} -- {$x = "20.5"}{$y = "5"}{$z = $x / $y}{"%0.2f"|sprintf:$z}'); + $tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{$z = $x * $y}{$z|string_format:"%0.2f"} -- {$x = "20.5"}{$y = "5"}{$z = $x / $y}{$z|string_format:"%0.2f"}'); $this->assertEquals($expected, $this->smarty->fetch($tpl)); } @@ -132,36 +133,36 @@ class MathTest extends PHPUnit_Smarty $this->assertEquals($expected, $this->smarty->fetch($tpl)); } - public function testBackticksIllegal() - { - $this->expectException(PHPUnit\Framework\Error\Warning::class); - $expected = "22.00"; - $tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="`ls` x * y" x=$x y=$y}'); - $this->assertEquals($expected, $this->smarty->fetch($tpl)); - } + public function testBackticksIllegal() + { + $this->expectException(PHPUnit\Framework\Error\Warning::class); + $expected = "22.00"; + $tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="`ls` x * y" x=$x y=$y}'); + $this->assertEquals($expected, $this->smarty->fetch($tpl)); + } - public function testDollarSignsIllegal() - { - $this->expectException(PHPUnit\Framework\Error\Warning::class); - $expected = "22.00"; - $tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="$" x=$x y=$y}'); - $this->assertEquals($expected, $this->smarty->fetch($tpl)); - } + public function testDollarSignsIllegal() + { + $this->expectException(PHPUnit\Framework\Error\Warning::class); + $expected = "22.00"; + $tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="$" x=$x y=$y}'); + $this->assertEquals($expected, $this->smarty->fetch($tpl)); + } - public function testBracketsIllegal() - { - $this->expectException(PHPUnit\Framework\Error\Warning::class); - $expected = "I"; - $tpl = $this->smarty->createTemplate('eval:{$x = "0"}{$y = "1"}{math equation="((y/x).(x))[x]" x=$x y=$y}'); - $this->assertEquals($expected, $this->smarty->fetch($tpl)); - } + public function testBracketsIllegal() + { + $this->expectException(PHPUnit\Framework\Error\Warning::class); + $expected = "I"; + $tpl = $this->smarty->createTemplate('eval:{$x = "0"}{$y = "1"}{math equation="((y/x).(x))[x]" x=$x y=$y}'); + $this->assertEquals($expected, $this->smarty->fetch($tpl)); + } - public function testRand() - { - $tpl = $this->smarty->createTemplate('eval:{$x = "0"}{math equation="x * rand()" x=$x}'); - // this assertion may seem silly, but it serves to prove that using rand() without a parameter - // will not trigger a security error (see https://github.com/smarty-php/smarty/issues/794) - $this->assertEquals("0", $this->smarty->fetch($tpl)); - } + public function testRand() + { + $tpl = $this->smarty->createTemplate('eval:{$x = "0"}{math equation="x * rand()" x=$x}'); + // this assertion may seem silly, but it serves to prove that using rand() without a parameter + // will not trigger a security error (see https://github.com/smarty-php/smarty/issues/794) + $this->assertEquals("0", $this->smarty->fetch($tpl)); + } } diff --git a/tests/UnitTests/TemplateSource/_Issues/327/ModifierIssue327Test.php b/tests/UnitTests/TemplateSource/_Issues/327/ModifierIssue327Test.php index 8418ea3e..0634907b 100644 --- a/tests/UnitTests/TemplateSource/_Issues/327/ModifierIssue327Test.php +++ b/tests/UnitTests/TemplateSource/_Issues/327/ModifierIssue327Test.php @@ -18,6 +18,7 @@ class ModifierIssue327Test extends PHPUnit_Smarty public function setUp(): void { $this->setUpSmarty(dirname(__FILE__)); + $this->smarty->registerPlugin('modifier', 'substr', 'substr'); } public function testInit()