1<?php 2 3/* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Twig\ExpressionParser\Infix; 13 14use Twig\Attribute\FirstClassTwigCallableReady; 15use Twig\ExpressionParser\AbstractExpressionParser; 16use Twig\ExpressionParser\ExpressionParserDescriptionInterface; 17use Twig\ExpressionParser\InfixAssociativity; 18use Twig\ExpressionParser\InfixExpressionParserInterface; 19use Twig\Node\Expression\AbstractExpression; 20use Twig\Node\Expression\ArrayExpression; 21use Twig\Node\Expression\MacroReferenceExpression; 22use Twig\Node\Expression\NameExpression; 23use Twig\Node\Nodes; 24use Twig\Parser; 25use Twig\Token; 26use Twig\TwigTest; 27 28/** 29 * @internal 30 */ 31class IsExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface 32{ 33 use ArgumentsTrait; 34 35 private $readyNodes = []; 36 37 public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression 38 { 39 $stream = $parser->getStream(); 40 $test = $parser->getTest($token->getLine()); 41 42 $arguments = null; 43 if ($stream->test(Token::OPERATOR_TYPE, '(')) { 44 $arguments = $this->parseNamedArguments($parser); 45 } elseif ($test->hasOneMandatoryArgument()) { 46 $arguments = new Nodes([0 => $parser->parseExpression($this->getPrecedence())]); 47 } 48 49 if ('defined' === $test->getName() && $expr instanceof NameExpression && null !== $alias = $parser->getImportedSymbol('function', $expr->getAttribute('name'))) { 50 $expr = new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], new ArrayExpression([], $expr->getTemplateLine()), $expr->getTemplateLine()); 51 } 52 53 $ready = $test instanceof TwigTest; 54 if (!isset($this->readyNodes[$class = $test->getNodeClass()])) { 55 $this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class); 56 } 57 58 if (!$ready = $this->readyNodes[$class]) { 59 trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigTest" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class); 60 } 61 62 return new $class($expr, $ready ? $test : $test->getName(), $arguments, $stream->getCurrent()->getLine()); 63 } 64 65 public function getPrecedence(): int 66 { 67 return 100; 68 } 69 70 public function getName(): string 71 { 72 return 'is'; 73 } 74 75 public function getDescription(): string 76 { 77 return 'Twig tests'; 78 } 79 80 public function getAssociativity(): InfixAssociativity 81 { 82 return InfixAssociativity::Left; 83 } 84} 85