diff --git a/src/BlockHandler/BlockPluginWrapper.php b/src/BlockHandler/BlockPluginWrapper.php index ed5ee9b0..95340ee4 100644 --- a/src/BlockHandler/BlockPluginWrapper.php +++ b/src/BlockHandler/BlockPluginWrapper.php @@ -13,6 +13,6 @@ class BlockPluginWrapper implements BlockHandlerInterface { } public function handle($params, $content, Template $template, &$repeat) { - return call_user_func($this->callback, $params, $content, $template, $repeat); + return call_user_func_array($this->callback, [$params, $content, $template, &$repeat]); } } \ No newline at end of file diff --git a/src/BlockHandler/TextFormat.php b/src/BlockHandler/TextFormat.php index 70e7698b..af545fa9 100644 --- a/src/BlockHandler/TextFormat.php +++ b/src/BlockHandler/TextFormat.php @@ -2,6 +2,7 @@ namespace Smarty\BlockHandler; +use Smarty\Smarty; use Smarty\Template; /** @@ -78,8 +79,8 @@ class TextFormat implements BlockHandlerInterface { $_paragraph = preg_replace( array( - '!\s+!' . \Smarty::$_UTF8_MODIFIER, - '!(^\s+)|(\s+$)!' . \Smarty::$_UTF8_MODIFIER + '!\s+!' . Smarty::$_UTF8_MODIFIER, + '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER ), array( ' ', diff --git a/src/Compile/BlockCompiler.php b/src/Compile/BlockCompiler.php index 0e66dfac..f7b0660d 100644 --- a/src/Compile/BlockCompiler.php +++ b/src/Compile/BlockCompiler.php @@ -10,6 +10,10 @@ namespace Smarty\Compile; +use Smarty\Compiler\Template; +use Smarty\CompilerException; +use Smarty\Exception; + /** * Smarty Internal Plugin Compile Block Plugin Class * @@ -33,78 +37,117 @@ class BlockCompiler extends Base { */ public $nesting = 0; + /** * Compiles code for the execution of block plugin * * @param array $args array with attributes from parser - * @param \Smarty\Compiler\Template $compiler compiler object + * @param Template $compiler compiler object * @param array $parameter array with compilation parameter * @param string $tag name of block plugin * @param string $function PHP function name * * @return string compiled code - * @throws \Smarty\CompilerException - * @throws \Smarty\Exception + * @throws CompilerException + * @throws Exception */ - public function compile($args, \Smarty\Compiler\Template $compiler, $parameter = [], $tag = null, $function = null) { + public function compile($args, Template $compiler, $parameter = [], $tag = null, $function = null) { + if (!isset($tag[5]) || substr($tag, -5) !== 'close') { - // opening tag of block plugin - // check and get attributes - $_attr = $this->getAttributes($compiler, $args); - $this->nesting++; - unset($_attr['nocache']); - [$callback, $_paramsArray, $callable] = $this->setup($compiler, $_attr, $tag, $function); - $_params = 'array(' . implode(',', $_paramsArray) . ')'; - // compile code - $output = "nesting} = isset({$callback[0]}) ? {$callback[0]} : null;\n"; - $callback = "\$_block_plugin{$this->nesting}{$callback[1]}"; - } - if (isset($callable)) { - $output .= "if (!is_callable({$callable})) {\nthrow new \\Smarty\\Exception('block tag \'{$tag}\' not callable or registered');\n}\n"; - } - $output .= "\$_block_repeat=true;\necho {$callback}({$_params}, null, \$_smarty_tpl, \$_block_repeat);\nwhile (\$_block_repeat) {\nob_start();?>"; - $this->openTag($compiler, $tag, [$_params, $compiler->nocache, $callback]); - // maybe nocache because of nocache variables or nocache plugin - $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + $output = $this->compileOpeningTag($compiler, $args, $tag, $function); } else { - // must endblock be nocache? - if ($compiler->nocache) { - $compiler->tag_nocache = true; - } - // closing tag of block plugin, restore nocache - [$_params, $compiler->nocache, $callback] = $this->closeTag($compiler, substr($tag, 0, -5)); - // compile code - if (!isset($parameter['modifier_list'])) { - $mod_pre = $mod_post = $mod_content = ''; - $mod_content2 = 'ob_get_clean()'; - } else { - $mod_content2 = "\$_block_content{$this->nesting}"; - $mod_content = "\$_block_content{$this->nesting} = ob_get_clean();\n"; - $mod_pre = "ob_start();\n"; - $mod_post = 'echo ' . $compiler->compileModifier($parameter['modifier_list'], 'ob_get_clean()') - . ";\n"; - } - $output = - "'; + $output = $this->compileClosingTag($compiler, $tag, $parameter, $function); } return $output; } /** - * Setup callback and parameter array + * Returns the code used for the isset check * - * @param \Smarty\Compiler\Template $compiler - * @param array $_attr attributes - * @param string $tag - * @param string $function + * @param string $tag tag name + * @param string $function base tag or method name * - * @return array + * @return string */ - protected function setup(\Smarty\Compiler\Template $compiler, $_attr, $tag, $function) { - $_paramsArray = $this->formatParamsArray($_attr); - return [$function, $_paramsArray, null]; + protected function getIsCallableCode($tag, $function): string { + return "\$_smarty_tpl->smarty->getBlockHandler(" . var_export($function, true) . ")"; } + + /** + * Returns the full code used to call the callback + * + * @param string $tag tag name + * @param string $function base tag or method name + * + * @return string + */ + protected function getFullCallbackCode($tag, $function): string { + return "\$_smarty_tpl->smarty->getBlockHandler(" . var_export($function, true) . ")->handle"; + } + + /** + * @param Template $compiler + * @param array $args + * @param string|null $tag + * @param string|null $function + * + * @return string + */ + private function compileOpeningTag(Template $compiler, array $args, ?string $tag, ?string $function): string { + + // check and get attributes + $_attr = $this->getAttributes($compiler, $args); + $this->nesting++; + unset($_attr['nocache']); + $_params = 'array(' . implode(',', $this->formatParamsArray($_attr)) . ')'; + + // compile code + $output = "getIsCallableCode($tag, $function) .") {\nthrow new \\Smarty\\Exception('block tag \'{$tag}\' not callable or registered');\n}\n +echo " . $this->getFullCallbackCode($tag, $function) . "({$_params}, null, \$_smarty_tpl, \$_block_repeat); +while (\$_block_repeat) { + ob_start(); +?>"; + $this->openTag($compiler, $tag, [$_params, $compiler->nocache]); + // maybe nocache because of nocache variables or nocache plugin + $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; + return $output; + } + + /** + * @param Template $compiler + * @param string $tag + * @param array $parameter + * @param string|null $function + * + * @return string + * @throws CompilerException + * @throws Exception + */ + private function compileClosingTag(Template $compiler, string $tag, array $parameter, ?string $function): string { + // must endblock be nocache? + if ($compiler->nocache) { + $compiler->tag_nocache = true; + } + // closing tag of block plugin, restore nocache + $base_tag = substr($tag, 0, -5); + [$_params, $compiler->nocache] = $this->closeTag($compiler, $base_tag); + // compile code + if (!isset($parameter['modifier_list'])) { + $mod_pre = $mod_post = $mod_content = ''; + $mod_content2 = 'ob_get_clean()'; + } else { + $mod_content2 = "\$_block_content{$this->nesting}"; + $mod_content = "\$_block_content{$this->nesting} = ob_get_clean();\n"; + $mod_pre = "ob_start();\n"; + $mod_post = 'echo ' . $compiler->compileModifier($parameter['modifier_list'], 'ob_get_clean()') + . ";\n"; + } + $output = "getFullCallbackCode($base_tag, $function); + $output .= "echo {$callback}({$_params}, {$mod_content2}, \$_smarty_tpl, \$_block_repeat);\n"; + $output .= "{$mod_post}}\n?>"; + return $output; + } + } diff --git a/src/Compile/ObjectMethodBlockCompiler.php b/src/Compile/ObjectMethodBlockCompiler.php index 71853639..18df7bcc 100644 --- a/src/Compile/ObjectMethodBlockCompiler.php +++ b/src/Compile/ObjectMethodBlockCompiler.php @@ -11,6 +11,8 @@ namespace Smarty\Compile; use Smarty\Compiler\Template; +use Smarty\CompilerException; +use Smarty\Exception; /** * Smarty Internal Plugin Compile Object Block Function Class @@ -21,18 +23,19 @@ use Smarty\Compiler\Template; class ObjectMethodBlockCompiler extends BlockCompiler { /** - * Setup callback and parameter array - * - * @param Template $compiler - * @param array $_attr attributes - * @param string $tag - * @param string $function - * - * @return array + * @inheritDoc */ - protected function setup(Template $compiler, $_attr, $tag, $function) { - $_paramsArray = $this->formatParamsArray($_attr); - $callback = ["\$_smarty_tpl->smarty->registered_objects['{$tag}'][0]", "->{$function}"]; - return [$callback, $_paramsArray, "array(\$_block_plugin{$this->nesting}, '{$function}')"]; + protected function getIsCallableCode($tag, $function): string { + $callbackObject = "\$_smarty_tpl->smarty->registered_objects['{$tag}'][0]"; + return "(isset({$callbackObject}) && is_callable(array({$callbackObject}, '{$function}')))"; } + + /** + * @inheritDoc + */ + protected function getFullCallbackCode($tag, $function): string { + $callbackObject = "\$_smarty_tpl->smarty->registered_objects['{$tag}'][0]"; + return "{$callbackObject}->{$function}"; + } + } diff --git a/src/Compiler/Template.php b/src/Compiler/Template.php index 96b177c2..863b30c0 100644 --- a/src/Compiler/Template.php +++ b/src/Compiler/Template.php @@ -286,6 +286,10 @@ class Template extends BaseCompiler { * @var string */ public $postfixCompiledCode = ''; + /** + * @var ObjectMethodBlockCompiler + */ + private $objectMethodBlockCompiler; /** * Initialize compiler @@ -302,6 +306,8 @@ class Template extends BaseCompiler { '_', uniqid(mt_rand(), true) ); + + $this->objectMethodBlockCompiler = new ObjectMethodBlockCompiler(); } /** @@ -1424,7 +1430,7 @@ class Template extends BaseCompiler { || in_array($method, $this->smarty->registered_objects[$base_tag][1]); if ($allowedAsBlockFunction) { - return (new ObjectMethodBlockCompiler())->compile($args, $this, $parameter, $tag, $method); + return $this->objectMethodBlockCompiler->compile($args, $this, $parameter, $tag, $method); } elseif ($allowedAsNormalFunction) { return (new ObjectMethodCallCompiler())->compile($args, $this, $parameter, $tag, $method); } @@ -1439,7 +1445,7 @@ class Template extends BaseCompiler { // closing tag if ($allowedAsBlockFunction) { - return (new ObjectMethodBlockCompiler())->compile($args, $this, $parameter, $tag, $method); + return $this->objectMethodBlockCompiler->compile($args, $this, $parameter, $tag, $method); } $this->trigger_template_error( diff --git a/tests/UnitTests/TemplateSource/TagTests/BlockPlugin/CompileBlockPluginTest.php b/tests/UnitTests/TemplateSource/TagTests/BlockPlugin/CompileBlockPluginTest.php index fd2c1224..13887877 100644 --- a/tests/UnitTests/TemplateSource/TagTests/BlockPlugin/CompileBlockPluginTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/BlockPlugin/CompileBlockPluginTest.php @@ -57,10 +57,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin function definition in script - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRegisteredFunction() { @@ -70,9 +66,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin function definition in script - * - * @runInSeparateProcess - * @preserveGlobalState disabled */ public function testBlockPluginRegisteredFunction2() { @@ -83,10 +76,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin static method - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRegisteredStatic() { @@ -96,10 +85,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin static method failure - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRegisteredStatic2() { @@ -110,10 +95,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin object method - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRegisteredMethod() { @@ -124,9 +105,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin object method failure - * - * @runInSeparateProcess - * @preserveGlobalState disabled */ public function testBlockPluginRegisteredMethod2() { @@ -137,10 +115,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin registered object - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRegisteredObject() { @@ -151,23 +125,16 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin registered object failure - * - * @runInSeparateProcess - * @preserveGlobalState disabled */ public function testBlockPluginRegisteredObject2() { $this->expectException(\Smarty\Exception::class); - $this->expectExceptionMessage('block tag \'myobject\' not callable'); + $this->expectExceptionMessage('block tag \'myobject\' not callable or registered'); $this->assertEquals('object block test', $this->smarty->fetch('registered_object.tpl')); } /** * test block plugin repeat function - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRepeat() { @@ -176,10 +143,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin repeat function with modifier - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRepeatModidier1() { @@ -188,10 +151,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin repeat function with modifier list - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginRepeatModidier2() { @@ -200,10 +159,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test block plugin with no output - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginNoOutput() { @@ -212,10 +167,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test nested block plugin - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginNested() { @@ -224,10 +175,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test default block plugin - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginDefault1() { @@ -237,10 +184,6 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * test default block plugin - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * */ public function testBlockPluginDefault2() { @@ -251,10 +194,7 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * Test caching * - * @runInSeparateProcess - * @preserveGlobalState disabled * @dataProvider data - * */ public function testCache($isCached, $caching, @@ -304,9 +244,7 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * Test spacings * - * @preserveGlobalState disabled * @dataProvider dataTestSpacing - * @runInSeparateProcess */ public function testSpacing($code, $result, $testName, $testNumber) { @@ -345,9 +283,7 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * Test spacings * - * @preserveGlobalState disabled * @dataProvider dataTestDefaultSpacing - * @runInSeparateProcess */ public function testSpacingDefault($code, $result, $testName, $testNumber) { @@ -389,9 +325,7 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * Test nocache block spacings * - * @preserveGlobalState disabled * @dataProvider dataTestNocacheSpacing - * @runInSeparateProcess */ public function testBlockNocache($code, $result, $testName, $testNumber) { @@ -409,9 +343,7 @@ class CompileBlockPluginTest extends PHPUnit_Smarty /** * Test nocache block spacings * - * @preserveGlobalState disabled * @dataProvider dataTestNocacheSpacing - * @runInSeparateProcess */ public function testBlockNocache2($code, $result, $testName, $testNumber) { @@ -446,7 +378,7 @@ class CompileBlockPluginTest extends PHPUnit_Smarty } } -function myblockplugintest($params, $content, &$smarty_tpl, &$repeat) +function myblockplugintest($params, $content, $smarty_tpl, &$repeat) { if (!$repeat) { $output = str_replace('hello world', 'block test', $content); @@ -455,7 +387,7 @@ function myblockplugintest($params, $content, &$smarty_tpl, &$repeat) } } -function myblockplugintest2($params, $content, &$smarty_tpl, &$repeat) +function myblockplugintest2($params, $content, $smarty_tpl, &$repeat) { if (!$repeat) { $output = str_replace('hello world', "block test{$params['var']}", $content); @@ -466,7 +398,7 @@ function myblockplugintest2($params, $content, &$smarty_tpl, &$repeat) class myblockclass1 { - static function staticfunc($params, $content, &$smarty_tpl, &$repeat) + static function staticfunc($params, $content, $smarty_tpl, &$repeat) { if (!$repeat) { $output = str_replace('hello world', 'static block test', $content); @@ -474,7 +406,7 @@ class myblockclass1 } } - public function methodfunc($params, $content, &$smarty_tpl, &$repeat) + public function methodfunc($params, $content, $smarty_tpl, &$repeat) { if (!$repeat) { $output = str_replace('hello world', 'method block test', $content); @@ -482,7 +414,7 @@ class myblockclass1 } } - public function objectfunc($params, $content, &$smarty_tpl, &$repeat) + public function objectfunc($params, $content, $smarty_tpl, &$repeat) { if (!$repeat) { $output = str_replace('hello world', 'object block test', $content);