TwigNodeVisitor.php

Same filename in other branches
  1. 9 core/lib/Drupal/Core/Template/TwigNodeVisitor.php
  2. 8.9.x core/lib/Drupal/Core/Template/TwigNodeVisitor.php
  3. 10 core/lib/Drupal/Core/Template/TwigNodeVisitor.php

Namespace

Drupal\Core\Template

File

core/lib/Drupal/Core/Template/TwigNodeVisitor.php

View source
<?php

namespace Drupal\Core\Template;

use Twig\Environment;
use Twig\TwigFunction;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\NodeVisitor\NodeVisitorInterface;

/**
 * Provides a TwigNodeVisitor to change the generated parse-tree.
 *
 * This is used to ensure that everything printed is wrapped via the
 * TwigExtension->renderVar() function in order to just write {{ content }}
 * in templates instead of having to write {{ render_var(content) }}.
 *
 * @see twig_render
 */
class TwigNodeVisitor implements NodeVisitorInterface {
    
    /**
     * Tracks whether there is a render array aware filter active already.
     */
    protected ?bool $skipRenderVarFunction;
    
    /**
     * {@inheritdoc}
     */
    public function enterNode(Node $node, Environment $env) : Node {
        return $node;
    }
    
    /**
     * {@inheritdoc}
     */
    public function leaveNode(Node $node, Environment $env) : ?Node {
        // We use this to inject a call to render_var -> TwigExtension->renderVar()
        // before anything is printed.
        if ($node instanceof PrintNode) {
            if (!empty($this->skipRenderVarFunction)) {
                // No need to add the callback, we have escape active already.
                unset($this->skipRenderVarFunction);
                return $node;
            }
            $class = get_class($node);
            $line = $node->getTemplateLine();
            return new $class(new FunctionExpression(new TwigFunction('render_var', [
                $env->getExtension(TwigExtension::class),
                'renderVar',
            ]), new Node([
                $node->getNode('expr'),
            ]), $line), $line);
        }
        elseif ($node instanceof FilterExpression) {
            $name = $node->getAttribute('twig_callable')
                ->getName();
            if ('escape' == $name || 'e' == $name) {
                // Use our own escape filter that is MarkupInterface aware.
                $node->setAttribute('twig_callable', $env->getFilter('drupal_escape'));
                // Store that we have a filter active already that knows
                // how to deal with render arrays.
                $this->skipRenderVarFunction = TRUE;
            }
        }
        return $node;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getPriority() {
        // Just above the Optimizer, which is the normal last one.
        return 256;
    }

}

Classes

Title Deprecated Summary
TwigNodeVisitor Provides a TwigNodeVisitor to change the generated parse-tree.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.