vendor/twig/twig/src/Parser.php line 405

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Twig.
  4.  *
  5.  * (c) Fabien Potencier
  6.  * (c) Armin Ronacher
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Twig;
  12. use Twig\Error\SyntaxError;
  13. use Twig\ExpressionParser\ExpressionParserInterface;
  14. use Twig\ExpressionParser\ExpressionParsers;
  15. use Twig\ExpressionParser\ExpressionParserType;
  16. use Twig\ExpressionParser\InfixExpressionParserInterface;
  17. use Twig\ExpressionParser\Prefix\LiteralExpressionParser;
  18. use Twig\ExpressionParser\PrefixExpressionParserInterface;
  19. use Twig\Node\BlockNode;
  20. use Twig\Node\BlockReferenceNode;
  21. use Twig\Node\BodyNode;
  22. use Twig\Node\EmptyNode;
  23. use Twig\Node\Expression\AbstractExpression;
  24. use Twig\Node\Expression\Variable\AssignMacroVariable;
  25. use Twig\Node\Expression\Variable\MacroVariable;
  26. use Twig\Node\MacroNode;
  27. use Twig\Node\MacrosNode;
  28. use Twig\Node\ModuleNode;
  29. use Twig\Node\Node;
  30. use Twig\Node\NodeDocumentation;
  31. use Twig\Node\Nodes;
  32. use Twig\Node\PrintNode;
  33. use Twig\Node\TextNode;
  34. use Twig\TokenParser\TokenParserInterface;
  35. use Twig\Util\ReflectionCallable;
  36. /**
  37.  * @author Fabien Potencier <fabien@symfony.com>
  38.  */
  39. class Parser
  40. {
  41.     private $stack = [];
  42.     private ?\WeakMap $expressionRefs = null;
  43.     private $stream;
  44.     private $parent;
  45.     private $visitors;
  46.     private $expressionParser;
  47.     private $blocks;
  48.     private $blockStack;
  49.     /** @var list<Node|null> */
  50.     private array $documentationTargets = [];
  51.     private $macros;
  52.     private $importedSymbols;
  53.     private $traits;
  54.     private $embeddedTemplates = [];
  55.     private int $lastEmbedIndex = 0;
  56.     private $varNameSalt = 0;
  57.     private $ignoreUnknownTwigCallables = false;
  58.     private ExpressionParsers $parsers;
  59.     public function __construct(
  60.         private Environment $env,
  61.     ) {
  62.         $this->parsers = $env->getExpressionParsers();
  63.     }
  64.     public function getEnvironment(): Environment
  65.     {
  66.         return $this->env;
  67.     }
  68.     public function getVarName(): string
  69.     {
  70.         trigger_deprecation('twig/twig', '3.15', 'The "%s()" method is deprecated.', __METHOD__);
  71.         return \sprintf('__internal_parse_%d', $this->varNameSalt++);
  72.     }
  73.     /**
  74.      * @throws SyntaxError
  75.      */
  76.     public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
  77.     {
  78.         // reset on root parse() calls only, so the counter spans nested/reentrant parses
  79.         if (!$this->stack) {
  80.             $this->lastEmbedIndex = 0;
  81.         }
  82.         $vars = get_object_vars($this);
  83.         unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['lastEmbedIndex'], $vars['varNameSalt']);
  84.         $this->stack[] = $vars;
  85.         // node visitors
  86.         if (null === $this->visitors) {
  87.             $this->visitors = $this->env->getNodeVisitors();
  88.         }
  89.         $this->stream = $stream;
  90.         $this->parent = null;
  91.         $this->blocks = [];
  92.         $this->documentationTargets = [];
  93.         $this->macros = [];
  94.         $this->traits = [];
  95.         $this->blockStack = [];
  96.         $this->importedSymbols = [[]];
  97.         $this->embeddedTemplates = [];
  98.         $this->expressionRefs = new \WeakMap();
  99.         try {
  100.             $body = $this->subparse($test, $dropNeedle);
  101.         } catch (SyntaxError $e) {
  102.             if (!$e->getSourceContext()) {
  103.                 $e->setSourceContext($this->stream->getSourceContext());
  104.             }
  105.             if (!$e->getTemplateLine()) {
  106.                 $e->setTemplateLine($this->getCurrentToken()->getLine());
  107.             }
  108.             throw $e;
  109.         } finally {
  110.             $this->expressionRefs = null;
  111.         }
  112.         if ($this->parent) {
  113.             $body = $this->cleanupBodyForChildTemplates($body);
  114.         }
  115.         $node = new ModuleNode(
  116.             new BodyNode([$body]),
  117.             $this->parent,
  118.             $this->blocks ? new Nodes($this->blocks) : new EmptyNode(),
  119.             new MacrosNode($this->macros),
  120.             $this->traits ? new Nodes($this->traits) : new EmptyNode(),
  121.             $this->embeddedTemplates ? new Nodes($this->embeddedTemplates) : new EmptyNode(),
  122.             $stream->getSourceContext(),
  123.         );
  124.         $traverser = new NodeTraverser($this->env, $this->visitors);
  125.         /**
  126.          * @var ModuleNode $node
  127.          */
  128.         $node = $traverser->traverse($node);
  129.         // restore previous stack so previous parse() call can resume working
  130.         foreach (array_pop($this->stack) as $key => $val) {
  131.             $this->$key = $val;
  132.         }
  133.         return $node;
  134.     }
  135.     public function shouldIgnoreUnknownTwigCallables(): bool
  136.     {
  137.         return $this->ignoreUnknownTwigCallables;
  138.     }
  139.     public function subparseIgnoreUnknownTwigCallables($test, bool $dropNeedle = false): void
  140.     {
  141.         $previous = $this->ignoreUnknownTwigCallables;
  142.         $this->ignoreUnknownTwigCallables = true;
  143.         try {
  144.             $this->subparse($test, $dropNeedle);
  145.         } finally {
  146.             $this->ignoreUnknownTwigCallables = $previous;
  147.         }
  148.     }
  149.     /**
  150.      * @throws SyntaxError
  151.      */
  152.     public function subparse($test, bool $dropNeedle = false): Node
  153.     {
  154.         $lineno = $this->getCurrentToken()->getLine();
  155.         $rv = [];
  156.         while (!$this->stream->isEOF()) {
  157.             switch (true) {
  158.                 case $this->stream->getCurrent()->test(Token::TEXT_TYPE):
  159.                     $token = $this->stream->next();
  160.                     $rv[] = new TextNode($token->getValue(), $token->getLine());
  161.                     break;
  162.                 case $this->stream->getCurrent()->test(Token::VAR_START_TYPE):
  163.                     $token = $this->stream->next();
  164.                     $expr = $this->parseExpression();
  165.                     $this->stream->expect(Token::VAR_END_TYPE);
  166.                     $node = new PrintNode($expr, $token->getLine());
  167.                     NodeDocumentation::add($node, $token);
  168.                     $rv[] = $node;
  169.                     break;
  170.                 case $this->stream->getCurrent()->test(Token::BLOCK_START_TYPE):
  171.                     $startToken = $this->stream->next();
  172.                     $token = $this->getCurrentToken();
  173.                     if (!$token->test(Token::NAME_TYPE)) {
  174.                         throw new SyntaxError('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext());
  175.                     }
  176.                     if (null !== $test && $test($token)) {
  177.                         if ($dropNeedle) {
  178.                             $this->stream->next();
  179.                         }
  180.                         if (1 === \count($rv)) {
  181.                             return $rv[0];
  182.                         }
  183.                         return new Nodes($rv, $lineno);
  184.                     }
  185.                     if (!$subparser = $this->env->getTokenParser($token->getValue())) {
  186.                         if (null !== $test) {
  187.                             $e = new SyntaxError(\sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
  188.                             $callable = (new ReflectionCallable(new TwigTest('decision', $test)))->getCallable();
  189.                             if (\is_array($callable) && $callable[0] instanceof TokenParserInterface) {
  190.                                 $e->appendMessage(\sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $callable[0]->getTag(), $lineno));
  191.                             }
  192.                         } else {
  193.                             $e = new SyntaxError(\sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
  194.                             $e->addSuggestions($token->getValue(), array_keys($this->env->getTokenParsers()));
  195.                         }
  196.                         throw $e;
  197.                     }
  198.                     $this->stream->next();
  199.                     $subparser->setParser($this);
  200.                     $documentationTargetIndex = \count($this->documentationTargets);
  201.                     $this->documentationTargets[] = null;
  202.                     try {
  203.                         $node = $subparser->parse($token);
  204.                         $documentationTarget = $this->documentationTargets[$documentationTargetIndex];
  205.                     } finally {
  206.                         array_pop($this->documentationTargets);
  207.                     }
  208.                     if (!$node) {
  209.                         trigger_deprecation('twig/twig', '3.12', 'Returning "null" from "%s" is deprecated and forbidden by "TokenParserInterface".', $subparser::class);
  210.                     } else {
  211.                         $node->setNodeTag($subparser->getTag());
  212.                         NodeDocumentation::add($node, $startToken);
  213.                         if (null !== $documentationTarget && $node !== $documentationTarget) {
  214.                             NodeDocumentation::move($node, $documentationTarget);
  215.                         }
  216.                         $rv[] = $node;
  217.                     }
  218.                     break;
  219.                 default:
  220.                     throw new SyntaxError('The lexer or the parser ended up in an unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext());
  221.             }
  222.         }
  223.         if (1 === \count($rv)) {
  224.             return $rv[0];
  225.         }
  226.         return new Nodes($rv, $lineno);
  227.     }
  228.     public function getBlockStack(): array
  229.     {
  230.         trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__);
  231.         return $this->blockStack;
  232.     }
  233.     /**
  234.      * @return string|null
  235.      */
  236.     public function peekBlockStack()
  237.     {
  238.         return $this->blockStack[\count($this->blockStack) - 1] ?? null;
  239.     }
  240.     public function popBlockStack(): void
  241.     {
  242.         array_pop($this->blockStack);
  243.     }
  244.     public function pushBlockStack($name): void
  245.     {
  246.         $this->blockStack[] = $name;
  247.     }
  248.     public function hasBlock(string $name): bool
  249.     {
  250.         trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__);
  251.         return isset($this->blocks[$name]);
  252.     }
  253.     public function getBlock(string $name): Node
  254.     {
  255.         trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__);
  256.         return $this->blocks[$name];
  257.     }
  258.     public function setBlock(string $name, BlockNode $value): void
  259.     {
  260.         if (isset($this->blocks[$name])) {
  261.             throw new SyntaxError(\sprintf("The block '%s' has already been defined line %d.", $name, $this->blocks[$name]->getTemplateLine()), $this->getCurrentToken()->getLine(), $this->blocks[$name]->getSourceContext());
  262.         }
  263.         $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine());
  264.     }
  265.     public function hasMacro(string $name): bool
  266.     {
  267.         trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__);
  268.         return isset($this->macros[$name]);
  269.     }
  270.     public function setDocumentationTarget(Node $node): void
  271.     {
  272.         if (null === $index = array_key_last($this->documentationTargets)) {
  273.             throw new \LogicException('A documentation target can only be set while parsing a tag.');
  274.         }
  275.         if (null !== $this->documentationTargets[$index]) {
  276.             throw new \LogicException('The documentation target for a tag can only be set once.');
  277.         }
  278.         $this->documentationTargets[$index] = $node;
  279.     }
  280.     public function setMacro(string $name, MacroNode $node): void
  281.     {
  282.         if (isset($this->macros[$name])) {
  283.             trigger_deprecation('twig/twig', '3.29', 'Defining the macro "%s" more than once in "%s" is deprecated and will throw a SyntaxError in Twig 4.0 (previous definition at line %d, new definition at line %d). The last definition is used in Twig 3.', $name, $this->stream->getSourceContext()->getName(), $this->macros[$name]->getTemplateLine(), $node->getTemplateLine());
  284.         }
  285.         $this->macros[$name] = $node;
  286.     }
  287.     public function addTrait($trait): void
  288.     {
  289.         $this->traits[] = $trait;
  290.     }
  291.     public function hasTraits(): bool
  292.     {
  293.         trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__);
  294.         return \count($this->traits) > 0;
  295.     }
  296.     /**
  297.      * @return void
  298.      */
  299.     public function embedTemplate(ModuleNode $template)
  300.     {
  301.         $template->setIndex(++$this->lastEmbedIndex);
  302.         $this->embeddedTemplates[] = $template;
  303.     }
  304.     public function addImportedSymbol(string $type, string $alias, ?string $name = null, AbstractExpression|AssignMacroVariable|null $internalRef = null): void
  305.     {
  306.         if ($internalRef && !$internalRef instanceof AssignMacroVariable) {
  307.             trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance as an internal reference is deprecated ("%s" given).', __METHOD__, AssignMacroVariable::class, $internalRef::class);
  308.             $internalRef = new AssignMacroVariable(new MacroVariable($internalRef->getAttribute('name'), $internalRef->getTemplateLine()), $internalRef->getAttribute('global'));
  309.         }
  310.         $this->importedSymbols[0][$type][$alias] = ['name' => $name, 'node' => $internalRef];
  311.     }
  312.     /**
  313.      * @return array{name: string, node: AssignMacroVariable|null}|null
  314.      */
  315.     public function getImportedSymbol(string $type, string $alias)
  316.     {
  317.         // if the symbol does not exist in the current scope (0), try in the main/global scope (last index)
  318.         return $this->importedSymbols[0][$type][$alias] ?? ($this->importedSymbols[\count($this->importedSymbols) - 1][$type][$alias] ?? null);
  319.     }
  320.     public function isMainScope(): bool
  321.     {
  322.         return 1 === \count($this->importedSymbols);
  323.     }
  324.     public function pushLocalScope(): void
  325.     {
  326.         array_unshift($this->importedSymbols, []);
  327.     }
  328.     public function popLocalScope(): void
  329.     {
  330.         array_shift($this->importedSymbols);
  331.     }
  332.     /**
  333.      * @deprecated since Twig 3.21
  334.      */
  335.     public function getExpressionParser(): ExpressionParser
  336.     {
  337.         trigger_deprecation('twig/twig', '3.21', 'Method "%s()" is deprecated, use "parseExpression()" instead.', __METHOD__);
  338.         if (null === $this->expressionParser) {
  339.             $this->expressionParser = new ExpressionParser($this, $this->env);
  340.         }
  341.         return $this->expressionParser;
  342.     }
  343.     public function parseExpression(int $precedence = 0): AbstractExpression
  344.     {
  345.         $token = $this->getCurrentToken();
  346.         if ($token->test(Token::OPERATOR_TYPE) && $ep = $this->parsers->getByName(PrefixExpressionParserInterface::class, $token->getValue())) {
  347.             $this->getStream()->next();
  348.             $expr = $ep->parse($this, $token);
  349.             $this->checkPrecedenceDeprecations($ep, $expr);
  350.         } else {
  351.             $expr = $this->parsers->getByClass(LiteralExpressionParser::class)->parse($this, $token);
  352.         }
  353.         $token = $this->getCurrentToken();
  354.         while ($token->test(Token::OPERATOR_TYPE) && ($ep = $this->parsers->getByName(InfixExpressionParserInterface::class, $token->getValue())) && $ep->getPrecedence() >= $precedence) {
  355.             $this->getStream()->next();
  356.             $expr = $ep->parse($this, $expr, $token);
  357.             $this->checkPrecedenceDeprecations($ep, $expr);
  358.             $token = $this->getCurrentToken();
  359.         }
  360.         return $expr;
  361.     }
  362.     public function getParent(): ?Node
  363.     {
  364.         trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__);
  365.         return $this->parent;
  366.     }
  367.     /**
  368.      * @return bool
  369.      */
  370.     public function hasInheritance()
  371.     {
  372.         return $this->parent || 0 < \count($this->traits);
  373.     }
  374.     public function setParent(?Node $parent, bool $throwOnMultiple = true): void
  375.     {
  376.         if (null === $parent) {
  377.             trigger_deprecation('twig/twig', '3.12', 'Passing "null" to "%s()" is deprecated.', __METHOD__);
  378.         }
  379.         if (null !== $parent && !$parent instanceof AbstractExpression) {
  380.             trigger_deprecation('twig/twig', '3.24', 'Passing a "%s" instance to "%s()" is deprecated, pass an "AbstractExpression" instance instead.', $parent::class, __METHOD__);
  381.         }
  382.         if (null !== $this->parent) {
  383.             if (!$throwOnMultiple) {
  384.                 return;
  385.             }
  386.             throw new SyntaxError('Multiple extends tags are forbidden.', $parent->getTemplateLine(), $parent->getSourceContext());
  387.         }
  388.         $this->parent = $parent;
  389.     }
  390.     public function getStream(): TokenStream
  391.     {
  392.         return $this->stream;
  393.     }
  394.     public function getCurrentToken(): Token
  395.     {
  396.         return $this->stream->getCurrent();
  397.     }
  398.     public function getFunction(string $name, int $line): TwigFunction
  399.     {
  400.         try {
  401.             $function = $this->env->getFunction($name);
  402.         } catch (SyntaxError $e) {
  403.             if (!$this->shouldIgnoreUnknownTwigCallables()) {
  404.                 throw $e;
  405.             }
  406.             $function = null;
  407.         }
  408.         if (!$function) {
  409.             if ($this->shouldIgnoreUnknownTwigCallables()) {
  410.                 return new TwigFunction($name, static fn () => '');
  411.             }
  412.             $e = new SyntaxError(\sprintf('Unknown "%s" function.', $name), $line, $this->stream->getSourceContext());
  413.             $e->addSuggestions($name, array_keys($this->env->getFunctions()));
  414.             throw $e;
  415.         }
  416.         if ($function->isDeprecated()) {
  417.             $src = $this->stream->getSourceContext();
  418.             $function->triggerDeprecation($src->getPath() ?: $src->getName(), $line);
  419.         }
  420.         return $function;
  421.     }
  422.     public function getFilter(string $name, int $line): TwigFilter
  423.     {
  424.         try {
  425.             $filter = $this->env->getFilter($name);
  426.         } catch (SyntaxError $e) {
  427.             if (!$this->shouldIgnoreUnknownTwigCallables()) {
  428.                 throw $e;
  429.             }
  430.             $filter = null;
  431.         }
  432.         if (!$filter) {
  433.             if ($this->shouldIgnoreUnknownTwigCallables()) {
  434.                 return new TwigFilter($name, static fn () => '');
  435.             }
  436.             $e = new SyntaxError(\sprintf('Unknown "%s" filter.', $name), $line, $this->stream->getSourceContext());
  437.             $e->addSuggestions($name, array_keys($this->env->getFilters()));
  438.             throw $e;
  439.         }
  440.         if ($filter->isDeprecated()) {
  441.             $src = $this->stream->getSourceContext();
  442.             $filter->triggerDeprecation($src->getPath() ?: $src->getName(), $line);
  443.         }
  444.         return $filter;
  445.     }
  446.     public function getTest(int $line): TwigTest
  447.     {
  448.         $name = $this->stream->expect(Token::NAME_TYPE)->getValue();
  449.         if ($this->stream->test(Token::NAME_TYPE)) {
  450.             // try 2-words tests
  451.             $name = $name.' '.$this->getCurrentToken()->getValue();
  452.             try {
  453.                 $test = $this->env->getTest($name);
  454.             } catch (SyntaxError $e) {
  455.                 if (!$this->shouldIgnoreUnknownTwigCallables()) {
  456.                     throw $e;
  457.                 }
  458.                 $test = null;
  459.             }
  460.             $this->stream->next();
  461.         } else {
  462.             try {
  463.                 $test = $this->env->getTest($name);
  464.             } catch (SyntaxError $e) {
  465.                 if (!$this->shouldIgnoreUnknownTwigCallables()) {
  466.                     throw $e;
  467.                 }
  468.                 $test = null;
  469.             }
  470.         }
  471.         if (!$test) {
  472.             if ($this->shouldIgnoreUnknownTwigCallables()) {
  473.                 return new TwigTest($name, static fn () => '');
  474.             }
  475.             $e = new SyntaxError(\sprintf('Unknown "%s" test.', $name), $line, $this->stream->getSourceContext());
  476.             $e->addSuggestions($name, array_keys($this->env->getTests()));
  477.             throw $e;
  478.         }
  479.         if ($test->isDeprecated()) {
  480.             $src = $this->stream->getSourceContext();
  481.             $test->triggerDeprecation($src->getPath() ?: $src->getName(), $this->stream->getCurrent()->getLine());
  482.         }
  483.         return $test;
  484.     }
  485.     private function cleanupBodyForChildTemplates(Node $body): Node
  486.     {
  487.         if ($body instanceof BlockReferenceNode) {
  488.             return new EmptyNode();
  489.         }
  490.         if ($body instanceof TextNode && $body->isBlank()) {
  491.             return new EmptyNode();
  492.         }
  493.         foreach ($body as $k => $node) {
  494.             if ($node instanceof BlockReferenceNode) {
  495.                 // as it has a parent, the block reference won't be used
  496.                 $body->removeNode($k);
  497.             } elseif ($node instanceof TextNode && $node->isBlank()) {
  498.                 // remove nodes considered as "empty"
  499.                 $body->removeNode($k);
  500.             }
  501.         }
  502.         return $body;
  503.     }
  504.     private function checkPrecedenceDeprecations(ExpressionParserInterface $expressionParser, AbstractExpression $expr): void
  505.     {
  506.         $this->expressionRefs[$expr] = $expressionParser;
  507.         $precedenceChanges = $this->parsers->getPrecedenceChanges();
  508.         // Check that the all nodes that are between the 2 precedences have explicit parentheses
  509.         if (!isset($precedenceChanges[$expressionParser])) {
  510.             return;
  511.         }
  512.         if ($expr->hasExplicitParentheses()) {
  513.             return;
  514.         }
  515.         if ($expressionParser instanceof PrefixExpressionParserInterface) {
  516.             /** @var AbstractExpression $node */
  517.             $node = $expr->getNode('node');
  518.             foreach ($precedenceChanges as $ep => $changes) {
  519.                 if (!\in_array($expressionParser, $changes, true)) {
  520.                     continue;
  521.                 }
  522.                 if (isset($this->expressionRefs[$node]) && $ep === $this->expressionRefs[$node]) {
  523.                     $change = $expressionParser->getPrecedenceChange();
  524.                     trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $expressionParser->getName(), ExpressionParserType::getType($expressionParser)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine()));
  525.                 }
  526.             }
  527.         }
  528.         foreach ($precedenceChanges[$expressionParser] as $ep) {
  529.             foreach ($expr as $node) {
  530.                 /** @var AbstractExpression $node */
  531.                 if (isset($this->expressionRefs[$node]) && $ep === $this->expressionRefs[$node] && !$node->hasExplicitParentheses()) {
  532.                     $change = $ep->getPrecedenceChange();
  533.                     trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $ep->getName(), ExpressionParserType::getType($ep)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine()));
  534.                 }
  535.             }
  536.         }
  537.     }
  538. }