From cb8568949fde1554077e2d2872beec4fe6145449 Mon Sep 17 00:00:00 2001 From: "ondrej@mirtes.cz" Date: Fri, 6 Nov 2026 20:00:45 +0000 Subject: [PATCH] Fetch the exported nodes of non-analysed files only when the result cache is saved The full-analysis save callback fetched them before doSave() decided not to save at all - a run on files passed on the command line or in editor mode never saves - so every such run parsed each file the analysed code depends on for nothing. doSave() now receives them as a callback and calls it only once the cache is known to be saved. WordPress src/index.php, which requires four files: 0.98 s -> 0.70 s with --debug and PHPSTAN_RESTART=none. Amp-Thread-ID: https://ampcode.com/threads/T-01a1b250-30d4-72bb-8d3e-b0687619f435 --- src/Analyser/ResultCache/ResultCacheManager.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php index 48b27837989..0c803ff8ee4 100644 --- a/src/Analyser/ResultCache/ResultCacheManager.php +++ b/src/Analyser/ResultCache/ResultCacheManager.php @@ -1315,7 +1315,7 @@ public function process(AnalyserResult $analyserResult, ResultCache $resultCache } // Returns the collected data as it can be read back from the saved file, or null when nothing // was saved. - $doSave = function (array $errorsByFile, $locallyIgnoredErrorsByFile, $linesToIgnore, $unmatchedLineIgnores, LazyCollectedData $collectedData, ?array $dependencies, ?array $usedTraitDependencies, ?array $valueDependencies, ?array $packageDependencies, array $exportedNodes, CachedExportedNodes $cachedExportedNodes, array $projectExtensionFiles, FinalizerResult $finalizerResult) use ($internalErrors, $resultCache, $output, $onlyFiles, $meta): ?LazyCollectedData { + $doSave = function (array $errorsByFile, $locallyIgnoredErrorsByFile, $linesToIgnore, $unmatchedLineIgnores, LazyCollectedData $collectedData, ?array $dependencies, ?array $usedTraitDependencies, ?array $valueDependencies, ?array $packageDependencies, callable $getExportedNodes, CachedExportedNodes $cachedExportedNodes, array $projectExtensionFiles, FinalizerResult $finalizerResult) use ($internalErrors, $resultCache, $output, $onlyFiles, $meta): ?LazyCollectedData { if ($onlyFiles) { if ($output->isVeryVerbose()) { $output->writeLineFormatted('Result cache was not saved because only files were passed as analysed paths.'); @@ -1361,6 +1361,10 @@ public function process(AnalyserResult $analyserResult, ResultCache $resultCache return null; } + // fetching the nodes of the non-analysed files costs a parse of each of them, so it waits + // until the cache is known to be saved - a run on a few files or in editor mode never is + $exportedNodes = $getExportedNodes(); + // A parse error describes the analysed code, so the result cache is still saved. The file's // own result is not cached: its errors carry the exception, and it is re-analysed on every // run instead. @@ -1506,7 +1510,7 @@ public function process(AnalyserResult $analyserResult, ResultCache $resultCache $projectExtensionFiles = $this->getProjectExtensionFiles($projectConfigArray, $analyserResult->getDependencies()); } - return $doSave($freshErrorsByFile, $freshLocallyIgnoredErrorsByFile, $analyserResult->getLinesToIgnore(), $analyserResult->getUnmatchedLineIgnores(), $freshCollectedData, $analyserResult->getDependencies(), $analyserResult->getUsedTraitDependencies(), $analyserResult->getValueDependencies(), $analyserResult->getPackageDependencies(), $this->addNonAnalysedExportedNodes($analyserResult->getExportedNodes(), $analyserResult->getDependencies(), $analyserResult->getUsedTraitDependencies(), CachedExportedNodes::createEmpty()), CachedExportedNodes::createEmpty(), $projectExtensionFiles, $finalizerResult); + return $doSave($freshErrorsByFile, $freshLocallyIgnoredErrorsByFile, $analyserResult->getLinesToIgnore(), $analyserResult->getUnmatchedLineIgnores(), $freshCollectedData, $analyserResult->getDependencies(), $analyserResult->getUsedTraitDependencies(), $analyserResult->getValueDependencies(), $analyserResult->getPackageDependencies(), fn (): array => $this->addNonAnalysedExportedNodes($analyserResult->getExportedNodes(), $analyserResult->getDependencies(), $analyserResult->getUsedTraitDependencies(), CachedExportedNodes::createEmpty()), CachedExportedNodes::createEmpty(), $projectExtensionFiles, $finalizerResult); }); } @@ -1548,7 +1552,7 @@ public function process(AnalyserResult $analyserResult, ResultCache $resultCache $projectExtensionFiles[$file] = [$hash, true, $className]; } } - return $doSave($errorsByFile, $locallyIgnoredErrorsByFile, $linesToIgnore, $unmatchedLineIgnores, $collectedData, $dependencies, $usedTraitDependencies, $valueDependencies, $packageDependencies, $exportedNodes, $cachedExportedNodes, $projectExtensionFiles, $finalizerResult); + return $doSave($errorsByFile, $locallyIgnoredErrorsByFile, $linesToIgnore, $unmatchedLineIgnores, $collectedData, $dependencies, $usedTraitDependencies, $valueDependencies, $packageDependencies, static fn (): array => $exportedNodes, $cachedExportedNodes, $projectExtensionFiles, $finalizerResult); }; $flatErrors = [];