diff --git a/src/Latte/Macros/BlockMacros.php b/src/Latte/Macros/BlockMacros.php index 4f0e1c9f89..1a42eff11d 100644 --- a/src/Latte/Macros/BlockMacros.php +++ b/src/Latte/Macros/BlockMacros.php @@ -84,7 +84,7 @@ public function finalize() $compiler = $this->getCompiler(); foreach ($this->placeholders as $key => [$index, $blockName]) { $block = $this->blocks[$index][$blockName] ?? $this->blocks[Template::LAYER_LOCAL][$blockName] ?? null; - $compiler->placeholders[$key] = $block + $compiler->placeholders[$key] = $block && !$block->hasArgs ? 'get_defined_vars()' : '[]'; } @@ -336,7 +336,13 @@ public function macroDefine(MacroNode $node, PhpWriter $writer): string if ($tokens->nextToken($tokens::T_SYMBOL, '?', 'null', '\\')) { // type $tokens->nextAll($tokens::T_SYMBOL, '\\', '|', '[', ']', 'null'); } - $args[] = $tokens->consumeValue($tokens::T_VARIABLE); + $arg = $tokens->consumeValue($tokens::T_VARIABLE); + $args[] = $writer->write( + '%raw = $__args[%var] ?? $__args[%var] ?? null;', + $arg, + count($args), + substr($arg, 1) + ); if ($tokens->isNext()) { $tokens->consumeValue(','); } @@ -344,10 +350,11 @@ public function macroDefine(MacroNode $node, PhpWriter $writer): string $extendsCheck = $this->blocks[Template::LAYER_TOP] || count($this->blocks) > 1 || $node->parentNode; $block = $this->addBlock($node, $layer); + $block->hasArgs = (bool) $args; $data->after = function () use ($node, $block, $args) { $args = $args - ? ('[' . implode(', ', $args) . '] = $__args + [' . str_repeat('null, ', count($args)) . '];') + ? implode('', $args) : null; $this->extractMethod($node, $block, $args); }; diff --git a/src/Latte/Runtime/Block.php b/src/Latte/Runtime/Block.php index 473f386661..612b38b037 100644 --- a/src/Latte/Runtime/Block.php +++ b/src/Latte/Runtime/Block.php @@ -21,4 +21,7 @@ final class Block /** @var callable[] used by Template */ public $functions = []; + + /** @var bool used by BlockMacros */ + public $hasArgs = false; } diff --git a/tests/Latte/BlockMacros.define.args.phpt b/tests/Latte/BlockMacros.define.args.phpt index 276f9cde7d..dbb2455dd0 100644 --- a/tests/Latte/BlockMacros.define.args.phpt +++ b/tests/Latte/BlockMacros.define.args.phpt @@ -44,6 +44,7 @@ Assert::matchFile( ); +//typehints $template = <<<'XX' {define test $var1, ?stdClass $var2, \C\B|null $var3} {/define} @@ -55,3 +56,80 @@ Assert::matchFile( __DIR__ . '/expected/BlockMacros.define.typehints.phtml', $latte->compile($template) ); + + +// named arguments +$template = <<<'XX' +named arguments + +{define test $var1, $var2, $var3} + Variables {$var1}, {$var2}, {$var3} +{/define} + +a) {include test, 1, var1 => 2} + +b) {include test, var2 => 1} + +c) {include test, hello => 1} + +d) {include test, var2 => 1, 2} // invalid +XX; + +Assert::matchFile( + __DIR__ . '/expected/BlockMacros.define.args2.phtml', + $latte->compile($template) +); +Assert::matchFile( + __DIR__ . '/expected/BlockMacros.define.args2.html', + $latte->renderToString($template, ['var3' => 'outer']) +); + + +// named arguments (order dependent) +$template = <<<'XX' +named arguments order + +a) {include test, 1, var1 => 2} + +b) {include test, var2 => 1} + +c) {include test, hello => 1} + +{define test $var1, $var2, $var3} + Variables {$var1}, {$var2}, {$var3} +{/define} +XX; + +Assert::matchFile( + __DIR__ . '/expected/BlockMacros.define.args3.phtml', + $latte->compile($template) +); + + +// named arguments (file dependent) +$latte->setLoader(new Latte\Loaders\StringLoader([ + 'main' => ' +named arguments import + +{import import.latte} + +a) {include test, 1, var1 => 2} + +b) {include test, var2 => 1} + +c) {include test, hello => 1}', + + 'import.latte' => ' +{define test $var1, $var2, $var3} + Variables {$var1}, {$var2}, {$var3} +{/define}', +])); + +Assert::matchFile( + __DIR__ . '/expected/BlockMacros.define.args4.phtml', + $latte->compile('main') +); +Assert::matchFile( + __DIR__ . '/expected/BlockMacros.define.args4.html', + $latte->renderToString('main', ['var3' => 'outer']) +); diff --git a/tests/Latte/expected/BlockMacros.define.args1.phtml b/tests/Latte/expected/BlockMacros.define.args1.phtml index a1e0d360c2..ec69ba0840 100644 --- a/tests/Latte/expected/BlockMacros.define.args1.phtml +++ b/tests/Latte/expected/BlockMacros.define.args1.phtml @@ -16,7 +16,7 @@ final class Template%a% extends Latte\Runtime\Template } echo ' a) '; - $this->renderBlock($__nm = 'test', [1] + get_defined_vars(), 'html'); + $this->renderBlock($__nm = 'test', [1] + [], 'html'); echo ' @@ -27,11 +27,11 @@ b) '; '; $var1 = 'outer'; echo 'c) '; - $this->renderBlock($__nm = 'test', get_defined_vars(), 'html'); + $this->renderBlock($__nm = 'test', [], 'html'); echo ' d) '; - $this->renderBlock($__nm = 'test', [null] + get_defined_vars(), 'html'); + $this->renderBlock($__nm = 'test', [null] + [], 'html'); return get_defined_vars(); } @@ -39,7 +39,9 @@ d) '; public function blockTest(array $__args): void { extract($this->params); - [$var1, $var2, $var3] = $__args + [null, null, null, ]; + $var1 = $__args[0] ?? $__args['var1'] ?? null; + $var2 = $__args[1] ?? $__args['var2'] ?? null; + $var3 = $__args[2] ?? $__args['var3'] ?? null; echo ' Variables '; echo LR\Filters::escapeHtmlText($var1) /* line 2 */; echo ', '; @@ -54,7 +56,7 @@ d) '; { extract($this->params); extract($__args); - $this->renderBlock($__nm = 'test', ['hello'] + get_defined_vars(), 'html'); + $this->renderBlock($__nm = 'test', ['hello'] + [], 'html'); } diff --git a/tests/Latte/expected/BlockMacros.define.args2.html b/tests/Latte/expected/BlockMacros.define.args2.html new file mode 100644 index 0000000000..25fcf1765f --- /dev/null +++ b/tests/Latte/expected/BlockMacros.define.args2.html @@ -0,0 +1,14 @@ +named arguments + + +a) Variables 1, , + + +b) Variables , 1, + + +c) Variables , , + + +d) Variables 2, 1, + // invalid diff --git a/tests/Latte/expected/BlockMacros.define.args2.phtml b/tests/Latte/expected/BlockMacros.define.args2.phtml new file mode 100644 index 0000000000..bcfd4886ea --- /dev/null +++ b/tests/Latte/expected/BlockMacros.define.args2.phtml @@ -0,0 +1,55 @@ + 'blockTest'], + ]; + + + public function main(): array + { + extract($this->params); + echo 'named arguments + +'; + if ($this->getParentName()) { + return get_defined_vars(); + } + echo ' +a) '; + $this->renderBlock($__nm = 'test', [1, 'var1' => 2] + [], 'html'); + echo ' + +b) '; + $this->renderBlock($__nm = 'test', ['var2' => 1] + [], 'html'); + echo ' + +c) '; + $this->renderBlock($__nm = 'test', ['hello' => 1] + [], 'html'); + echo ' + +d) '; + $this->renderBlock($__nm = 'test', ['var2' => 1, 2] + [], 'html'); + echo ' // invalid'; + return get_defined_vars(); + } + + + public function blockTest(array $__args): void + { + extract($this->params); + $var1 = $__args[0] ?? $__args['var1'] ?? null; + $var2 = $__args[1] ?? $__args['var2'] ?? null; + $var3 = $__args[2] ?? $__args['var3'] ?? null; + echo ' Variables '; + echo LR\Filters::escapeHtmlText($var1) /* line 4 */; + echo ', '; + echo LR\Filters::escapeHtmlText($var2) /* line 4 */; + echo ', '; + echo LR\Filters::escapeHtmlText($var3) /* line 4 */; + echo "\n"; + } + +} diff --git a/tests/Latte/expected/BlockMacros.define.args3.phtml b/tests/Latte/expected/BlockMacros.define.args3.phtml new file mode 100644 index 0000000000..4ccf356ab1 --- /dev/null +++ b/tests/Latte/expected/BlockMacros.define.args3.phtml @@ -0,0 +1,51 @@ + 'blockTest'], + ]; + + + public function main(): array + { + extract($this->params); + echo 'named arguments order + +a) '; + $this->renderBlock($__nm = 'test', [1, 'var1' => 2] + [], 'html'); + echo ' + +b) '; + $this->renderBlock($__nm = 'test', ['var2' => 1] + [], 'html'); + echo ' + +c) '; + $this->renderBlock($__nm = 'test', ['hello' => 1] + [], 'html'); + echo ' + +'; + if ($this->getParentName()) { + return get_defined_vars(); + } + return get_defined_vars(); + } + + + public function blockTest(array $__args): void + { + extract($this->params); + $var1 = $__args[0] ?? $__args['var1'] ?? null; + $var2 = $__args[1] ?? $__args['var2'] ?? null; + $var3 = $__args[2] ?? $__args['var3'] ?? null; + echo ' Variables '; + echo LR\Filters::escapeHtmlText($var1) /* line 10 */; + echo ', '; + echo LR\Filters::escapeHtmlText($var2) /* line 10 */; + echo ', '; + echo LR\Filters::escapeHtmlText($var3) /* line 10 */; + echo "\n"; + } + +} diff --git a/tests/Latte/expected/BlockMacros.define.args4.html b/tests/Latte/expected/BlockMacros.define.args4.html new file mode 100644 index 0000000000..732b77eaf1 --- /dev/null +++ b/tests/Latte/expected/BlockMacros.define.args4.html @@ -0,0 +1,11 @@ + +named arguments import + + +a) Variables 1, , + + +b) Variables , 1, + + +c) Variables , , diff --git a/tests/Latte/expected/BlockMacros.define.args4.phtml b/tests/Latte/expected/BlockMacros.define.args4.phtml new file mode 100644 index 0000000000..9ff4be6d05 --- /dev/null +++ b/tests/Latte/expected/BlockMacros.define.args4.phtml @@ -0,0 +1,29 @@ +params); + echo ' +named arguments import + +'; + $this->createTemplate("import.latte", $this->params, "import")->render(); + echo ' +a) '; + $this->renderBlock($__nm = 'test', [1, 'var1' => 2] + [], 'html'); + echo ' + +b) '; + $this->renderBlock($__nm = 'test', ['var2' => 1] + [], 'html'); + echo ' + +c) '; + $this->renderBlock($__nm = 'test', ['hello' => 1] + [], 'html'); + return get_defined_vars(); + } + +} diff --git a/tests/Latte/expected/BlockMacros.define.typehints.phtml b/tests/Latte/expected/BlockMacros.define.typehints.phtml index e3c9a36116..18309f0b54 100644 --- a/tests/Latte/expected/BlockMacros.define.typehints.phtml +++ b/tests/Latte/expected/BlockMacros.define.typehints.phtml @@ -15,7 +15,7 @@ final class Template%a% extends Latte\Runtime\Template return get_defined_vars(); } echo "\n"; - $this->renderBlock($__nm = 'test', [1] + get_defined_vars(), 'html'); + $this->renderBlock($__nm = 'test', [1] + [], 'html'); return get_defined_vars(); }