diff --git a/src/Latte/Essential/CoreExtension.php b/src/Latte/Essential/CoreExtension.php index f155635d4..63346bbe2 100644 --- a/src/Latte/Essential/CoreExtension.php +++ b/src/Latte/Essential/CoreExtension.php @@ -100,6 +100,7 @@ public function getTags(): array 'ifset' => [Nodes\IfNode::class, 'create'], 'ifchanged' => [Nodes\IfChangedNode::class, 'create'], 'n:ifcontent' => [Nodes\IfContentNode::class, 'create'], + 'n:else' => [Nodes\NElseNode::class, 'create'], 'switch' => [Nodes\SwitchNode::class, 'create'], ]; } @@ -198,6 +199,7 @@ public function getPasses(): array 'overwrittenVariables' => [Passes::class, 'overwrittenVariablesPass'], 'customFunctions' => fn(TemplateNode $node) => Passes::customFunctionsPass($node, $this->functions), 'moveTemplatePrintToHead' => [Passes::class, 'moveTemplatePrintToHeadPass'], + 'nElse' => [Nodes\NElseNode::class, 'processPass'], ]; } diff --git a/src/Latte/Essential/Nodes/NElseNode.php b/src/Latte/Essential/Nodes/NElseNode.php new file mode 100644 index 000000000..77c39e72d --- /dev/null +++ b/src/Latte/Essential/Nodes/NElseNode.php @@ -0,0 +1,82 @@ + */ + public static function create(Tag $tag): \Generator + { + $node = $tag->node = new static; + [$node->content] = yield; + return $node; + } + + + public function print(PrintContext $context): string + { + throw new \LogicException('Cannot directly print'); + } + + + public function &getIterator(): \Generator + { + yield $this->content; + } + + + public static function processPass(TemplateNode $node): void + { + (new NodeTraverser)->traverse($node, function (Node $node) { + if ($node instanceof Nodes\FragmentNode) { + for ($i = count($node->children) - 1; $i >= 0; $i--) { + $child = $node->children[$i]; + if (!$child instanceof self) { + continue; + } + + array_splice($node->children, $i, 1); + $prev = $node->children[--$i] ?? null; + if ($prev instanceof Nodes\TextNode && trim($prev->content) === '') { + array_splice($node->children, $i, 1); + $prev = $node->children[--$i] ?? null; + } + + if (!$prev instanceof IfNode && !$prev instanceof ForeachNode && !$prev instanceof TryNode) { + throw new CompileException('n:else must be immediately after n:if / n:try / n:foreach.', $child->position); + } elseif ($prev->else) { + throw new CompileException('Multiple "else" found.', $child->position); + } + + $prev->else = $child->content; + } + } elseif ($node instanceof self) { + throw new CompileException('n:else must be immediately after n:if / n:try / n:foreach.', $node->position); + } + }); + } +} diff --git a/tests/tags/n-else.phpt b/tests/tags/n-else.phpt new file mode 100644 index 000000000..e89ee0525 --- /dev/null +++ b/tests/tags/n-else.phpt @@ -0,0 +1,142 @@ +setLoader(new Latte\Loaders\StringLoader); + +// errors +Assert::exception( + fn() => $latte->compile(''), + Latte\CompileException::class, + 'n:else must be immediately after n:if / n:try / n:foreach (on line 1 at column 7)', +); + +Assert::exception( + fn() => $latte->compile('
else
+ end + XX, + ), +); + +Assert::match( + <<<'XX' + begin +else
+ end + XX, + $latte->renderToString( + <<<'XX' + begin +else
+ end + XX, + ), +); + + +// n:foreach & n:else +Assert::match( + <<<'XX' + begin +else
+ end + XX, + ), +); + +Assert::match( + <<<'XX' + begin +else
+ end + XX, + $latte->renderToString( + <<<'XX' + begin +else
+ end + XX, + ), +); + + +// n:try & n:else +Assert::match( + <<<'XX' + begin +else
+ end + XX, + ), +); + +Assert::match( + <<<'XX' + begin +else
+ end + XX, + $latte->renderToString( + <<<'XX' + begin +else
+ end + XX, + ), +);