How to check if a tag is empty or self-closing

daun
Member | 9
+
+1
-

Is there a way of allowing both {custom}inner{/custom} as well as {custom} when writing custom tags?

I'm trying to write a tag that can either be used to output strings (like include) or with content inside (like embed). Basically, it needs to behave differently when it's a statement vs. when it contains content.

There's the option of {custom/}, but how can I find out if the tag is self-closing? The resulting node seems to be identical whether using {custom/} or {custom}{/custom}.

This is what I have so far, but it returns true for both types:

$selfClosing = (
  !$node->content ||
  $node->content instanceof Nodes\NopNode ||
  ($node->content instanceof Nodes\TextNode && trim($node->content->content) === '') ||
  ($node->content instanceof Nodes\FragmentNode && count($node->content->children) === 0)
);

For good measure, this is the whole tag. Maybe the yield $this->content must be conditional somehow?

<?php

namespace App\Latte\Extensions\Nodes;

use Latte\CompileException;
use Latte\Compiler\Node;
use Latte\Compiler\Nodes;
use Latte\Compiler\Nodes\AreaNode;
use Latte\Compiler\Nodes\Php\Expression\ArrayNode;
use Latte\Compiler\Nodes\StatementNode;
use Latte\Compiler\NodeTraverser;
use Latte\Compiler\PrintContext;
use Latte\Compiler\Tag;

/**
 * {custom}{/custom} or {custom}
 */
final class TagNode extends StatementNode
{
    public string $name;

    public ArrayNode $args;

    public AreaNode $content;

    public bool $selfClosing = false;

    /** @return \Generator<int, AreaNode|null> */
    public static function create(Tag $tag): \Generator
    {
        $node = $tag->node = new self;
        $node->name = $tag->name;
        $node->args = $tag->parser->parseArguments();
        [$node->content] = yield;
        $node->selfClosing = (
            !$node->content ||
            $node->content instanceof Nodes\NopNode ||
            ($node->content instanceof Nodes\TextNode && trim($node->content->content) === '') ||
            ($node->content instanceof Nodes\FragmentNode && count($node->content->children) === 0)
        );

        return $node;
    }

    public function print(PrintContext $context): string
    {
        return $this->selfClosing
          ? $context->format('echo customFunctionThatReturnsString(%node);', $this->args)
          : $context->format('foreach (%node) { %node }', $this->args, $this->content);
    }

    public function &getIterator(): \Generator
    {
        yield $this->content;
    }
}

Appreciate any pointers! Thanks in advance.

daun
Member | 9
+
0
-

Getting closer. When yielding the end tag, it turns out I can check if it's the same object as the opening tag. So this works to distinguish between {custom/} and {custom}{/custom}.

[$node->content, $endTag] = yield;

$node->hasContent = $endTag && $endTag->closing;
$node->selfClosing = !$endTag || $endTag === $tag;

As regards the non-closing standalone {custom}, it looks like that isn't possible? The reason it works for {block} is that there's a hardcoded check in the TemplateParser class. Might open a feature request :)

Last edited by daun (2024-10-26 12:36)

daun
Member | 9
+
0
-