Skip to content

Commit c6d056d

Browse files
committed
Fix: prevent "Cannot traverse an already closed generator" error by materializing Traversable input
1 parent 368bfb7 commit c6d056d

File tree

2 files changed

+18
-0
lines changed

2 files changed

+18
-0
lines changed

src/Symfony/Component/Serializer/Encoder/CsvEncoder.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ public function encode(mixed $data, string $format, array $context = []): string
6565
} elseif (empty($data)) {
6666
$data = [[]];
6767
} else {
68+
// TODO: Remove this check once PHP 8.2+ is the minimum supported version.
69+
// In PHP 8.2, iterator_to_array() accepts iterable, so this instanceof check becomes unnecessary.
70+
if ($data instanceof \Traversable) {
71+
// Generators can only be iterated once — convert to array to allow multiple traversals
72+
$data = iterator_to_array($data);
73+
}
6874
// Sequential arrays of arrays are considered as collections
6975
$i = 0;
7076
foreach ($data as $key => $value) {

src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,4 +710,16 @@ public function testEndOfLinePassedInConstructor()
710710
$encoder = new CsvEncoder([CsvEncoder::END_OF_LINE => "\r\n"]);
711711
$this->assertSame("foo,bar\r\nhello,test\r\n", $encoder->encode($value, 'csv'));
712712
}
713+
714+
public function testEncodeWithGenerator()
715+
{
716+
$this->assertSame("0\n1\n2\n3\n4\n5\n", $this->encoder->encode($this->getLineIterator(), CsvEncoder::FORMAT));
717+
}
718+
719+
private function getLineIterator(): \Generator
720+
{
721+
for ($i = 1; $i <= 5; ++$i) {
722+
yield [(string) $i];
723+
}
724+
}
713725
}

0 commit comments

Comments
 (0)