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\Node\Expression; 13 14use Twig\Attribute\FirstClassTwigCallableReady; 15use Twig\Compiler; 16use Twig\Node\CoercesChildrenToStringInterface; 17use Twig\Node\NameDeprecation; 18use Twig\Node\Node; 19use Twig\TwigTest; 20 21class TestExpression extends CallExpression implements ReturnBoolInterface, CoercesChildrenToStringInterface 22{ 23 #[FirstClassTwigCallableReady] 24 /** 25 * @param AbstractExpression $node 26 */ 27 public function __construct(Node $node, string|TwigTest $test, ?Node $arguments, int $lineno) 28 { 29 if (!$node instanceof AbstractExpression) { 30 trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $node::class); 31 } 32 33 $nodes = ['node' => $node]; 34 if (null !== $arguments) { 35 $nodes['arguments'] = $arguments; 36 } 37 38 if ($test instanceof TwigTest) { 39 $name = $test->getName(); 40 } else { 41 $name = $test; 42 trigger_deprecation('twig/twig', '3.12', 'Not passing an instance of "TwigTest" when creating a "%s" test of type "%s" is deprecated.', $name, static::class); 43 } 44 45 parent::__construct($nodes, ['name' => $name, 'type' => 'test'], $lineno); 46 47 if ($test instanceof TwigTest) { 48 $this->setAttribute('twig_callable', $test); 49 } 50 51 $this->deprecateAttribute('arguments', new NameDeprecation('twig/twig', '3.12')); 52 $this->deprecateAttribute('callable', new NameDeprecation('twig/twig', '3.12')); 53 $this->deprecateAttribute('is_variadic', new NameDeprecation('twig/twig', '3.12')); 54 $this->deprecateAttribute('dynamic_name', new NameDeprecation('twig/twig', '3.12')); 55 } 56 57 public function compile(Compiler $compiler): void 58 { 59 $name = $this->getAttribute('name'); 60 if ($this->hasAttribute('twig_callable')) { 61 $name = $this->getAttribute('twig_callable')->getName(); 62 if ($name !== $this->getAttribute('name')) { 63 trigger_deprecation('twig/twig', '3.12', 'Changing the value of a "test" node in a NodeVisitor class is not supported anymore.'); 64 $this->removeAttribute('twig_callable'); 65 } 66 } 67 68 if (!$this->hasAttribute('twig_callable')) { 69 $this->setAttribute('twig_callable', $compiler->getEnvironment()->getTest($this->getAttribute('name'))); 70 } 71 72 $this->compileCallable($compiler); 73 } 74 75 public function getStringCoercedChildNames(): array 76 { 77 $names = []; 78 79 // the `empty` test triggers an implicit string coercion through `CoreExtension::testEmpty()` 80 if ('empty' === $this->getAttribute('name')) { 81 $names[] = 'node'; 82 } 83 84 // a test may coerce its arguments to string (the host PHP code is opaque to Twig) 85 if ($this->hasNode('arguments')) { 86 $names[] = 'arguments'; 87 } 88 89 return $names; 90 } 91} 92