mirror of
https://github.com/smarty-php/smarty.git
synced 2025-08-02 17:34:26 +02:00
Convert isset and empty to modifiercomilers, and smooth the error handling to fix unit tests.
This commit is contained in:
@@ -55,23 +55,28 @@ class FunctionCallCompiler extends Base {
|
||||
$_attr = $this->getAttributes($compiler, $args);
|
||||
unset($_attr['nocache']);
|
||||
|
||||
if (!$functionHandler = $compiler->getSmarty()->getFunctionHandler($function)) {
|
||||
throw new CompilerException("Cannot compile unknown function $function.");
|
||||
}
|
||||
|
||||
// not cacheable?
|
||||
$compiler->tag_nocache = $compiler->tag_nocache || !$functionHandler->isCacheable();
|
||||
|
||||
$_paramsArray = $this->formatParamsArray($_attr);
|
||||
|
||||
$_params = 'array(' . implode(',', $_paramsArray) . ')';
|
||||
|
||||
$output = "\$_smarty_tpl->getSmarty()->getFunctionHandler(" . var_export($function, true) . ")";
|
||||
$output .= "->handle($_params, \$_smarty_tpl)";
|
||||
try {
|
||||
$value = array_shift($_attr);
|
||||
$output = $compiler->compileModifier([array_merge([$function], $_attr)], $value);
|
||||
} catch (\Smarty\CompilerException $e) {
|
||||
if ($functionHandler = $compiler->getSmarty()->getFunctionHandler($function)) {
|
||||
|
||||
// not cacheable?
|
||||
$compiler->tag_nocache = $compiler->tag_nocache || !$functionHandler->isCacheable();
|
||||
$output = "\$_smarty_tpl->getSmarty()->getFunctionHandler(" . var_export($function, true) . ")";
|
||||
$output .= "->handle($_params, \$_smarty_tpl)";
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($parameter['modifierlist'])) {
|
||||
$output = $compiler->compileModifier($parameter['modifierlist'], $output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
19
src/Compile/Modifier/EmptyModifierCompiler.php
Normal file
19
src/Compile/Modifier/EmptyModifierCompiler.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace Smarty\Compile\Modifier;
|
||||
use Smarty\CompilerException;
|
||||
|
||||
/**
|
||||
* Smarty empty modifier plugin
|
||||
*/
|
||||
class EmptyModifierCompiler extends Base {
|
||||
|
||||
public function compile($params, \Smarty\Compiler\Template $compiler) {
|
||||
|
||||
if (count($params) !== 1) {
|
||||
throw new CompilerException("Invalid number of arguments for empty. empty expects exactly 1 parameter.");
|
||||
}
|
||||
|
||||
return 'empty(' . $params[0] . ')';
|
||||
}
|
||||
|
||||
}
|
25
src/Compile/Modifier/IssetModifierCompiler.php
Normal file
25
src/Compile/Modifier/IssetModifierCompiler.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace Smarty\Compile\Modifier;
|
||||
use Smarty\CompilerException;
|
||||
|
||||
/**
|
||||
* Smarty isset modifier plugin
|
||||
*/
|
||||
class IssetModifierCompiler extends Base {
|
||||
|
||||
public function compile($params, \Smarty\Compiler\Template $compiler) {
|
||||
|
||||
$params = array_filter($params, function($v) { return !empty($v); });
|
||||
|
||||
if (count($params) < 1) {
|
||||
throw new CompilerException("Invalid number of arguments for isset. isset expects at least one parameter.");
|
||||
}
|
||||
|
||||
$tests = [];
|
||||
foreach ($params as $param) {
|
||||
$tests[] = 'null !== (' . $param . ' ?? null)';
|
||||
}
|
||||
return '(' . implode(' && ', $tests) . ')';
|
||||
}
|
||||
|
||||
}
|
@@ -11,6 +11,8 @@
|
||||
namespace Smarty\Compile;
|
||||
|
||||
use Smarty\Compile\Base;
|
||||
use Smarty\Compiler\Template;
|
||||
use Smarty\CompilerException;
|
||||
|
||||
/**
|
||||
* Smarty Internal Plugin Compile Modifier Class
|
||||
@@ -35,9 +37,8 @@ class ModifierCompiler extends Base {
|
||||
|
||||
$compiler->has_code = true;
|
||||
|
||||
// check and get attributes
|
||||
$_attr = $this->getAttributes($compiler, $args);
|
||||
$output = $parameter['value'];
|
||||
|
||||
// loop over list of modifiers
|
||||
foreach ($parameter['modifierlist'] as $single_modifier) {
|
||||
/* @var string $modifier */
|
||||
@@ -74,4 +75,19 @@ class ModifierCompiler extends Base {
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wether this class will be able to compile the given modifier.
|
||||
* @param string $modifier
|
||||
* @param Template $compiler
|
||||
*
|
||||
* @return bool
|
||||
* @throws CompilerException
|
||||
*/
|
||||
public function canCompileForModifier(string $modifier, \Smarty\Compiler\Template $compiler): bool {
|
||||
return $compiler->getModifierCompiler($modifier)
|
||||
|| $compiler->getSmarty()->getModifierCallback($modifier)
|
||||
|| $compiler->getPluginFromDefaultHandler($modifier, \Smarty\Smarty::PLUGIN_MODIFIERCOMPILER)
|
||||
|| $compiler->getPluginFromDefaultHandler($modifier, \Smarty\Smarty::PLUGIN_MODIFIER);
|
||||
}
|
||||
}
|
||||
|
35
src/Extension/CallbackWrapper.php
Normal file
35
src/Extension/CallbackWrapper.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Smarty\Extension;
|
||||
|
||||
use Smarty\Exception;
|
||||
|
||||
class CallbackWrapper {
|
||||
|
||||
/**
|
||||
* @var callback
|
||||
*/
|
||||
private $callback;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $modifierName;
|
||||
|
||||
/**
|
||||
* @param string $modifierName
|
||||
* @param callback $callback
|
||||
*/
|
||||
public function __construct(string $modifierName, $callback) {
|
||||
$this->callback = $callback;
|
||||
$this->modifierName = $modifierName;
|
||||
}
|
||||
|
||||
public function handle(...$params) {
|
||||
try {
|
||||
return call_user_func_array($this->callback, $params);
|
||||
} catch (\ArgumentCountError $e) {
|
||||
throw new Exception("Invalid number of arguments to modifier " . $this->modifierName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -23,9 +23,11 @@ class DefaultExtension extends Base {
|
||||
case 'count_sentences': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountSentencesModifierCompiler(); break;
|
||||
case 'count_words': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountWordsModifierCompiler(); break;
|
||||
case 'default': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\DefaultModifierCompiler(); break;
|
||||
case 'empty': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EmptyModifierCompiler(); break;
|
||||
case 'escape': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EscapeModifierCompiler(); break;
|
||||
case 'from_charset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\FromCharsetModifierCompiler(); break;
|
||||
case 'indent': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IndentModifierCompiler(); break;
|
||||
case 'isset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IssetModifierCompiler(); break;
|
||||
case 'lower': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\LowerModifierCompiler(); break;
|
||||
case 'nl2br': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\Nl2brModifierCompiler(); break;
|
||||
case 'noprint': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\NoPrintModifierCompiler(); break;
|
||||
@@ -72,7 +74,6 @@ class DefaultExtension extends Base {
|
||||
case 'count': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Count(); break;
|
||||
case 'counter': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Counter(); break;
|
||||
case 'cycle': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Cycle(); break;
|
||||
case 'empty': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\EmptyHandler(); break;
|
||||
case 'fetch': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Fetch(); break;
|
||||
case 'html_checkboxes': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlCheckboxes(); break;
|
||||
case 'html_image': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlImage(); break;
|
||||
@@ -83,7 +84,6 @@ class DefaultExtension extends Base {
|
||||
case 'html_table': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlTable(); break;
|
||||
case 'in_array': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\InArray(); break;
|
||||
case 'is_array': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\IsArray(); break;
|
||||
case 'isset': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\IssetHandler(); break;
|
||||
case 'mailto': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Mailto(); break;
|
||||
case 'math': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Math(); break;
|
||||
case 'strlen': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Strlen(); break;
|
||||
|
@@ -20,7 +20,7 @@ class Count extends Base {
|
||||
$params = array_values($params ?? []);
|
||||
|
||||
if (count($params) < 1 || count($params) > 2) {
|
||||
throw new Exception("Invalid number of arguments for count. count expects 2 or 3 parameters.");
|
||||
throw new Exception("Invalid number of arguments for count. count expects 1 or 2 parameters.");
|
||||
}
|
||||
|
||||
$value = $params[0];
|
||||
|
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Smarty\FunctionHandler;
|
||||
|
||||
use Smarty\Exception;
|
||||
use Smarty\Template;
|
||||
|
||||
/**
|
||||
* empty(mixed $var): bool
|
||||
*
|
||||
* Returns true if var does not exist or has a value that is empty or equal to zero, aka falsey, see conversion to
|
||||
* boolean. Otherwise returns false.
|
||||
*
|
||||
* No warning is generated if the variable does not exist. That means empty() is essentially the concise equivalent
|
||||
* to !isset($var) || $var == false.
|
||||
*/
|
||||
class EmptyHandler extends Base {
|
||||
|
||||
public function handle($params, Template $template) {
|
||||
|
||||
if (count($params) !== 1) {
|
||||
throw new Exception("Invalid number of arguments for empty. empty expects exactly 1 parameter.");
|
||||
}
|
||||
return empty(reset($params));
|
||||
}
|
||||
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Smarty\FunctionHandler;
|
||||
|
||||
use Smarty\Exception;
|
||||
use Smarty\Template;
|
||||
|
||||
/**
|
||||
* Determines if a variable is declared and is different than null
|
||||
* `isset(mixed $var, mixed ...$vars): bool`
|
||||
*
|
||||
* Returns true if var exists and has any value other than null. false otherwise.
|
||||
*/
|
||||
class IssetHandler extends Base {
|
||||
|
||||
public function handle($params, Template $template) {
|
||||
|
||||
if (count($params) === 0) {
|
||||
throw new Exception("Invalid number of arguments for isset. isset expects at least 1 parameter.");
|
||||
}
|
||||
|
||||
foreach ($params as $param) {
|
||||
if (!isset($param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -7,6 +7,7 @@ use RecursiveIteratorIterator;
|
||||
use Smarty\Cacheresource\File;
|
||||
use Smarty\Extension\Base;
|
||||
use Smarty\Extension\BCPluginsAdapter;
|
||||
use Smarty\Extension\CallbackWrapper;
|
||||
use Smarty\Extension\CoreExtension;
|
||||
use Smarty\Extension\DefaultExtension;
|
||||
use Smarty\Extension\ExtensionInterface;
|
||||
@@ -1672,7 +1673,7 @@ class Smarty extends \Smarty\TemplateBase
|
||||
public function getModifierCallback(string $modifierName) {
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
if ($callback = $extension->getModifierCallback($modifierName)) {
|
||||
return $callback;
|
||||
return [new CallbackWrapper($modifierName, $callback), 'handle'];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@@ -32,7 +32,7 @@ class FunctionTest extends PHPUnit_Smarty
|
||||
{
|
||||
$this->smarty->enableSecurity();
|
||||
$this->expectException(\Smarty\CompilerException::class);
|
||||
$this->expectExceptionMessage('Cannot compile unknown function unknown');
|
||||
$this->expectExceptionMessage('unknown modifier');
|
||||
$this->smarty->fetch('eval:{unknown()}');
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ class EmptyTest extends \PHPUnit_Smarty {
|
||||
}
|
||||
|
||||
public function testInvalidParameters() {
|
||||
$this->expectException(\Smarty\Exception::class);
|
||||
$this->expectException(\Smarty\CompilerException::class);
|
||||
$this->expectExceptionMessage('Invalid number of arguments');
|
||||
$this->assertEquals("", $this->smarty->fetch("string:{empty(3, 'foo')}"));
|
||||
}
|
||||
|
@@ -37,9 +37,9 @@ class IssetTest extends \PHPUnit_Smarty {
|
||||
}
|
||||
|
||||
public function testInvalidParameters() {
|
||||
$this->expectException(\Smarty\Exception::class);
|
||||
$this->expectException(\Smarty\CompilerException::class);
|
||||
$this->expectExceptionMessage('Invalid number of arguments');
|
||||
$this->assertEquals("", $this->smarty->fetch("string:{empty(3, 'foo')}"));
|
||||
$this->assertEquals("", $this->smarty->fetch("string:{if isset()}blurp{/if}"));
|
||||
}
|
||||
|
||||
}
|
@@ -57,6 +57,7 @@ class PhpFunctionTest extends PHPUnit_Smarty
|
||||
public function testEmpty2()
|
||||
{
|
||||
$this->smarty->disableSecurity();
|
||||
$this->getSmarty()->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'pass', function ($v) { return $v; });
|
||||
$this->smarty->assign('var', array(null,
|
||||
false,
|
||||
(int) 0,
|
||||
@@ -78,6 +79,7 @@ class PhpFunctionTest extends PHPUnit_Smarty
|
||||
public function testEmpty3()
|
||||
{
|
||||
$this->smarty->disableSecurity();
|
||||
$this->getSmarty()->registerPlugin(\Smarty\Smarty::PLUGIN_FUNCTION, 'pass', function ($v) { return $v; });
|
||||
$this->smarty->assign('var', array(true,
|
||||
(int) 1,
|
||||
(float) 0.1,
|
||||
@@ -97,6 +99,7 @@ class PhpFunctionTest extends PHPUnit_Smarty
|
||||
public function testEmpty4()
|
||||
{
|
||||
$this->smarty->disableSecurity();
|
||||
$this->getSmarty()->registerPlugin(\Smarty\Smarty::PLUGIN_FUNCTION, 'pass', function ($v) { return $v; });
|
||||
$this->smarty->assign('var', new TestIsset());
|
||||
$expected = ' true , false , false , true , true , true , false ';
|
||||
$this->assertEquals($expected, $this->smarty->fetch('string:{strip}{if empty($var->isNull)} true {else} false {/IF}
|
||||
@@ -114,6 +117,7 @@ class PhpFunctionTest extends PHPUnit_Smarty
|
||||
public function testIsset1()
|
||||
{
|
||||
$this->smarty->disableSecurity();
|
||||
$this->getSmarty()->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'pass', function ($v) { return $v; });
|
||||
$this->smarty->assign('isNull', null);
|
||||
$this->smarty->assign('isSet', 1);
|
||||
$this->smarty->assign('arr', array('isNull' => null, 'isSet' => 1));
|
||||
@@ -135,6 +139,7 @@ class PhpFunctionTest extends PHPUnit_Smarty
|
||||
{
|
||||
$this->smarty->disableSecurity();
|
||||
$this->smarty->assign('var', new TestIsset());
|
||||
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'pass', function ($v) { return $v; });
|
||||
$expected = ' false , true , true , false , false , false , true ';
|
||||
$this->assertEquals($expected, $this->smarty->fetch('string:{strip}{if isset($var->isNull)} true {else} false {/IF}
|
||||
,{if isset($var->isSet)} true {else} false {/IF}
|
||||
@@ -155,6 +160,7 @@ class PhpFunctionTest extends PHPUnit_Smarty
|
||||
public function testIsset3($strTemplate, $result)
|
||||
{
|
||||
$this->smarty->disableSecurity();
|
||||
$this->smarty->registerPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, 'intval', 'intval');
|
||||
|
||||
$this->smarty->assign('varobject', new TestIsset());
|
||||
$this->smarty->assign('vararray', $vararray = array(
|
||||
@@ -198,15 +204,6 @@ class PhpFunctionTest extends PHPUnit_Smarty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $v
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function pass($v) {
|
||||
return $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class TestIsset
|
||||
*/
|
||||
|
Reference in New Issue
Block a user