mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-11 01:46:37 +00:00
Enforce AbstractBinary for all binary operators
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
# 3.16.0 (2024-11-29)
|
||||
|
||||
* Deprecate `ConditionalExpression` and `NullCoalesceExpression` (use `ConditionalTernary` and `NullCoalesceBinary` instead)
|
||||
* Deprecate `InlinePrint`
|
||||
* Fix having macro variables starting with an underscore
|
||||
* Deprecate not passing a `Source` instance to `TokenStream`
|
||||
|
||||
+9
-1
@@ -189,7 +189,15 @@ Nodes
|
||||
made ready for ``yield``; the ``use_yield`` Environment option can be turned
|
||||
on when all nodes use the ``#[\Twig\Attribute\YieldReady]`` attribute.
|
||||
|
||||
* The ``InlinePrint`` class is deprecated as of Twig 3.16 with no replacement.
|
||||
* The ``Twig\Node\InlinePrint`` class is deprecated as of Twig 3.16 with no
|
||||
replacement.
|
||||
|
||||
* The ``Twig\Node\Expression\NullCoalesceExpression`` class is deprecated as
|
||||
of Twig 3.16, use ``Twig\Node\Expression\Binary\NullCoalesceBinary``
|
||||
instead.
|
||||
|
||||
* The ``Twig\Node\Expression\ConditionalExpression`` class is deprecated as of
|
||||
Twig 3.16, use ``Twig\Node\Expression\Ternary\ConditionalTernary`` instead.
|
||||
|
||||
Node Visitors
|
||||
-------------
|
||||
|
||||
@@ -20,11 +20,11 @@ use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\ArrowFunctionExpression;
|
||||
use Twig\Node\Expression\Binary\AbstractBinary;
|
||||
use Twig\Node\Expression\Binary\ConcatBinary;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\MacroReferenceExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\Ternary\ConditionalTernary;
|
||||
use Twig\Node\Expression\TestExpression;
|
||||
use Twig\Node\Expression\Unary\AbstractUnary;
|
||||
use Twig\Node\Expression\Unary\NegUnary;
|
||||
@@ -269,22 +269,16 @@ class ExpressionParser
|
||||
private function parseConditionalExpression($expr): AbstractExpression
|
||||
{
|
||||
while ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, '?')) {
|
||||
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
// Ternary operator (expr ? expr2 : expr3)
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
// Ternary without else (expr ? expr2)
|
||||
$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
} else {
|
||||
// Ternary without then (expr ?: expr3)
|
||||
$expr2 = $expr;
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
// Ternary operator (expr ? expr2 : expr3)
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
// Ternary without else (expr ? expr2)
|
||||
$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
$expr = new ConditionalExpression($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
|
||||
$expr = new ConditionalTernary($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
return $expr;
|
||||
|
||||
@@ -26,6 +26,7 @@ use Twig\Node\Expression\Binary\BitwiseOrBinary;
|
||||
use Twig\Node\Expression\Binary\BitwiseXorBinary;
|
||||
use Twig\Node\Expression\Binary\ConcatBinary;
|
||||
use Twig\Node\Expression\Binary\DivBinary;
|
||||
use Twig\Node\Expression\Binary\ElvisBinary;
|
||||
use Twig\Node\Expression\Binary\EndsWithBinary;
|
||||
use Twig\Node\Expression\Binary\EqualBinary;
|
||||
use Twig\Node\Expression\Binary\FloorDivBinary;
|
||||
@@ -41,6 +42,7 @@ use Twig\Node\Expression\Binary\ModBinary;
|
||||
use Twig\Node\Expression\Binary\MulBinary;
|
||||
use Twig\Node\Expression\Binary\NotEqualBinary;
|
||||
use Twig\Node\Expression\Binary\NotInBinary;
|
||||
use Twig\Node\Expression\Binary\NullCoalesceBinary;
|
||||
use Twig\Node\Expression\Binary\OrBinary;
|
||||
use Twig\Node\Expression\Binary\PowerBinary;
|
||||
use Twig\Node\Expression\Binary\RangeBinary;
|
||||
@@ -320,6 +322,8 @@ final class CoreExtension extends AbstractExtension
|
||||
'+' => ['precedence' => 500, 'class' => PosUnary::class],
|
||||
],
|
||||
[
|
||||
'?:' => ['precedence' => 5, 'class' => ElvisBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],
|
||||
'??' => ['precedence' => 300, 'precedence_change' => new OperatorPrecedenceChange('twig/twig', '3.15', 5), 'class' => NullCoalesceBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],
|
||||
'or' => ['precedence' => 10, 'class' => OrBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
|
||||
'xor' => ['precedence' => 12, 'class' => XorBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
|
||||
'and' => ['precedence' => 15, 'class' => AndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
|
||||
@@ -351,7 +355,6 @@ final class CoreExtension extends AbstractExtension
|
||||
'is' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],
|
||||
'is not' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT],
|
||||
'**' => ['precedence' => 200, 'class' => PowerBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],
|
||||
'??' => ['precedence' => 300, 'precedence_change' => new OperatorPrecedenceChange('twig/twig', '3.15', 5), 'class' => NullCoalesceExpression::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\OperatorEscapeInterface;
|
||||
|
||||
final class ElvisBinary extends AbstractBinary implements OperatorEscapeInterface
|
||||
{
|
||||
public function __construct(AbstractExpression $left, AbstractExpression $right, int $lineno)
|
||||
{
|
||||
parent::__construct($left, $right, $lineno);
|
||||
|
||||
$this->setNode('test', clone $left);
|
||||
$left->setAttribute('always_defined', true);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->raw('((')
|
||||
->subcompile($this->getNode('test'))
|
||||
->raw(') ? (')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(') : (')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw('))')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('?:');
|
||||
}
|
||||
|
||||
public function getOperandNamesToEscape(): array
|
||||
{
|
||||
return ['left', 'right'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\EmptyNode;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\BlockReferenceExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\OperatorEscapeInterface;
|
||||
use Twig\Node\Expression\Test\DefinedTest;
|
||||
use Twig\Node\Expression\Test\NullTest;
|
||||
use Twig\Node\Expression\Unary\NotUnary;
|
||||
use Twig\TwigTest;
|
||||
|
||||
final class NullCoalesceBinary extends AbstractBinary implements OperatorEscapeInterface
|
||||
{
|
||||
public function __construct(AbstractExpression $left, AbstractExpression $right, int $lineno)
|
||||
{
|
||||
parent::__construct($left, $right, $lineno);
|
||||
|
||||
if (!$left instanceof NameExpression) {
|
||||
$left = clone $left;
|
||||
$test = new DefinedTest($left, new TwigTest('defined'), new EmptyNode(), $left->getTemplateLine());
|
||||
// for "block()", we don't need the null test as the return value is always a string
|
||||
if (!$left instanceof BlockReferenceExpression) {
|
||||
$test = new AndBinary(
|
||||
$test,
|
||||
new NotUnary(new NullTest($left, new TwigTest('null'), new EmptyNode(), $left->getTemplateLine()), $left->getTemplateLine()),
|
||||
$left->getTemplateLine(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->setNode('test', $test);
|
||||
} else {
|
||||
$left->setAttribute('always_defined', true);
|
||||
}
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
/*
|
||||
* This optimizes only one case. PHP 7 also supports more complex expressions
|
||||
* that can return null. So, for instance, if log is defined, log("foo") ?? "..." works,
|
||||
* but log($a["foo"]) ?? "..." does not if $a["foo"] is not defined. More advanced
|
||||
* cases might be implemented as an optimizer node visitor, but has not been done
|
||||
* as benefits are probably not worth the added complexity.
|
||||
*/
|
||||
if ($this->hasNode('test')) {
|
||||
$compiler
|
||||
->raw('((')
|
||||
->subcompile($this->getNode('test'))
|
||||
->raw(') ? (')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(') : (')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw('))')
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
parent::compile($compiler);
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('??');
|
||||
}
|
||||
|
||||
public function getOperandNamesToEscape(): array
|
||||
{
|
||||
return $this->hasNode('test') ? ['left', 'right'] : ['right'];
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,15 @@
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\OperatorEscapeInterface;
|
||||
use Twig\Node\Expression\Ternary\ConditionalTernary;
|
||||
|
||||
class ConditionalExpression extends AbstractExpression
|
||||
class ConditionalExpression extends AbstractExpression implements OperatorEscapeInterface
|
||||
{
|
||||
public function __construct(AbstractExpression $expr1, AbstractExpression $expr2, AbstractExpression $expr3, int $lineno)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.16', \sprintf('"%s" is deprecated; use "%s" instead.', __CLASS__, ConditionalTernary::class));
|
||||
|
||||
parent::__construct(['expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3], [], $lineno);
|
||||
}
|
||||
|
||||
@@ -42,4 +46,9 @@ class ConditionalExpression extends AbstractExpression
|
||||
->raw('))');
|
||||
}
|
||||
}
|
||||
|
||||
public function getOperandNamesToEscape(): array
|
||||
{
|
||||
return ['expr2', 'expr3'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ use Twig\Compiler;
|
||||
use Twig\Extension\CoreExtension;
|
||||
use Twig\Node\EmptyNode;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\Ternary\ConditionalTernary;
|
||||
use Twig\Node\Expression\Test\DefinedTest;
|
||||
use Twig\Node\Node;
|
||||
use Twig\TwigFilter;
|
||||
@@ -57,7 +57,7 @@ class DefaultFilter extends FilterExpression
|
||||
$test = new DefinedTest(clone $node, new TwigTest('defined'), new EmptyNode(), $node->getTemplateLine());
|
||||
$false = \count($arguments) ? $arguments->getNode('0') : new ConstantExpression('', $node->getTemplateLine());
|
||||
|
||||
$node = new ConditionalExpression($test, $default, $false, $node->getTemplateLine());
|
||||
$node = new ConditionalTernary($test, $default, $false, $node->getTemplateLine());
|
||||
} else {
|
||||
$node = $default;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Twig\Node\Expression;
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\EmptyNode;
|
||||
use Twig\Node\Expression\Binary\AndBinary;
|
||||
use Twig\Node\Expression\Binary\NullCoalesceBinary;
|
||||
use Twig\Node\Expression\Test\DefinedTest;
|
||||
use Twig\Node\Expression\Test\NullTest;
|
||||
use Twig\Node\Expression\Unary\NotUnary;
|
||||
@@ -28,6 +29,8 @@ class NullCoalesceExpression extends ConditionalExpression
|
||||
*/
|
||||
public function __construct(Node $left, Node $right, int $lineno)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.16', \sprintf('"%s" is deprecated; use "%s" instead.', __CLASS__, NullCoalesceBinary::class));
|
||||
|
||||
if (!$left instanceof AbstractExpression) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "left" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, get_class($left));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
/**
|
||||
* Interface implemented by n-ary operators for n > 1.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface OperatorEscapeInterface
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getOperandNamesToEscape(): array;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Ternary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\OperatorEscapeInterface;
|
||||
|
||||
final class ConditionalTernary extends AbstractExpression implements OperatorEscapeInterface
|
||||
{
|
||||
public function __construct(AbstractExpression $test, AbstractExpression $left, AbstractExpression $right, int $lineno)
|
||||
{
|
||||
parent::__construct(['test' => $test, 'left' => $left, 'right' => $right], [], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->raw('((')
|
||||
->subcompile($this->getNode('test'))
|
||||
->raw(') ? (')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(') : (')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw('))')
|
||||
;
|
||||
}
|
||||
|
||||
public function getOperandNamesToEscape(): array
|
||||
{
|
||||
return ['left', 'right'];
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ use Twig\Node\AutoEscapeNode;
|
||||
use Twig\Node\BlockNode;
|
||||
use Twig\Node\BlockReferenceNode;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
use Twig\Node\Expression\OperatorEscapeInterface;
|
||||
use Twig\Node\ImportNode;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
@@ -75,7 +75,7 @@ final class EscaperNodeVisitor implements NodeVisitorInterface
|
||||
return $this->preEscapeFilterNode($node, $env);
|
||||
} elseif ($node instanceof PrintNode && false !== $type = $this->needEscaping()) {
|
||||
$expression = $node->getNode('expr');
|
||||
if ($expression instanceof ConditionalExpression) {
|
||||
if ($expression instanceof OperatorEscapeInterface) {
|
||||
$this->escapeConditional($expression, $env, $type);
|
||||
} else {
|
||||
$node->setNode('expr', $this->escapeExpression($expression, $env, $type));
|
||||
@@ -93,22 +93,19 @@ final class EscaperNodeVisitor implements NodeVisitorInterface
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function escapeConditional(ConditionalExpression $expression, Environment $env, string $type): void
|
||||
/**
|
||||
* @param AbstractExpression&OperatorEscapeInterface $expression
|
||||
*/
|
||||
private function escapeConditional($expression, Environment $env, string $type): void
|
||||
{
|
||||
/** @var AbstractExpression $expr2 */
|
||||
$expr2 = $expression->getNode('expr2');
|
||||
if ($expr2 instanceof ConditionalExpression) {
|
||||
$this->escapeConditional($expr2, $env, $type);
|
||||
} else {
|
||||
$expression->setNode('expr2', $this->escapeExpression($expr2, $env, $type));
|
||||
}
|
||||
|
||||
/** @var AbstractExpression $expr3 */
|
||||
$expr3 = $expression->getNode('expr3');
|
||||
if ($expr3 instanceof ConditionalExpression) {
|
||||
$this->escapeConditional($expr3, $env, $type);
|
||||
} else {
|
||||
$expression->setNode('expr3', $this->escapeExpression($expr3, $env, $type));
|
||||
foreach ($expression->getOperandNamesToEscape() as $name) {
|
||||
/** @var AbstractExpression $operand */
|
||||
$operand = $expression->getNode($name);
|
||||
if ($operand instanceof OperatorEscapeInterface) {
|
||||
$this->escapeConditional($operand, $env, $type);
|
||||
} else {
|
||||
$expression->setNode($name, $this->escapeExpression($operand, $env, $type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace Twig\NodeVisitor;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\Expression\BlockReferenceExpression;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
use Twig\Node\Expression\FunctionExpression;
|
||||
@@ -21,6 +20,7 @@ use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\MacroReferenceExpression;
|
||||
use Twig\Node\Expression\MethodCallExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\OperatorEscapeInterface;
|
||||
use Twig\Node\Expression\ParentExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
@@ -96,10 +96,15 @@ final class SafeAnalysisNodeVisitor implements NodeVisitorInterface
|
||||
} elseif ($node instanceof ParentExpression) {
|
||||
// parent block is safe by definition
|
||||
$this->setSafe($node, ['all']);
|
||||
} elseif ($node instanceof ConditionalExpression) {
|
||||
// intersect safeness of both operands
|
||||
$safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3')));
|
||||
$this->setSafe($node, $safe);
|
||||
} elseif ($node instanceof OperatorEscapeInterface) {
|
||||
// intersect safeness of operands
|
||||
$operands = $node->getOperandNamesToEscape();
|
||||
if (2 < \count($operands)) {
|
||||
throw new \LogicException(\sprintf('Operators with more than 2 operands are not supported yet, got %d.', \count($operands)));
|
||||
} elseif (2 === \count($operands)) {
|
||||
$safe = $this->intersectSafe($this->getSafe($node->getNode($operands[0])), $this->getSafe($node->getNode($operands[1])));
|
||||
$this->setSafe($node, $safe);
|
||||
}
|
||||
} elseif ($node instanceof FilterExpression) {
|
||||
// filter expression is safe when the filter is safe
|
||||
if ($node->hasAttribute('twig_callable')) {
|
||||
|
||||
@@ -63,9 +63,13 @@ final class TypesTokenParser extends AbstractTokenParser
|
||||
$first = false;
|
||||
|
||||
$nameToken = $stream->expect(Token::NAME_TYPE);
|
||||
$isOptional = null !== $stream->nextIf(Token::PUNCTUATION_TYPE, '?');
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ':', 'A type name must be followed by a colon (:)');
|
||||
if ($stream->nextIf(Token::OPERATOR_TYPE, '?:')) {
|
||||
$isOptional = true;
|
||||
} else {
|
||||
$isOptional = null !== $stream->nextIf(Token::PUNCTUATION_TYPE, '?');
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ':', 'A type name must be followed by a colon (:)');
|
||||
}
|
||||
|
||||
$valueToken = $stream->expect(Token::STRING_TYPE);
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Twig\Tests\Node\Expression\Binary;
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Twig\Node\Expression\Binary\NullCoalesceBinary;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
class NullCoalesceTest extends NodeTestCase
|
||||
{
|
||||
public static function provideTests(): iterable
|
||||
{
|
||||
$left = new ContextVariable('foo', 1);
|
||||
$right = new ConstantExpression(2, 1);
|
||||
$node = new NullCoalesceBinary($left, $right, 1);
|
||||
|
||||
return [[$node, "(// line 1\n\$context[\"foo\"] ?? 2)"]];
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class ConditionalTest extends NodeTestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
|
||||
@@ -16,6 +16,9 @@ use Twig\Node\Expression\NullCoalesceExpression;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class NullCoalesceTest extends NodeTestCase
|
||||
{
|
||||
public static function provideTests(): iterable
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Twig\Tests\Node\Expression;
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Ternary\ConditionalTernary;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
class ConditionalTernaryTest extends NodeTestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$test = new ConstantExpression(1, 1);
|
||||
$left = new ConstantExpression(2, 1);
|
||||
$right = new ConstantExpression(3, 1);
|
||||
$node = new ConditionalTernary($test, $left, $right, 1);
|
||||
|
||||
$this->assertEquals($test, $node->getNode('test'));
|
||||
$this->assertEquals($left, $node->getNode('left'));
|
||||
$this->assertEquals($right, $node->getNode('right'));
|
||||
}
|
||||
|
||||
public static function provideTests(): iterable
|
||||
{
|
||||
$tests = [];
|
||||
|
||||
$test = new ConstantExpression(1, 1);
|
||||
$left = new ConstantExpression(2, 1);
|
||||
$right = new ConstantExpression(3, 1);
|
||||
$node = new ConditionalTernary($test, $left, $right, 1);
|
||||
$tests[] = [$node, '((1) ? (2) : (3))'];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ namespace Twig\Tests\Node;
|
||||
*/
|
||||
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Ternary\ConditionalTernary;
|
||||
use Twig\Node\IncludeNode;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
@@ -46,7 +46,7 @@ yield from $this->loadTemplate("foo.twig", null, 1)->unwrap()->yield($context);
|
||||
EOF
|
||||
];
|
||||
|
||||
$expr = new ConditionalExpression(
|
||||
$expr = new ConditionalTernary(
|
||||
new ConstantExpression(true, 1),
|
||||
new ConstantExpression('foo', 1),
|
||||
new ConstantExpression('foo', 1),
|
||||
|
||||
@@ -15,8 +15,8 @@ use Twig\Environment;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Node\BodyNode;
|
||||
use Twig\Node\EmptyNode;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Ternary\ConditionalTernary;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Expression\Variable\AssignTemplateVariable;
|
||||
use Twig\Node\Expression\Variable\TemplateVariable;
|
||||
@@ -223,7 +223,7 @@ EOF
|
||||
|
||||
$set = new SetNode(false, new Nodes([new AssignContextVariable('foo', 4)]), new Nodes([new ConstantExpression('foo', 4)]), 4);
|
||||
$body = new BodyNode([$set]);
|
||||
$extends = new ConditionalExpression(
|
||||
$extends = new ConditionalTernary(
|
||||
new ConstantExpression(true, 2),
|
||||
new ConstantExpression('foo', 2),
|
||||
new ConstantExpression('foo', 2),
|
||||
|
||||
Reference in New Issue
Block a user