Accessing raw text content of AreaNode

daun
Member | 6
+
0
-

Hi! I'm writing a custom tag for Latte 3. I need to extract the raw value between start and end tag and store that in a variable. Is this possible? To get the raw value, I must tell Latte to ignore the contents of the tag somehow.

Given the below Latte template, I'd like to get the exact string “Ignored by {latte} <b>and<b/> {returned} as string” inside the custom Node class to create a unique hash from it and write it to disk.

Normally rendered by Latte

{raw}
  Ignored by {latte} <b>and<b/> {returned} as string
{/raw}

The class below is what I have so far, but it returns already parsed PHP output.

Using $context->format('%raw', $areaNode) throws an error that I can't render a FragmentNode instance to string.

Using $areaNode->print($context) seems to parse the node and return the PHP output.

Appreciate any help.

<?php

use Latte\Compiler\Nodes\AreaNode;
use Latte\Compiler\Nodes\NopNode;
use Latte\Compiler\Nodes\StatementNode;
use Latte\Compiler\PrintContext;
use Latte\Compiler\Tag;

/**
 * {raw} {/raw}
 */
class RawNode extends StatementNode
{
	public AreaNode $content;

	/** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
	public static function create(Tag $tag): \Generator
	{
		$node = $tag->node = new static;
		if ($tag->void) return new NopNode;
		[$node->content] = yield;
		return $node;
	}

	public function print(PrintContext $context): string
	{
		$raw = $this->content->print($context);
		$hash = sha1($raw);
		// ?????
		return $context->format("echo '%raw'; %line;", $hash, $this->position);
	}


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

Last edited by daun (2024-01-29 16:24)

daun
Member | 6
+
0
-

I think I've found a solution. It seems a bit hacky, so if anyone can think of a more elegant solution, happy to hear it!

The idea is:

  • Disable lexer syntax
  • Set parser content type to text
  • Read inner value of node
  • Restore syntax and content type
  • Extract text contents using NodeHelpers::toText()
<?php

use Latte\CompileException;
use Latte\Compiler\NodeHelpers;
use Latte\Compiler\Nodes\AreaNode;
use Latte\Compiler\Nodes\StatementNode;
use Latte\Compiler\PrintContext;
use Latte\Compiler\Tag;
use Latte\Compiler\TemplateParser;
use Latte\ContentType;

/**
 * {raw} {/raw}
 */
class RawNode extends StatementNode
{
	public AreaNode $content;

	/** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
	public static function create(Tag $tag, TemplateParser $parser): \Generator
	{
		$node = $tag->node = new static;

        // Temporarily disable {} syntax and switch to text content type
		$lexer = $parser->getLexer();
		$delimiters = [$lexer->openDelimiter, $lexer->closeDelimiter];
		$lexer->setSyntax('off', $tag->isNAttribute() ? null : $tag->name);
		$type = $parser->getContentType();
		$parser->setContentType(ContentType::Text);

        // Read inner content as raw text
		[$node->content] = yield;

        // Restore previous syntax and content type
		$parser->setContentType($type);
		[$lexer->openDelimiter, $lexer->closeDelimiter] = $delimiters;

		return $node;
	}

	public function print(PrintContext $context): string
	{
		$raw = NodeHelpers::toText($this->content);
		$hash = sha1($raw);
		return $context->format("echo '%raw'; %line;", $hash, $this->position);
	}


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

Marek Bartoš
Nette Blogger | 1173
+
0
-
{syntax off}
  Ignored by {latte} <b>and<b/> {returned} as string
{/syntax}
daun
Member | 6
+
0
-

@MarekBartoš Thanks! Turning syntax off is only the first required step here. The tag also needs to save the raw inner content into a variable, hash it, save it to disk and render the content in a different template language :) For that to work, I also need to switch the content type to text so Latte doesn't return html node as children of the syntax node, however the contentType tag only works for the whole file, as far as I could tell.