diff --git a/src/ExpressionParser.php b/src/ExpressionParser.php index 9922d11ec9b..d90d5f90bfd 100644 --- a/src/ExpressionParser.php +++ b/src/ExpressionParser.php @@ -162,10 +162,10 @@ public function parseSubscriptExpression($node) $parsers = new \ReflectionProperty($this->parser, 'parsers'); if ('.' === $this->parser->getStream()->next()->getValue()) { - return $parsers->getValue($this->parser)->getInfixByClass(DotExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken()); + return $parsers->getValue($this->parser)->getByClass(DotExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken()); } - return $parsers->getValue($this->parser)->getInfixByClass(SquareBracketExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken()); + return $parsers->getValue($this->parser)->getByClass(SquareBracketExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken()); } /** @@ -189,7 +189,7 @@ public function parseFilterExpressionRaw($node) $parsers = new \ReflectionProperty($this->parser, 'parsers'); - $op = $parsers->getValue($this->parser)->getInfixByClass(FilterExpressionParser::class); + $op = $parsers->getValue($this->parser)->getByClass(FilterExpressionParser::class); while (true) { $node = $op->parse($this->parser, $node, $this->parser->getCurrentToken()); if (!$this->parser->getStream()->test(Token::OPERATOR_TYPE, '|')) { diff --git a/src/ExpressionParser/ExpressionParsers.php b/src/ExpressionParser/ExpressionParsers.php index e973465b233..5dd9e89eb50 100644 --- a/src/ExpressionParser/ExpressionParsers.php +++ b/src/ExpressionParser/ExpressionParsers.php @@ -19,20 +19,15 @@ final class ExpressionParsers implements \IteratorAggregate { /** - * @var array, array> + * @var array, array> */ - private array $parsers = []; + private array $parsersByName = []; /** - * @var array, array, ExpressionParserInterface>> + * @var array, ExpressionParserInterface> */ private array $parsersByClass = []; - /** - * @var array, array> - */ - private array $aliases = []; - /** * @var \WeakMap>|null */ @@ -59,11 +54,11 @@ public function add(array $parsers): self trigger_deprecation('twig/twig', '3.20', 'Precedence for "%s" must be between -512 and 512, got %d.', $parser->getName(), $parser->getPrecedence()); // throw new \InvalidArgumentException(\sprintf('Precedence for "%s" must be between -1024 and 1024, got %d.', $parser->getName(), $parser->getPrecedence())); } - $type = ExpressionParserType::getType($parser); - $this->parsers[$type->value][$parser->getName()] = $parser; - $this->parsersByClass[$type->value][get_class($parser)] = $parser; + $interface = $parser instanceof PrefixExpressionParserInterface ? PrefixExpressionParserInterface::class : InfixExpressionParserInterface::class; + $this->parsersByName[$interface][$parser->getName()] = $parser; + $this->parsersByClass[get_class($parser)] = $parser; foreach ($parser->getAliases() as $alias) { - $this->aliases[$type->value][$alias] = $parser; + $this->parsersByName[$interface][$alias] = $parser; } } @@ -71,42 +66,32 @@ public function add(array $parsers): self } /** - * @param class-string $name + * @template T of ExpressionParserInterface + * + * @param class-string $class + * + * @return T|null */ - public function getPrefixByClass(string $name): ?PrefixExpressionParserInterface + public function getByClass(string $class): ?ExpressionParserInterface { - return $this->parsersByClass[ExpressionParserType::Prefix->value][$name] ?? null; - } - - public function getPrefix(string $name): ?PrefixExpressionParserInterface - { - return - $this->parsers[ExpressionParserType::Prefix->value][$name] - ?? $this->aliases[ExpressionParserType::Prefix->value][$name] - ?? null - ; + return $this->parsersByClass[$class] ?? null; } /** - * @param class-string $name + * @template T of ExpressionParserInterface + * + * @param class-string $interface + * + * @return T|null */ - public function getInfixByClass(string $name): ?InfixExpressionParserInterface - { - return $this->parsersByClass[ExpressionParserType::Infix->value][$name] ?? null; - } - - public function getInfix(string $name): ?InfixExpressionParserInterface + public function getByName(string $interface, string $name): ?ExpressionParserInterface { - return - $this->parsers[ExpressionParserType::Infix->value][$name] - ?? $this->aliases[ExpressionParserType::Infix->value][$name] - ?? null - ; + return $this->parsersByName[$interface][$name] ?? null; } public function getIterator(): \Traversable { - foreach ($this->parsers as $parsers) { + foreach ($this->parsersByName as $parsers) { // we don't yield the keys yield from $parsers; } diff --git a/src/Parser.php b/src/Parser.php index 59d45d986a9..5e2d1f9ec54 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -16,6 +16,7 @@ use Twig\ExpressionParser\ExpressionParserInterface; use Twig\ExpressionParser\ExpressionParsers; use Twig\ExpressionParser\ExpressionParserType; +use Twig\ExpressionParser\InfixExpressionParserInterface; use Twig\ExpressionParser\Prefix\LiteralExpressionParser; use Twig\ExpressionParser\PrefixExpressionParserInterface; use Twig\Node\BlockNode; @@ -357,16 +358,16 @@ public function getExpressionParser(): ExpressionParser public function parseExpression(int $precedence = 0): AbstractExpression { $token = $this->getCurrentToken(); - if ($token->test(Token::OPERATOR_TYPE) && $ep = $this->parsers->getPrefix($token->getValue())) { + if ($token->test(Token::OPERATOR_TYPE) && $ep = $this->parsers->getByName(PrefixExpressionParserInterface::class, $token->getValue())) { $this->getStream()->next(); $expr = $ep->parse($this, $token); $this->checkPrecedenceDeprecations($ep, $expr); } else { - $expr = $this->parsers->getPrefixByClass(LiteralExpressionParser::class)->parse($this, $token); + $expr = $this->parsers->getByClass(LiteralExpressionParser::class)->parse($this, $token); } $token = $this->getCurrentToken(); - while ($token->test(Token::OPERATOR_TYPE) && ($ep = $this->parsers->getInfix($token->getValue())) && $ep->getPrecedence() >= $precedence) { + while ($token->test(Token::OPERATOR_TYPE) && ($ep = $this->parsers->getByName(InfixExpressionParserInterface::class, $token->getValue())) && $ep->getPrecedence() >= $precedence) { $this->getStream()->next(); $expr = $ep->parse($this, $expr, $token); $this->checkPrecedenceDeprecations($ep, $expr); diff --git a/src/TokenParser/ApplyTokenParser.php b/src/TokenParser/ApplyTokenParser.php index e4e3cfaebf0..5b560e74916 100644 --- a/src/TokenParser/ApplyTokenParser.php +++ b/src/TokenParser/ApplyTokenParser.php @@ -35,7 +35,7 @@ public function parse(Token $token): Node $lineno = $token->getLine(); $ref = new LocalVariable(null, $lineno); $filter = $ref; - $op = $this->parser->getEnvironment()->getExpressionParsers()->getInfixByClass(FilterExpressionParser::class); + $op = $this->parser->getEnvironment()->getExpressionParsers()->getByClass(FilterExpressionParser::class); while (true) { $filter = $op->parse($this->parser, $filter, $this->parser->getCurrentToken()); if (!$this->parser->getStream()->test(Token::OPERATOR_TYPE, '|')) { diff --git a/tests/EnvironmentTest.php b/tests/EnvironmentTest.php index 5ddf07009bd..19f2fce4677 100644 --- a/tests/EnvironmentTest.php +++ b/tests/EnvironmentTest.php @@ -19,7 +19,9 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Twig\ExpressionParser\Infix\BinaryOperatorExpressionParser; +use Twig\ExpressionParser\InfixExpressionParserInterface; use Twig\ExpressionParser\Prefix\UnaryOperatorExpressionParser; +use Twig\ExpressionParser\PrefixExpressionParserInterface; use Twig\Extension\AbstractExtension; use Twig\Extension\ExtensionInterface; use Twig\Extension\GlobalsInterface; @@ -309,8 +311,8 @@ public function testAddExtension() $this->assertArrayHasKey('foo_filter', $twig->getFilters()); $this->assertArrayHasKey('foo_function', $twig->getFunctions()); $this->assertArrayHasKey('foo_test', $twig->getTests()); - $this->assertNotNull($twig->getExpressionParsers()->getPrefix('foo_unary')); - $this->assertNotNull($twig->getExpressionParsers()->getInfix('foo_binary')); + $this->assertNotNull($twig->getExpressionParsers()->getByName(PrefixExpressionParserInterface::class, 'foo_unary')); + $this->assertNotNull($twig->getExpressionParsers()->getByName(InfixExpressionParserInterface::class, 'foo_binary')); $this->assertArrayHasKey('foo_global', $twig->getGlobals()); $visitors = $twig->getNodeVisitors(); $found = false;