diff --git a/conf/config.neon b/conf/config.neon index 937b9633cb..8f7006699e 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -245,6 +245,9 @@ parametersSchema: usedLevel: string() cliAutoloadFile: schema(string(), nullable()) + # internal - static reflection + singleReflectionFile: schema(string(), nullable()) + services: - class: PhpParser\BuilderFactory @@ -1154,6 +1157,7 @@ services: analysedPaths: %analysedPaths% composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths% analysedPathsFromConfig: %analysedPathsFromConfig% + singleReflectionFile: %singleReflectionFile% - implement: PHPStan\Reflection\BetterReflection\BetterReflectionProviderFactory diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index 0bfcaee8ee..9f2791437e 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -44,7 +44,8 @@ public static function begin( ?string $level, bool $allowXdebug, bool $manageMemoryLimitFile = true, - bool $debugEnabled = false + bool $debugEnabled = false, + ?string $singleReflectionFile = null ): InceptionResult { if (!$allowXdebug) { @@ -242,7 +243,7 @@ public static function begin( } try { - $container = $containerFactory->create($tmpDir, $additionalConfigFiles, $paths, $composerAutoloaderProjectPaths, $analysedPathsFromConfig, $allCustomConfigFiles, $level ?? self::DEFAULT_LEVEL, $generateBaselineFile, $autoloadFile); + $container = $containerFactory->create($tmpDir, $additionalConfigFiles, $paths, $composerAutoloaderProjectPaths, $analysedPathsFromConfig, $allCustomConfigFiles, $level ?? self::DEFAULT_LEVEL, $generateBaselineFile, $autoloadFile, $singleReflectionFile); } catch (\Nette\DI\InvalidConfigurationException | \Nette\Utils\AssertionException $e) { $errorOutput->writeLineFormatted('Invalid configuration:'); $errorOutput->writeLineFormatted($e->getMessage()); diff --git a/src/DependencyInjection/ContainerFactory.php b/src/DependencyInjection/ContainerFactory.php index 58fe7eeda2..597c813b72 100644 --- a/src/DependencyInjection/ContainerFactory.php +++ b/src/DependencyInjection/ContainerFactory.php @@ -46,6 +46,7 @@ public function __construct(string $currentWorkingDirectory) * @param string $usedLevel * @param string|null $generateBaselineFile * @param string|null $cliAutoloadFile + * @param string|null $singleReflectionFile * @return \PHPStan\DependencyInjection\Container */ public function create( @@ -57,7 +58,8 @@ public function create( array $allCustomConfigFiles = [], string $usedLevel = CommandHelper::DEFAULT_LEVEL, ?string $generateBaselineFile = null, - ?string $cliAutoloadFile = null + ?string $cliAutoloadFile = null, + ?string $singleReflectionFile = null ): Container { $configurator = new Configurator(new LoaderFactory( @@ -85,6 +87,9 @@ public function create( 'usedLevel' => $usedLevel, 'cliAutoloadFile' => $cliAutoloadFile, ]); + $configurator->addDynamicParameters([ + 'singleReflectionFile' => $singleReflectionFile, + ]); $configurator->addConfig($this->configDirectory . '/config.neon'); foreach ($additionalConfigFiles as $additionalConfigFile) { $configurator->addConfig($additionalConfigFile); diff --git a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php index e192922550..1dbe18062b 100644 --- a/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php +++ b/src/Reflection/BetterReflection/BetterReflectionSourceLocatorFactory.php @@ -67,6 +67,9 @@ class BetterReflectionSourceLocatorFactory /** @var string[] */ private $analysedPathsFromConfig; + /** @var string|null */ + private $singleReflectionFile; + /** * @param string[] $autoloadDirectories * @param string[] $autoloadFiles @@ -75,6 +78,7 @@ class BetterReflectionSourceLocatorFactory * @param string[] $analysedPaths * @param string[] $composerAutoloaderProjectPaths * @param string[] $analysedPathsFromConfig + * @param string|null $singleReflectionFile */ public function __construct( \PhpParser\Parser $parser, @@ -91,7 +95,8 @@ public function __construct( array $scanDirectories, array $analysedPaths, array $composerAutoloaderProjectPaths, - array $analysedPathsFromConfig + array $analysedPathsFromConfig, + ?string $singleReflectionFile ) { $this->parser = $parser; @@ -109,12 +114,17 @@ public function __construct( $this->analysedPaths = $analysedPaths; $this->composerAutoloaderProjectPaths = $composerAutoloaderProjectPaths; $this->analysedPathsFromConfig = $analysedPathsFromConfig; + $this->singleReflectionFile = $singleReflectionFile; } public function create(): SourceLocator { $locators = []; + if ($this->singleReflectionFile !== null) { + $locators[] = $this->optimizedSingleFileSourceLocatorRepository->getOrCreate($this->singleReflectionFile); + } + foreach ($this->composerAutoloaderProjectPaths as $composerAutoloaderProjectPath) { $locator = $this->composerJsonAndInstalledJsonSourceLocatorMaker->create($composerAutoloaderProjectPath); if ($locator === null) { diff --git a/src/Reflection/ReflectionProvider/ClassBlacklistReflectionProvider.php b/src/Reflection/ReflectionProvider/ClassBlacklistReflectionProvider.php index be449ea5d4..05c957f18e 100644 --- a/src/Reflection/ReflectionProvider/ClassBlacklistReflectionProvider.php +++ b/src/Reflection/ReflectionProvider/ClassBlacklistReflectionProvider.php @@ -8,6 +8,7 @@ use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\GlobalConstantReflection; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Reflection\ReflectionWithFilename; use Roave\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber; class ClassBlacklistReflectionProvider implements ReflectionProvider @@ -20,6 +21,8 @@ class ClassBlacklistReflectionProvider implements ReflectionProvider /** @var string[] */ private array $patterns; + private ?string $singleReflectionFile; + /** * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider * @param string[] $patterns @@ -27,12 +30,14 @@ class ClassBlacklistReflectionProvider implements ReflectionProvider public function __construct( ReflectionProvider $reflectionProvider, PhpStormStubsSourceStubber $phpStormStubsSourceStubber, - array $patterns + array $patterns, + ?string $singleReflectionFile ) { $this->reflectionProvider = $reflectionProvider; $this->phpStormStubsSourceStubber = $phpStormStubsSourceStubber; $this->patterns = $patterns; + $this->singleReflectionFile = $singleReflectionFile; } public function hasClass(string $className): bool @@ -60,6 +65,11 @@ public function hasClass(string $className): bool } $classReflection = $this->reflectionProvider->getClass($className); + if ($this->singleReflectionFile !== null) { + if ($classReflection->getFileName() === $this->singleReflectionFile) { + return false; + } + } if ($classReflection->isSubclassOf(\DateInterval::class) || $classReflection->isSubclassOf(\DatePeriod::class) || $classReflection->isSubclassOf(\DOMDocument::class) @@ -99,7 +109,21 @@ public function getAnonymousClassReflection(\PhpParser\Node\Stmt\Class_ $classNo public function hasFunction(\PhpParser\Node\Name $nameNode, ?Scope $scope): bool { - return $this->reflectionProvider->hasFunction($nameNode, $scope); + $has = $this->reflectionProvider->hasFunction($nameNode, $scope); + if (!$has) { + return false; + } + + if ($this->singleReflectionFile === null) { + return true; + } + + $functionReflection = $this->reflectionProvider->getFunction($nameNode, $scope); + if (!$functionReflection instanceof ReflectionWithFilename) { + return true; + } + + return $functionReflection->getFileName() !== $this->singleReflectionFile; } public function getFunction(\PhpParser\Node\Name $nameNode, ?Scope $scope): FunctionReflection diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index b8abfd072c..e6bb36ef89 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -233,7 +233,8 @@ private function createRuntimeReflectionProvider(ReflectionProvider $actualRefle '#^PhpParser\\\\#', '#^PHPStan\\\\#', '#^Hoa\\\\#', - ] + ], + null ); $this->setUpReflectionProvider( $actualReflectionProvider,