diff --git a/src/ReaderTest.php b/src/ReaderTest.php index 49b36268..e539d82a 100644 --- a/src/ReaderTest.php +++ b/src/ReaderTest.php @@ -14,6 +14,7 @@ namespace League\Csv; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\Attributes\Group; use SplFileObject; use SplTempFileObject; @@ -637,4 +638,18 @@ public function testHeaderMapperFailsWithInvalidMapper(): void Reader::createFromString($csv) ->getRecords(['Annee' => 'Year', 'Prenom' => 'Firstname', 'Nombre' => 'Count']); } + + #[DoesNotPerformAssertions] + public function testStreamWithFiltersDestructsGracefully(): void + { + /** @var resource $fp */ + $fp = fopen('php://temp', 'r+'); + fputcsv($fp, ['abc', '123'], escape: ''); + + $csv = Reader::createFromStream($fp); + $csv->addStreamFilter('convert.iconv.UTF-8/UTF-16'); + + // An explicitly closed file handle makes the stream filter resources invalid + fclose($fp); + } } diff --git a/src/Stream.php b/src/Stream.php index a870669d..674c9e17 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -81,7 +81,11 @@ private function __construct($stream) public function __destruct() { - array_walk_recursive($this->filters, fn ($filter): bool => @stream_filter_remove($filter)); + array_walk_recursive($this->filters, static function ($filter): void { + if (is_resource($filter)) { + @stream_filter_remove($filter); + } + }); if ($this->should_close_stream) { set_error_handler(fn (int $errno, string $errstr, string $errfile, int $errline) => true);