vendor/twig/twig/src/ExpressionParser.php line 430
<?php/** This file is part of Twig.** (c) Fabien Potencier* (c) Armin Ronacher** For the full copyright and license information, please view the LICENSE* file that was distributed with this source code.*/namespace Twig;use Twig\Error\SyntaxError;use Twig\Node\Expression\AbstractExpression;use Twig\Node\Expression\ArrayExpression;use Twig\Node\Expression\ArrowFunctionExpression;use Twig\Node\Expression\AssignNameExpression;use Twig\Node\Expression\Binary\AbstractBinary;use Twig\Node\Expression\Binary\ConcatBinary;use Twig\Node\Expression\BlockReferenceExpression;use Twig\Node\Expression\ConditionalExpression;use Twig\Node\Expression\ConstantExpression;use Twig\Node\Expression\GetAttrExpression;use Twig\Node\Expression\MethodCallExpression;use Twig\Node\Expression\NameExpression;use Twig\Node\Expression\ParentExpression;use Twig\Node\Expression\TestExpression;use Twig\Node\Expression\Unary\AbstractUnary;use Twig\Node\Expression\Unary\NegUnary;use Twig\Node\Expression\Unary\NotUnary;use Twig\Node\Expression\Unary\PosUnary;use Twig\Node\Node;/*** Parses expressions.** This parser implements a "Precedence climbing" algorithm.** @see https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm* @see https://en.wikipedia.org/wiki/Operator-precedence_parser** @author Fabien Potencier <fabien@symfony.com>*/class ExpressionParser{public const OPERATOR_LEFT = 1;public const OPERATOR_RIGHT = 2;private $parser;private $env;/** @var array<string, array{precedence: int, class: class-string<AbstractUnary>}> */private $unaryOperators;/** @var array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: self::OPERATOR_*}> */private $binaryOperators;public function __construct(Parser $parser, Environment $env){$this->parser = $parser;$this->env = $env;$this->unaryOperators = $env->getUnaryOperators();$this->binaryOperators = $env->getBinaryOperators();}public function parseExpression($precedence = 0, $allowArrow = false){if ($allowArrow && $arrow = $this->parseArrow()) {return $arrow;}$expr = $this->getPrimary();$token = $this->parser->getCurrentToken();while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {$op = $this->binaryOperators[$token->getValue()];$this->parser->getStream()->next();if ('is not' === $token->getValue()) {$expr = $this->parseNotTestExpression($expr);} elseif ('is' === $token->getValue()) {$expr = $this->parseTestExpression($expr);} elseif (isset($op['callable'])) {$expr = $op['callable']($this->parser, $expr);} else {$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence'], true);$class = $op['class'];$expr = new $class($expr, $expr1, $token->getLine());}$token = $this->parser->getCurrentToken();}if (0 === $precedence) {return $this->parseConditionalExpression($expr);}return $expr;}/*** @return ArrowFunctionExpression|null*/private function parseArrow(){$stream = $this->parser->getStream();// short array syntax (one argument, no parentheses)?if ($stream->look(1)->test(/* Token::ARROW_TYPE */ 12)) {$line = $stream->getCurrent()->getLine();$token = $stream->expect(/* Token::NAME_TYPE */ 5);$names = [new AssignNameExpression($token->getValue(), $token->getLine())];$stream->expect(/* Token::ARROW_TYPE */ 12);return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);}// first, determine if we are parsing an arrow function by finding => (long form)$i = 0;if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {return null;}++$i;while (true) {// variable name++$i;if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ',')) {break;}++$i;}if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {return null;}++$i;if (!$stream->look($i)->test(/* Token::ARROW_TYPE */ 12)) {return null;}// yes, let's parse it properly$token = $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(');$line = $token->getLine();$names = [];while (true) {$token = $stream->expect(/* Token::NAME_TYPE */ 5);$names[] = new AssignNameExpression($token->getValue(), $token->getLine());if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {break;}}$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')');$stream->expect(/* Token::ARROW_TYPE */ 12);return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);}private function getPrimary(): AbstractExpression{$token = $this->parser->getCurrentToken();if ($this->isUnary($token)) {$operator = $this->unaryOperators[$token->getValue()];$this->parser->getStream()->next();$expr = $this->parseExpression($operator['precedence']);$class = $operator['class'];return $this->parsePostfixExpression(new $class($expr, $token->getLine()));} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {$this->parser->getStream()->next();$expr = $this->parseExpression();$this->parser->getStream()->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'An opened parenthesis is not properly closed');return $this->parsePostfixExpression($expr);}return $this->parsePrimaryExpression();}private function parseConditionalExpression($expr): AbstractExpression{while ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, '?')) {if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {$expr2 = $this->parseExpression();if ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {$expr3 = $this->parseExpression();} else {$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());}} else {$expr2 = $expr;$expr3 = $this->parseExpression();}$expr = new ConditionalExpression($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());}return $expr;}private function isUnary(Token $token): bool{return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->unaryOperators[$token->getValue()]);}private function isBinary(Token $token): bool{return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->binaryOperators[$token->getValue()]);}public function parsePrimaryExpression(){$token = $this->parser->getCurrentToken();switch ($token->getType()) {case /* Token::NAME_TYPE */ 5:$this->parser->getStream()->next();switch ($token->getValue()) {case 'true':case 'TRUE':$node = new ConstantExpression(true, $token->getLine());break;case 'false':case 'FALSE':$node = new ConstantExpression(false, $token->getLine());break;case 'none':case 'NONE':case 'null':case 'NULL':$node = new ConstantExpression(null, $token->getLine());break;default:if ('(' === $this->parser->getCurrentToken()->getValue()) {$node = $this->getFunctionNode($token->getValue(), $token->getLine());} else {$node = new NameExpression($token->getValue(), $token->getLine());}}break;case /* Token::NUMBER_TYPE */ 6:$this->parser->getStream()->next();$node = new ConstantExpression($token->getValue(), $token->getLine());break;case /* Token::STRING_TYPE */ 7:case /* Token::INTERPOLATION_START_TYPE */ 10:$node = $this->parseStringExpression();break;case /* Token::OPERATOR_TYPE */ 8:if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {// in this context, string operators are variable names$this->parser->getStream()->next();$node = new NameExpression($token->getValue(), $token->getLine());break;}if (isset($this->unaryOperators[$token->getValue()])) {$class = $this->unaryOperators[$token->getValue()]['class'];if (!\in_array($class, [NegUnary::class, PosUnary::class])) {throw new SyntaxError(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());}$this->parser->getStream()->next();$expr = $this->parsePrimaryExpression();$node = new $class($expr, $token->getLine());break;}// no breakdefault:if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) {$node = $this->parseArrayExpression();} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) {$node = $this->parseHashExpression();} elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {throw new SyntaxError(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());} else {throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());}}return $this->parsePostfixExpression($node);}public function parseStringExpression(){$stream = $this->parser->getStream();$nodes = [];// a string cannot be followed by another string in a single expression$nextCanBeString = true;while (true) {if ($nextCanBeString && $token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) {$nodes[] = new ConstantExpression($token->getValue(), $token->getLine());$nextCanBeString = false;} elseif ($stream->nextIf(/* Token::INTERPOLATION_START_TYPE */ 10)) {$nodes[] = $this->parseExpression();$stream->expect(/* Token::INTERPOLATION_END_TYPE */ 11);$nextCanBeString = true;} else {break;}}$expr = array_shift($nodes);foreach ($nodes as $node) {$expr = new ConcatBinary($expr, $node, $node->getTemplateLine());}return $expr;}public function parseArrayExpression(){$stream = $this->parser->getStream();$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'An array element was expected');$node = new ArrayExpression([], $stream->getCurrent()->getLine());$first = true;while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {if (!$first) {$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'An array element must be followed by a comma');// trailing ,?if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {break;}}$first = false;$node->addElement($this->parseExpression());}$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened array is not properly closed');return $node;}public function parseHashExpression(){$stream = $this->parser->getStream();$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '{', 'A hash element was expected');$node = new ArrayExpression([], $stream->getCurrent()->getLine());$first = true;while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) {if (!$first) {$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A hash value must be followed by a comma');// trailing ,?if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) {break;}}$first = false;// a hash key can be://// * a number -- 12// * a string -- 'a'// * a name, which is equivalent to a string -- a// * an expression, which must be enclosed in parentheses -- (1 + 2)if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) {$key = new ConstantExpression($token->getValue(), $token->getLine());// {a} is a shortcut for {a:a}if ($stream->test(Token::PUNCTUATION_TYPE, [',', '}'])) {$value = new NameExpression($key->getAttribute('value'), $key->getTemplateLine());$node->addElement($value, $key);continue;}} elseif (($token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) || $token = $stream->nextIf(/* Token::NUMBER_TYPE */ 6)) {$key = new ConstantExpression($token->getValue(), $token->getLine());} elseif ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {$key = $this->parseExpression();} else {$current = $stream->getCurrent();throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext());}$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ':', 'A hash key must be followed by a colon (:)');$value = $this->parseExpression();$node->addElement($value, $key);}$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '}', 'An opened hash is not properly closed');return $node;}public function parsePostfixExpression($node){while (true) {$token = $this->parser->getCurrentToken();if (/* Token::PUNCTUATION_TYPE */ 9 == $token->getType()) {if ('.' == $token->getValue() || '[' == $token->getValue()) {$node = $this->parseSubscriptExpression($node);} elseif ('|' == $token->getValue()) {$node = $this->parseFilterExpression($node);} else {break;}} else {break;}}return $node;}public function getFunctionNode($name, $line){switch ($name) {case 'parent':$this->parseArguments();if (!\count($this->parser->getBlockStack())) {throw new SyntaxError('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext());}if (!$this->parser->getParent() && !$this->parser->hasTraits()) {throw new SyntaxError('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext());}return new ParentExpression($this->parser->peekBlockStack(), $line);case 'block':$args = $this->parseArguments();if (\count($args) < 1) {throw new SyntaxError('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext());}return new BlockReferenceExpression($args->getNode(0), \count($args) > 1 ? $args->getNode(1) : null, $line);case 'attribute':$args = $this->parseArguments();if (\count($args) < 2) {throw new SyntaxError('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext());}return new GetAttrExpression($args->getNode(0), $args->getNode(1), \count($args) > 2 ? $args->getNode(2) : null, Template::ANY_CALL, $line);default:if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {$arguments = new ArrayExpression([], $line);foreach ($this->parseArguments() as $n) {$arguments->addElement($n);}$node = new MethodCallExpression($alias['node'], $alias['name'], $arguments, $line);$node->setAttribute('safe', true);return $node;}$args = $this->parseArguments(true);$class = $this->getFunctionNodeClass($name, $line);return new $class($name, $args, $line);}}public function parseSubscriptExpression($node){$stream = $this->parser->getStream();$token = $stream->next();$lineno = $token->getLine();$arguments = new ArrayExpression([], $lineno);$type = Template::ANY_CALL;if ('.' == $token->getValue()) {$token = $stream->next();if (/* Token::NAME_TYPE */ 5 == $token->getType()||/* Token::NUMBER_TYPE */ 6 == $token->getType()||(/* Token::OPERATOR_TYPE */ 8 == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue()))) {$arg = new ConstantExpression($token->getValue(), $lineno);if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {$type = Template::METHOD_CALL;foreach ($this->parseArguments() as $n) {$arguments->addElement($n);}}} else {throw new SyntaxError(sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), Token::typeToEnglish($token->getType())), $lineno, $stream->getSourceContext());}if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {if (!$arg instanceof ConstantExpression) {throw new SyntaxError(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext());}$name = $arg->getAttribute('value');$node = new MethodCallExpression($node, 'macro_'.$name, $arguments, $lineno);$node->setAttribute('safe', true);return $node;}} else {$type = Template::ARRAY_CALL;// slice?$slice = false;if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ':')) {$slice = true;$arg = new ConstantExpression(0, $token->getLine());} else {$arg = $this->parseExpression();}if ($stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {$slice = true;}if ($slice) {if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {$length = new ConstantExpression(null, $token->getLine());} else {$length = $this->parseExpression();}$class = $this->getFilterNodeClass('slice', $token->getLine());$arguments = new Node([$arg, $length]);$filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine());$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']');return $filter;}$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']');}return new GetAttrExpression($node, $arg, $arguments, $type, $lineno);}public function parseFilterExpression($node){$this->parser->getStream()->next();return $this->parseFilterExpressionRaw($node);}public function parseFilterExpressionRaw($node, $tag = null){while (true) {$token = $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5);$name = new ConstantExpression($token->getValue(), $token->getLine());if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {$arguments = new Node();} else {$arguments = $this->parseArguments(true, false, true);}$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());$node = new $class($node, $name, $arguments, $token->getLine(), $tag);if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '|')) {break;}$this->parser->getStream()->next();}return $node;}/*** Parses arguments.** @param bool $namedArguments Whether to allow named arguments or not* @param bool $definition Whether we are parsing arguments for a function definition** @return Node** @throws SyntaxError*/public function parseArguments($namedArguments = false, $definition = false, $allowArrow = false){$args = [];$stream = $this->parser->getStream();$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(', 'A list of arguments must begin with an opening parenthesis');while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {if (!empty($args)) {$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'Arguments must be separated by a comma');// if the comma above was a trailing comma, early exit the argument parse loopif ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {break;}}if ($definition) {$token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'An argument must be a name');$value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine());} else {$value = $this->parseExpression(0, $allowArrow);}$name = null;if ($namedArguments && $token = $stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) {if (!$value instanceof NameExpression) {throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext());}$name = $value->getAttribute('name');if ($definition) {$value = $this->parsePrimaryExpression();if (!$this->checkConstantExpression($value)) {throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, or an array).', $token->getLine(), $stream->getSourceContext());}} else {$value = $this->parseExpression(0, $allowArrow);}}if ($definition) {if (null === $name) {$name = $value->getAttribute('name');$value = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine());}$args[$name] = $value;} else {if (null === $name) {$args[] = $value;} else {$args[$name] = $value;}}}$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'A list of arguments must be closed by a parenthesis');return new Node($args);}public function parseAssignmentExpression(){$stream = $this->parser->getStream();$targets = [];while (true) {$token = $this->parser->getCurrentToken();if ($stream->test(/* Token::OPERATOR_TYPE */ 8) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {// in this context, string operators are variable names$this->parser->getStream()->next();} else {$stream->expect(/* Token::NAME_TYPE */ 5, null, 'Only variables can be assigned to');}$value = $token->getValue();if (\in_array(strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), ['true', 'false', 'none', 'null'])) {throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext());}$targets[] = new AssignNameExpression($value, $token->getLine());if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {break;}}return new Node($targets);}public function parseMultitargetExpression(){$targets = [];while (true) {$targets[] = $this->parseExpression();if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {break;}}return new Node($targets);}private function parseNotTestExpression(Node $node): NotUnary{return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine());}private function parseTestExpression(Node $node): TestExpression{$stream = $this->parser->getStream();list($name, $test) = $this->getTest($node->getTemplateLine());$class = $this->getTestNodeClass($test);$arguments = null;if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {$arguments = $this->parseArguments(true);} elseif ($test->hasOneMandatoryArgument()) {$arguments = new Node([0 => $this->parsePrimaryExpression()]);}if ('defined' === $name && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) {$node = new MethodCallExpression($alias['node'], $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine());$node->setAttribute('safe', true);}return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());}private function getTest(int $line): array{$stream = $this->parser->getStream();$name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue();if ($test = $this->env->getTest($name)) {return [$name, $test];}if ($stream->test(/* Token::NAME_TYPE */ 5)) {// try 2-words tests$name = $name.' '.$this->parser->getCurrentToken()->getValue();if ($test = $this->env->getTest($name)) {$stream->next();return [$name, $test];}}$e = new SyntaxError(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext());$e->addSuggestions($name, array_keys($this->env->getTests()));throw $e;}private function getTestNodeClass(TwigTest $test): string{if ($test->isDeprecated()) {$stream = $this->parser->getStream();$message = sprintf('Twig Test "%s" is deprecated', $test->getName());if ($test->getDeprecatedVersion()) {$message .= sprintf(' since version %s', $test->getDeprecatedVersion());}if ($test->getAlternative()) {$message .= sprintf('. Use "%s" instead', $test->getAlternative());}$src = $stream->getSourceContext();$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $stream->getCurrent()->getLine());@trigger_error($message, \E_USER_DEPRECATED);}return $test->getNodeClass();}private function getFunctionNodeClass(string $name, int $line): string{if (!$function = $this->env->getFunction($name)) {$e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext());$e->addSuggestions($name, array_keys($this->env->getFunctions()));throw $e;}if ($function->isDeprecated()) {$message = sprintf('Twig Function "%s" is deprecated', $function->getName());if ($function->getDeprecatedVersion()) {$message .= sprintf(' since version %s', $function->getDeprecatedVersion());}if ($function->getAlternative()) {$message .= sprintf('. Use "%s" instead', $function->getAlternative());}$src = $this->parser->getStream()->getSourceContext();$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);@trigger_error($message, \E_USER_DEPRECATED);}return $function->getNodeClass();}private function getFilterNodeClass(string $name, int $line): string{if (!$filter = $this->env->getFilter($name)) {$e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext());$e->addSuggestions($name, array_keys($this->env->getFilters()));throw $e;}if ($filter->isDeprecated()) {$message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());if ($filter->getDeprecatedVersion()) {$message .= sprintf(' since version %s', $filter->getDeprecatedVersion());}if ($filter->getAlternative()) {$message .= sprintf('. Use "%s" instead', $filter->getAlternative());}$src = $this->parser->getStream()->getSourceContext();$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);@trigger_error($message, \E_USER_DEPRECATED);}return $filter->getNodeClass();}// checks that the node only contains "constant" elementsprivate function checkConstantExpression(Node $node): bool{if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression|| $node instanceof NegUnary || $node instanceof PosUnary)) {return false;}foreach ($node as $n) {if (!$this->checkConstantExpression($n)) {return false;}}return true;}}