From 590fa30a50426b1af2d031720ea9c61fab96dfa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Tue, 8 Sep 2026 22:09:03 +0200 Subject: [PATCH] Guard iterator_to_array() against being declared twice src/iterator_to_array.php is loaded eagerly through composer's "autoload.files", but it also sits inside the directory "autoload.psr-4" maps Ibexa\PolyfillPhp82\ to. Anything that resolves "Ibexa\PolyfillPhp82\iterator_to_array" as a *class* name therefore makes the autoloader include the file a second time, and without a guard that's fatal: PHP Fatal error: Cannot redeclare Ibexa\PolyfillPhp82\iterator_to_array() (previously declared in .../src/iterator_to_array.php:18) Static analysers probing an imported function are the common trigger. PHPStan hits it in any package that writes `use function Ibexa\PolyfillPhp82\iterator_to_array;` -- it dies before reporting anything, so the analysis job fails with an error that has nothing to do with the code under test. ibexa/test-core is currently red on PHP 7.4 for exactly this reason. Wrapping the declaration in function_exists() makes the second include a harmless no-op, which is how symfony/polyfill handles its own function files. Added a regression test that requires the file a second time; it reproduces the fatal above without this change. Leaving the autoload.files / autoload.psr-4 overlap in place -- narrowing PSR-4 away from this file would work too, but the guard is the smaller change and protects against any other path that ends up including it twice. --- src/iterator_to_array.php | 36 +++++++++++++++++++----------------- tests/Php82Test.php | 7 +++++++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/iterator_to_array.php b/src/iterator_to_array.php index 48223cf..191975b 100644 --- a/src/iterator_to_array.php +++ b/src/iterator_to_array.php @@ -8,23 +8,25 @@ namespace Ibexa\PolyfillPhp82; -/** - * @template T - * - * @param iterable $iterator - * - * @return array - */ -function iterator_to_array(iterable $iterator, bool $preserve_keys = true): array -{ - if (\PHP_VERSION_ID > 8_02_00) { - /** @var \Traversable $iterator */ - return \iterator_to_array($iterator, $preserve_keys); - } +if (!function_exists(__NAMESPACE__ . '\\iterator_to_array')) { + /** + * @template T + * + * @param iterable $iterator + * + * @return array + */ + function iterator_to_array(iterable $iterator, bool $preserve_keys = true): array + { + if (\PHP_VERSION_ID > 8_02_00) { + /** @var \Traversable $iterator */ + return \iterator_to_array($iterator, $preserve_keys); + } - if ($iterator instanceof \Traversable) { - return \iterator_to_array($iterator, $preserve_keys); - } + if ($iterator instanceof \Traversable) { + return \iterator_to_array($iterator, $preserve_keys); + } - return $iterator; + return $iterator; + } } diff --git a/tests/Php82Test.php b/tests/Php82Test.php index 8bc961a..a1130f4 100644 --- a/tests/Php82Test.php +++ b/tests/Php82Test.php @@ -28,6 +28,13 @@ public function testGeneratorForIteratorToArray(): void ], $result); } + public function testDeclarationFileCanBeLoadedTwice(): void + { + require dirname(__DIR__) . '/src/iterator_to_array.php'; + + self::assertTrue(function_exists('Ibexa\PolyfillPhp82\iterator_to_array')); + } + public function testArrayForIteratorToArray(): void { $result = iterator_to_array([