From 11a208eab4d0083b3a48f8db0b1b0c1485553c29 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 5 Sep 2026 14:13:28 +0200 Subject: [PATCH] [Testing] Use fresh single-file source locator under PHPUnit to avoid stale reflection Fixture files are written to throwaway temp paths and deleted in tearDown. The shared OptimizedSingleFileSourceLocatorRepository caches locators by path for the whole worker and never evicts them, so a stale locator for a since-deleted file can leak into a later test running in the same parallel worker. Depending on how fastunit groups tests into worker chunks, this surfaces as an order-dependent reflection failure (e.g. a parent property becoming invisible), reproducible with a low worker count such as -p 4. Build a fresh locator per run under PHPUnit; keep the cached repository in production where files are stable. --- .../DynamicSourceLocatorProvider.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php index f5fc2e9dd12..8e5275cbb9e 100644 --- a/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php +++ b/src/NodeTypeResolver/Reflection/BetterReflection/SourceLocatorProvider/DynamicSourceLocatorProvider.php @@ -7,6 +7,7 @@ use PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator; use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedDirectorySourceLocatorFactory; +use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocatorFactory; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocatorRepository; use Rector\Contract\DependencyInjection\ResettableInterface; use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment; @@ -30,7 +31,8 @@ final class DynamicSourceLocatorProvider implements ResettableInterface public function __construct( private readonly OptimizedDirectorySourceLocatorFactory $optimizedDirectorySourceLocatorFactory, - private readonly OptimizedSingleFileSourceLocatorRepository $optimizedSingleFileSourceLocatorRepository + private readonly OptimizedSingleFileSourceLocatorRepository $optimizedSingleFileSourceLocatorRepository, + private readonly OptimizedSingleFileSourceLocatorFactory $optimizedSingleFileSourceLocatorFactory ) { } @@ -67,7 +69,12 @@ public function provide(): SourceLocator $sourceLocators = []; foreach ($this->filePaths as $file) { - $sourceLocators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($file); + // under PHPUnit each fixture is a throwaway temp file that is deleted after the test; + // the shared repository caches locators by path forever, so a stale locator for a since-deleted + // file can leak into a later test. build a fresh locator per run there, keep caching in production + $sourceLocators[] = $isPHPUnitRun + ? $this->optimizedSingleFileSourceLocatorFactory->create($file) + : $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($file); } foreach ($this->directories as $directory) {