From 9de1b3db98c03f8d0fc7a77656aa420ab649e611 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sat, 22 Aug 2026 12:36:24 -0400 Subject: [PATCH] Fix array destructuring from a Traversable Sequence destructuring compiled its right-hand side straight into array_pad(), which throws "Argument #1 must be of type array" when a Traversable is provided: {% do [a, b] = items %} with an IteratorAggregate/Generator for items crashed with a TypeError. Coerce Traversables via iterator_to_array() before padding, matching the behavior of the spread, merge, and slice operations which already accept Traversables. --- .../Binary/SequenceDestructuringSetBinary.php | 10 +++++++++- tests/Fixtures/expressions/set.test | 9 ++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Node/Expression/Binary/SequenceDestructuringSetBinary.php b/src/Node/Expression/Binary/SequenceDestructuringSetBinary.php index 394e7c053..0667b4645 100644 --- a/src/Node/Expression/Binary/SequenceDestructuringSetBinary.php +++ b/src/Node/Expression/Binary/SequenceDestructuringSetBinary.php @@ -57,7 +57,15 @@ class SequenceDestructuringSetBinary extends AbstractBinary $compiler->raw('$context[')->repr($name)->raw(']'); } } - $compiler->raw('] = array_pad(')->subcompile($this->getNode('right'))->raw(', ')->repr(\count($this->variables))->raw(', null)'); + $compiler->raw('] = array_pad(('); + $var = '$'.$compiler->getVarName(); + $compiler + ->raw($var.' = ') + ->subcompile($this->getNode('right')) + ->raw(') instanceof \Traversable ? iterator_to_array('.$var.') : '.$var) + ->raw(', ') + ->repr(\count($this->variables)) + ->raw(', null)'); } public function operator(Compiler $compiler): Compiler diff --git a/tests/Fixtures/expressions/set.test b/tests/Fixtures/expressions/set.test index 439e1648f..cb54d24d4 100644 --- a/tests/Fixtures/expressions/set.test +++ b/tests/Fixtures/expressions/set.test @@ -22,6 +22,9 @@ Twig supports the "=" operator (assignment) {% do {first_name, last_name} = user_map %}{{ first_name }} {{ last_name }} {% do {name} = user_obj %}{{ name }} +# Array destructuring from a Traversable +{% do [p, q] = pair_traversable %}{{ p }} {{ q }} + # Object destructuring with renaming {% do {name: userName, email: userEmail} = user %}{{ userName }} {{ userEmail }} {% do {first_name: first, last_name: last} = user_map %}{{ first }} {{ last }} @@ -34,7 +37,8 @@ return [ 'user_obj' => new class { public function getName() { return 'Fabien'; } }, - 'null_obj' => null + 'null_obj' => null, + 'pair_traversable' => new ArrayIterator(['Fabien', 'Potencier']), ] --EXPECT-- 4 @@ -54,6 +58,9 @@ Fabien fabien@example.com Fabien Potencier Fabien +# Array destructuring from a Traversable +Fabien Potencier + # Object destructuring with renaming Fabien fabien@example.com Fabien Potencier