Skip to content

Commit

Permalink
Using PHP functions as modifiers now triggers a deprecation notice.
Browse files Browse the repository at this point in the history
Fixes #813
  • Loading branch information
wisskid committed Sep 22, 2022
1 parent 612bd3f commit 2aa0b3f
Show file tree
Hide file tree
Showing 20 changed files with 271 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
36 changes: 36 additions & 0 deletions libs/plugins/modifier.count.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty count modifier plugin
* Type: modifier
* Name: count
* Purpose: counts all elements in an array or in a Countable object
* Input:
* - Countable|array: array or object to count
* - mode: int defaults to 0 for normal count mode, if set to 1 counts recursive
*
* @param mixed $arrayOrObject input array/object
* @param int $mode count mode
*
* @return int
*/
function smarty_modifier_count($arrayOrObject, $mode = 0)
{
/*
* @see https://www.php.net/count
* > 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;
}
23 changes: 23 additions & 0 deletions libs/plugins/modifiercompiler.nl2br.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty nl2br modifier plugin
* Type: modifier
* Name: nl2br
* Purpose: insert HTML line breaks before all newlines in a string
*
* @link https://www.smarty.net/docs/en/language.modifier.nl2br.tpl nl2br (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_nl2br($params) {
return 'nl2br((string) ' . $params[0] . ', (bool) ' . ($params[1] ?? true) . ')';
}
23 changes: 23 additions & 0 deletions libs/plugins/modifiercompiler.round.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty round modifier plugin
* Type: modifier
* Name: round
* Purpose: Returns the rounded value of num to specified precision (number of digits after the decimal point)
*
* @link https://www.smarty.net/docs/en/language.modifier.round.tpl round (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_round($params) {
return 'round((float) ' . $params[0] . ', (int) ' . ($params[1] ?? 0) . ', (int) ' . ($params[2] ?? PHP_ROUND_HALF_UP) . ')';
}
23 changes: 23 additions & 0 deletions libs/plugins/modifiercompiler.str_repeat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty str_repeat modifier plugin
* Type: modifier
* Name: str_repeat
* Purpose: returns string repeated times times
*
* @link https://www.smarty.net/docs/en/language.modifier.str_repeat.tpl str_repeat (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_str_repeat($params) {
return 'str_repeat((string) ' . $params[0] . ', (int) ' . $params[1] . ')';
}
23 changes: 23 additions & 0 deletions libs/plugins/modifiercompiler.strlen.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty strlen modifier plugin
* Type: modifier
* Name: strlen
* Purpose: return the length of the given string
*
* @link https://www.smarty.net/docs/en/language.modifier.strlen.tpl strlen (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_strlen($params) {
return 'strlen((string) ' . $params[0] . ')';
}
3 changes: 3 additions & 0 deletions libs/sysplugins/smarty_internal_compile_private_modifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $
if (!is_object($compiler->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;
Expand Down
4 changes: 2 additions & 2 deletions libs/sysplugins/smarty_security.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -328,7 +328,7 @@ public function isTrustedStaticClassAccess($class_name, $params, $compiler)
*
* @param string $modifier_name
* @param object $compiler compiler object
*
* @deprecated
* @return boolean true if modifier is trusted
*/
public function isTrustedPhpModifier($modifier_name, $compiler)
Expand Down
19 changes: 11 additions & 8 deletions tests/UnitTests/SecurityTests/SecurityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function testSecurityLoaded()
*/
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)}'));
}

/**
Expand All @@ -63,9 +63,9 @@ public function testTrustedPHPFunction()
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)}');
}

/**
Expand All @@ -75,38 +75,41 @@ public function testDisabledTrustedPHPFunction()
{
$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}'));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ public function testRegisterBlockFunctionModifier1()
{
$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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ public function testRegisteredObjectBlockFunction()

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function setUp(): void
$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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function setUp(): void
$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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function setUp(): void
$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()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* Smarty PHPunit tests of modifier
*/

/**
* class for modifier tests
*
* @runTestsInSeparateProcess
* @preserveGlobalState disabled
* @backupStaticAttributes enabled
*/
class PluginModifierCountTest extends PHPUnit_Smarty
{
public function setUp(): void
{
$this->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));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class PluginModifierExplodeTest extends \PHPUnit_Smarty
public function setUp(): void
{
$this->setUpSmarty(__DIR__);
$this->smarty->registerPlugin('modifier', 'json_encode', 'json_encode');
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
/**
* Smarty PHPunit tests of modifier
*/

/**
* class for modifier tests
*
* @runTestsInSeparateProcess
* @preserveGlobalState disabled
* @backupStaticAttributes enabled
*/
class PluginModifierNl2brTest extends PHPUnit_Smarty
{
public function setUp(): void
{
$this->setUpSmarty(dirname(__FILE__));
}

public function testDefault()
{
$tpl = $this->smarty->createTemplate('string:{$v|nl2br}');
$tpl->assign("v", "Line1\nLine2");
$this->assertEquals("Line1<br />\nLine2", $this->smarty->fetch($tpl));
}

public function testNoXHTML()
{
$tpl = $this->smarty->createTemplate('string:{$v|nl2br:false}');
$tpl->assign("v", "Line1\nLine2");
$this->assertEquals("Line1<br>\nLine2", $this->smarty->fetch($tpl));
}
}
Loading

0 comments on commit 2aa0b3f

Please sign in to comment.