mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-12 18:36:53 +00:00
minor #3860 Fix spread operator implementation (nicolas-grekas)
This PR was merged into the 3.x branch.
Discussion
----------
Fix spread operator implementation
#3839 is too invasive, leading to failures like https://github.com/symfony/symfony/actions/runs/5612246672/jobs/10269741115
Commits
-------
75efa5e1 Fix spread operator implementation
This commit is contained in:
@@ -59,9 +59,6 @@ class ArrayExpression extends AbstractExpression
|
||||
{
|
||||
if (null === $key) {
|
||||
$key = new ConstantExpression(++$this->index, $value->getTemplateLine());
|
||||
$key->setAttribute('index_specified', false);
|
||||
} else {
|
||||
$key->setAttribute('index_specified', true);
|
||||
}
|
||||
|
||||
array_push($this->nodes, $key, $value);
|
||||
@@ -70,8 +67,7 @@ class ArrayExpression extends AbstractExpression
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$keyValuePairs = $this->getKeyValuePairs();
|
||||
$hasSpreadItem = $this->hasSpreadItem($keyValuePairs);
|
||||
$needsArrayMergeSpread = \PHP_VERSION_ID < 80100 && $hasSpreadItem;
|
||||
$needsArrayMergeSpread = \PHP_VERSION_ID < 80100 && $this->hasSpreadItem($keyValuePairs);
|
||||
|
||||
if ($needsArrayMergeSpread) {
|
||||
$compiler->raw('twig_array_merge(');
|
||||
@@ -79,6 +75,7 @@ class ArrayExpression extends AbstractExpression
|
||||
$compiler->raw('[');
|
||||
$first = true;
|
||||
$reopenAfterMergeSpread = false;
|
||||
$nextIndex = 0;
|
||||
foreach ($keyValuePairs as $pair) {
|
||||
if ($reopenAfterMergeSpread) {
|
||||
$compiler->raw(', [');
|
||||
@@ -98,14 +95,22 @@ class ArrayExpression extends AbstractExpression
|
||||
|
||||
if ($pair['value']->hasAttribute('spread') && !$needsArrayMergeSpread) {
|
||||
$compiler->raw('...')->subcompile($pair['value']);
|
||||
++$nextIndex;
|
||||
} else {
|
||||
$indexSpecified = false === $pair['key']->hasAttribute('index_specified') || true === $pair['key']->getAttribute('index_specified');
|
||||
if ($indexSpecified) {
|
||||
$key = $pair['key'] instanceof ConstantExpression ? $pair['key']->getAttribute('value') : null;
|
||||
|
||||
if ($nextIndex !== $key) {
|
||||
if (\is_int($key)) {
|
||||
$nextIndex = $key + 1;
|
||||
}
|
||||
$compiler
|
||||
->subcompile($pair['key'])
|
||||
->raw(' => ')
|
||||
;
|
||||
} else {
|
||||
++$nextIndex;
|
||||
}
|
||||
|
||||
$compiler->subcompile($pair['value']);
|
||||
}
|
||||
}
|
||||
@@ -117,7 +122,7 @@ class ArrayExpression extends AbstractExpression
|
||||
}
|
||||
}
|
||||
|
||||
private function hasSpreadItem(array $pairs)
|
||||
private function hasSpreadItem(array $pairs): bool
|
||||
{
|
||||
foreach ($pairs as $pair) {
|
||||
if ($pair['value']->hasAttribute('spread')) {
|
||||
|
||||
@@ -93,106 +93,104 @@ class ExpressionParserTest extends TestCase
|
||||
return [
|
||||
// simple array
|
||||
['{{ [1, 2] }}', new ArrayExpression([
|
||||
$this->createConstantExpression(0, false),
|
||||
$this->createConstantExpression(1),
|
||||
new ConstantExpression(0, 1),
|
||||
new ConstantExpression(1, 1),
|
||||
|
||||
$this->createConstantExpression(1, false),
|
||||
$this->createConstantExpression(2),
|
||||
new ConstantExpression(1, 1),
|
||||
new ConstantExpression(2, 1),
|
||||
], 1),
|
||||
],
|
||||
|
||||
// array with trailing ,
|
||||
['{{ [1, 2, ] }}', new ArrayExpression([
|
||||
$this->createConstantExpression(0, false),
|
||||
$this->createConstantExpression(1),
|
||||
new ConstantExpression(0, 1),
|
||||
new ConstantExpression(1, 1),
|
||||
|
||||
$this->createConstantExpression(1, false),
|
||||
$this->createConstantExpression(2),
|
||||
new ConstantExpression(1, 1),
|
||||
new ConstantExpression(2, 1),
|
||||
], 1),
|
||||
],
|
||||
|
||||
// simple hash
|
||||
['{{ {"a": "b", "b": "c"} }}', new ArrayExpression([
|
||||
$this->createConstantExpression('a', true),
|
||||
$this->createConstantExpression('b'),
|
||||
new ConstantExpression('a', 1),
|
||||
new ConstantExpression('b', 1),
|
||||
|
||||
$this->createConstantExpression('b', true),
|
||||
$this->createConstantExpression('c'),
|
||||
new ConstantExpression('b', 1),
|
||||
new ConstantExpression('c', 1),
|
||||
], 1),
|
||||
],
|
||||
|
||||
// hash with trailing ,
|
||||
['{{ {"a": "b", "b": "c", } }}', new ArrayExpression([
|
||||
$this->createConstantExpression('a', true),
|
||||
$this->createConstantExpression('b'),
|
||||
new ConstantExpression('a', 1),
|
||||
new ConstantExpression('b', 1),
|
||||
|
||||
$this->createConstantExpression('b', true),
|
||||
$this->createConstantExpression('c'),
|
||||
new ConstantExpression('b', 1),
|
||||
new ConstantExpression('c', 1),
|
||||
], 1),
|
||||
],
|
||||
|
||||
// hash in an array
|
||||
['{{ [1, {"a": "b", "b": "c"}] }}', new ArrayExpression([
|
||||
$this->createConstantExpression(0, false),
|
||||
$this->createConstantExpression(1),
|
||||
new ConstantExpression(0, 1),
|
||||
new ConstantExpression(1, 1),
|
||||
|
||||
$this->createConstantExpression(1, false),
|
||||
new ConstantExpression(1, 1),
|
||||
new ArrayExpression([
|
||||
$this->createConstantExpression('a', true),
|
||||
$this->createConstantExpression('b'),
|
||||
new ConstantExpression('a', 1),
|
||||
new ConstantExpression('b', 1),
|
||||
|
||||
$this->createConstantExpression('b', true),
|
||||
$this->createConstantExpression('c'),
|
||||
new ConstantExpression('b', 1),
|
||||
new ConstantExpression('c', 1),
|
||||
], 1),
|
||||
], 1),
|
||||
],
|
||||
|
||||
// array in a hash
|
||||
['{{ {"a": [1, 2], "b": "c"} }}', new ArrayExpression([
|
||||
$this->createConstantExpression('a', true),
|
||||
new ArrayExpression([
|
||||
$this->createConstantExpression(0, false),
|
||||
$this->createConstantExpression(1),
|
||||
new ConstantExpression('a', 1),
|
||||
new ArrayExpression([
|
||||
new ConstantExpression(0, 1),
|
||||
new ConstantExpression(1, 1),
|
||||
|
||||
$this->createConstantExpression(1, false),
|
||||
$this->createConstantExpression(2),
|
||||
], 1),
|
||||
|
||||
$this->createConstantExpression('b', true),
|
||||
$this->createConstantExpression('c'),
|
||||
new ConstantExpression(1, 1),
|
||||
new ConstantExpression(2, 1),
|
||||
], 1),
|
||||
new ConstantExpression('b', 1),
|
||||
new ConstantExpression('c', 1),
|
||||
], 1),
|
||||
],
|
||||
['{{ {a, b} }}', new ArrayExpression([
|
||||
$this->createConstantExpression('a', true),
|
||||
new ConstantExpression('a', 1),
|
||||
new NameExpression('a', 1),
|
||||
|
||||
$this->createConstantExpression('b', true),
|
||||
new ConstantExpression('b', 1),
|
||||
new NameExpression('b', 1),
|
||||
], 1)],
|
||||
|
||||
// array with spread operator
|
||||
['{{ [1, 2, ...foo] }}',
|
||||
new ArrayExpression([
|
||||
$this->createConstantExpression(0, false),
|
||||
$this->createConstantExpression(1),
|
||||
new ConstantExpression(0, 1),
|
||||
new ConstantExpression(1, 1),
|
||||
|
||||
$this->createConstantExpression(1, false),
|
||||
$this->createConstantExpression(2),
|
||||
new ConstantExpression(1, 1),
|
||||
new ConstantExpression(2, 1),
|
||||
|
||||
$this->createConstantExpression(2, false),
|
||||
new ConstantExpression(2, 1),
|
||||
$this->createNameExpression('foo', ['spread' => true]),
|
||||
], 1)],
|
||||
|
||||
// hash with spread operator
|
||||
['{{ {"a": "b", "b": "c", ...otherLetters} }}',
|
||||
new ArrayExpression([
|
||||
$this->createConstantExpression('a', true),
|
||||
$this->createConstantExpression('b'),
|
||||
new ConstantExpression('a', 1),
|
||||
new ConstantExpression('b', 1),
|
||||
|
||||
$this->createConstantExpression('b', true),
|
||||
$this->createConstantExpression('c'),
|
||||
new ConstantExpression('b', 1),
|
||||
new ConstantExpression('c', 1),
|
||||
|
||||
$this->createConstantExpression(0, false),
|
||||
new ConstantExpression(0, 1),
|
||||
$this->createNameExpression('otherLetters', ['spread' => true]),
|
||||
], 1)],
|
||||
];
|
||||
@@ -425,15 +423,4 @@ class ExpressionParserTest extends TestCase
|
||||
|
||||
return $expression;
|
||||
}
|
||||
|
||||
private function createConstantExpression($value, ?bool $indexSpecified = null)
|
||||
{
|
||||
$constant = new ConstantExpression($value, 1);
|
||||
|
||||
if (null !== $indexSpecified) {
|
||||
$constant->setAttribute('index_specified', $indexSpecified);
|
||||
}
|
||||
|
||||
return $constant;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user