From 3cffd785fd482e5828aa2d09de09f8151caf16ae Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 1 May 2026 10:48:21 +0200 Subject: [PATCH 1/3] Lazily initialize AggregateSourceLocator --- .../BetterReflectionSourceLocatorFactory.php | 190 +++++++++++------- 1 file changed, 116 insertions(+), 74 deletions(-) diff --git a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php index ddffaffc899..b9fb3b71f14 100644 --- a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php +++ b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php @@ -4,6 +4,9 @@ use Phar; use PhpParser\Parser; +use PHPStan\BetterReflection\Identifier\Identifier; +use PHPStan\BetterReflection\Identifier\IdentifierType; +use PHPStan\BetterReflection\Reflector\Reflector; use PHPStan\BetterReflection\SourceLocator\Ast\Locator; use PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber; use PHPStan\BetterReflection\SourceLocator\SourceStubber\ReflectionSourceStubber; @@ -28,6 +31,7 @@ use PHPStan\Reflection\BetterReflection\SourceLocator\RewriteClassAliasSourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\SkipClassAliasSourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\SkipPolyfillSourceLocator; +use stdClass; use function array_merge; use function array_unique; use function count; @@ -80,95 +84,133 @@ public function __construct( public function create(): SourceLocator { - $locators = [ - $this->optimizedSingleFileSourceLocatorRepository->getOrCreate( - PHP_VERSION_ID < 80500 - ? __DIR__ . '/../../../stubs/runtime/Attribute84.php' - : __DIR__ . '/../../../stubs/runtime/Attribute85.php', - ), - ]; - - if ($this->singleReflectionFile !== null) { - $locators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($this->singleReflectionFile); - } - - $astLocator = new Locator($this->parser); - $locators[] = new AutoloadFunctionsSourceLocator( - new AutoloadSourceLocator($this->fileNodesFetcher, false), - new ReflectionClassSourceLocator( - $astLocator, - $this->reflectionSourceStubber, - ), - ); - - $analysedDirectories = []; - $analysedFiles = []; - - foreach (array_merge($this->analysedPaths, $this->analysedPathsFromConfig) as $analysedPath) { - if (is_file($analysedPath)) { - $analysedFiles[] = $analysedPath; - continue; + $initializer = function() { + $locators = [ + $this->optimizedSingleFileSourceLocatorRepository->getOrCreate( + PHP_VERSION_ID < 80500 + ? __DIR__ . '/../../../stubs/runtime/Attribute84.php' + : __DIR__ . '/../../../stubs/runtime/Attribute85.php', + ), + ]; + + if ($this->singleReflectionFile !== null) { + $locators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($this->singleReflectionFile); } - if (!is_dir($analysedPath)) { - continue; + $astLocator = new Locator($this->parser); + $locators[] = new AutoloadFunctionsSourceLocator( + new AutoloadSourceLocator($this->fileNodesFetcher, false), + new ReflectionClassSourceLocator( + $astLocator, + $this->reflectionSourceStubber, + ), + ); + + $analysedDirectories = []; + $analysedFiles = []; + + foreach (array_merge($this->analysedPaths, $this->analysedPathsFromConfig) as $analysedPath) { + if (is_file($analysedPath)) { + $analysedFiles[] = $analysedPath; + continue; + } + + if (!is_dir($analysedPath)) { + continue; + } + + $analysedDirectories[] = $analysedPath; } - $analysedDirectories[] = $analysedPath; - } + $fileLocators = []; + $analysedFiles = array_unique(array_merge($analysedFiles, $this->scanFiles)); + foreach ($analysedFiles as $analysedFile) { + $fileLocators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($analysedFile); + } - $fileLocators = []; - $analysedFiles = array_unique(array_merge($analysedFiles, $this->scanFiles)); - foreach ($analysedFiles as $analysedFile) { - $fileLocators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($analysedFile); - } + $directories = array_unique(array_merge($analysedDirectories, $this->scanDirectories)); + foreach ($directories as $directory) { + $fileLocators[] = $this->optimizedDirectorySourceLocatorRepository->getOrCreate($directory); + } - $directories = array_unique(array_merge($analysedDirectories, $this->scanDirectories)); - foreach ($directories as $directory) { - $fileLocators[] = $this->optimizedDirectorySourceLocatorRepository->getOrCreate($directory); - } + $astPhp8Locator = new Locator($this->php8Parser); - $astPhp8Locator = new Locator($this->php8Parser); + $composerLocators = []; - $composerLocators = []; + foreach ($this->composerAutoloaderProjectPaths as $composerAutoloaderProjectPath) { + $locator = $this->composerJsonAndInstalledJsonSourceLocatorMaker->create($composerAutoloaderProjectPath); + if ($locator === null) { + continue; + } + $composerLocators[] = $locator; + } - foreach ($this->composerAutoloaderProjectPaths as $composerAutoloaderProjectPath) { - $locator = $this->composerJsonAndInstalledJsonSourceLocatorMaker->create($composerAutoloaderProjectPath); - if ($locator === null) { - continue; + if (count($composerLocators) > 0) { + $fileLocators[] = new SkipPolyfillSourceLocator(new AggregateSourceLocator($composerLocators), $this->phpVersion); } - $composerLocators[] = $locator; - } - - if (count($composerLocators) > 0) { - $fileLocators[] = new SkipPolyfillSourceLocator(new AggregateSourceLocator($composerLocators), $this->phpVersion); - } - - if (extension_loaded('phar')) { - $pharProtocolPath = Phar::running(); - if ($pharProtocolPath !== '') { - $mappings = [ - 'PHPStan\\BetterReflection\\' => [$pharProtocolPath . '/vendor/ondrejmirtes/better-reflection/src/'], - ]; - if ($this->playgroundMode) { - $mappings['PHPStan\\'] = [$pharProtocolPath . '/src/']; - } else { - $mappings['PHPStan\\Testing\\'] = [$pharProtocolPath . '/src/Testing/']; + + if (extension_loaded('phar')) { + $pharProtocolPath = Phar::running(); + if ($pharProtocolPath !== '') { + $mappings = [ + 'PHPStan\\BetterReflection\\' => [$pharProtocolPath . '/vendor/ondrejmirtes/better-reflection/src/'], + ]; + if ($this->playgroundMode) { + $mappings['PHPStan\\'] = [$pharProtocolPath . '/src/']; + } else { + $mappings['PHPStan\\Testing\\'] = [$pharProtocolPath . '/src/Testing/']; + } + $fileLocators[] = $this->optimizedPsrAutoloaderLocatorFactory->create( + Psr4Mapping::fromArrayMappings($mappings), + ); } - $fileLocators[] = $this->optimizedPsrAutoloaderLocatorFactory->create( - Psr4Mapping::fromArrayMappings($mappings), - ); } - } - $locators[] = new RewriteClassAliasSourceLocator(new AggregateSourceLocator($fileLocators)); - $locators[] = new SkipClassAliasSourceLocator(new PhpInternalSourceLocator($astPhp8Locator, $this->phpstormStubsSourceStubber)); + $locators[] = new RewriteClassAliasSourceLocator(new AggregateSourceLocator($fileLocators)); + $locators[] = new SkipClassAliasSourceLocator(new PhpInternalSourceLocator($astPhp8Locator, $this->phpstormStubsSourceStubber)); + + $locators[] = new AutoloadSourceLocator($this->fileNodesFetcher, true); + $locators[] = new PhpVersionBlacklistSourceLocator(new PhpInternalSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); + $locators[] = new PhpVersionBlacklistSourceLocator(new EvaledCodeSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); + + return new AggregateSourceLocator($locators); + }; - $locators[] = new AutoloadSourceLocator($this->fileNodesFetcher, true); - $locators[] = new PhpVersionBlacklistSourceLocator(new PhpInternalSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); - $locators[] = new PhpVersionBlacklistSourceLocator(new EvaledCodeSourceLocator($astLocator, $this->reflectionSourceStubber), $this->phpstormStubsSourceStubber); + $lazyLocator = new class($initializer) implements SourceLocator { + private ?SourceLocator $wrappedSourceLocator = null; + + /** + * @var callable():SourceLocator + */ + private $initializer; + + /** + * @param callable():SourceLocator $initializer + */ + public function __construct(callable $initializer) + { + $this->initializer = $initializer; + } + + private function lazyInitialize(): SourceLocator { + if ($this->wrappedSourceLocator === null) { + $this->wrappedSourceLocator = ($this->initializer)(); + } + return $this->wrappedSourceLocator; + } + + public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?\PHPStan\BetterReflection\Reflection\Reflection + { + return $this->lazyInitialize()->locateIdentifier($reflector, $identifier); + } + + public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array + { + return $this->lazyInitialize()->locateIdentifiersByType($reflector, $identifierType); + } + }; - return new MemoizingSourceLocator(new AggregateSourceLocator($locators)); + return new MemoizingSourceLocator($lazyLocator); } } From 553a1a1db34df6f2cbff5798e2a33293d95b581a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 1 May 2026 10:54:35 +0200 Subject: [PATCH 2/3] extract class --- .../BetterReflectionSourceLocatorFactory.php | 43 ++---------------- .../SourceLocator/LazySourceLocator.php | 45 +++++++++++++++++++ 2 files changed, 48 insertions(+), 40 deletions(-) create mode 100644 src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php diff --git a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php index b9fb3b71f14..fa6b0e858b1 100644 --- a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php +++ b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php @@ -4,9 +4,6 @@ use Phar; use PhpParser\Parser; -use PHPStan\BetterReflection\Identifier\Identifier; -use PHPStan\BetterReflection\Identifier\IdentifierType; -use PHPStan\BetterReflection\Reflector\Reflector; use PHPStan\BetterReflection\SourceLocator\Ast\Locator; use PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber; use PHPStan\BetterReflection\SourceLocator\SourceStubber\ReflectionSourceStubber; @@ -23,6 +20,7 @@ use PHPStan\Reflection\BetterReflection\SourceLocator\AutoloadSourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\ComposerJsonAndInstalledJsonSourceLocatorMaker; use PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher; +use PHPStan\Reflection\BetterReflection\SourceLocator\LazySourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedDirectorySourceLocatorRepository; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedPsrAutoloaderLocatorFactory; use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocatorRepository; @@ -31,7 +29,6 @@ use PHPStan\Reflection\BetterReflection\SourceLocator\RewriteClassAliasSourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\SkipClassAliasSourceLocator; use PHPStan\Reflection\BetterReflection\SourceLocator\SkipPolyfillSourceLocator; -use stdClass; use function array_merge; use function array_unique; use function count; @@ -84,7 +81,7 @@ public function __construct( public function create(): SourceLocator { - $initializer = function() { + $initializer = function () { $locators = [ $this->optimizedSingleFileSourceLocatorRepository->getOrCreate( PHP_VERSION_ID < 80500 @@ -176,41 +173,7 @@ public function create(): SourceLocator return new AggregateSourceLocator($locators); }; - $lazyLocator = new class($initializer) implements SourceLocator { - private ?SourceLocator $wrappedSourceLocator = null; - - /** - * @var callable():SourceLocator - */ - private $initializer; - - /** - * @param callable():SourceLocator $initializer - */ - public function __construct(callable $initializer) - { - $this->initializer = $initializer; - } - - private function lazyInitialize(): SourceLocator { - if ($this->wrappedSourceLocator === null) { - $this->wrappedSourceLocator = ($this->initializer)(); - } - return $this->wrappedSourceLocator; - } - - public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?\PHPStan\BetterReflection\Reflection\Reflection - { - return $this->lazyInitialize()->locateIdentifier($reflector, $identifier); - } - - public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array - { - return $this->lazyInitialize()->locateIdentifiersByType($reflector, $identifierType); - } - }; - - return new MemoizingSourceLocator($lazyLocator); + return new MemoizingSourceLocator(new LazySourceLocator($initializer)); } } diff --git a/src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php b/src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php new file mode 100644 index 00000000000..b1e9fb9e74d --- /dev/null +++ b/src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php @@ -0,0 +1,45 @@ +initializer = $initializer; + } + + private function lazyInitialize(): SourceLocator + { + if ($this->wrappedSourceLocator === null) { + $this->wrappedSourceLocator = ($this->initializer)(); + } + return $this->wrappedSourceLocator; + } + + public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection + { + return $this->lazyInitialize()->locateIdentifier($reflector, $identifier); + } + + public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array + { + return $this->lazyInitialize()->locateIdentifiersByType($reflector, $identifierType); + } + +} From 386d3614dd1e54e891a351d7a272556622d24453 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 1 May 2026 10:57:47 +0200 Subject: [PATCH 3/3] cs --- .../BetterReflection/SourceLocator/LazySourceLocator.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php b/src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php index b1e9fb9e74d..e605b4d06cd 100644 --- a/src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php +++ b/src/Reflection/BetterReflection/SourceLocator/LazySourceLocator.php @@ -2,6 +2,7 @@ namespace PHPStan\Reflection\BetterReflection\SourceLocator; +use Override; use PHPStan\BetterReflection\Identifier\Identifier; use PHPStan\BetterReflection\Identifier\IdentifierType; use PHPStan\BetterReflection\Reflection\Reflection; @@ -26,17 +27,16 @@ public function __construct(callable $initializer) private function lazyInitialize(): SourceLocator { - if ($this->wrappedSourceLocator === null) { - $this->wrappedSourceLocator = ($this->initializer)(); - } - return $this->wrappedSourceLocator; + return $this->wrappedSourceLocator ??= ($this->initializer)(); } + #[Override] public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection { return $this->lazyInitialize()->locateIdentifier($reflector, $identifier); } + #[Override] public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array { return $this->lazyInitialize()->locateIdentifiersByType($reflector, $identifierType);