From 4bde072567e33a2311b5513fa2234218842f3098 Mon Sep 17 00:00:00 2001 From: Greg Korba Date: Fri, 3 Jan 2025 13:42:51 +0100 Subject: [PATCH 1/5] Initial implementation of `ResultCacheMetaExtension` This introduces contract for extending the way how result cache metadata is calculated, so PHPStan extensions can take part into deciding whether analysis should be re-run or its result can be re-use from cache. --- .../ResultCache/ResultCacheManager.php | 31 +++++++++++++++ .../ResultCache/ResultCacheMetaExtension.php | 39 +++++++++++++++++++ .../ConditionalTagsExtension.php | 2 + 3 files changed, 72 insertions(+) create mode 100644 src/Analyser/ResultCache/ResultCacheMetaExtension.php diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php index 567b61f798..692c02fe33 100644 --- a/src/Analyser/ResultCache/ResultCacheManager.php +++ b/src/Analyser/ResultCache/ResultCacheManager.php @@ -10,6 +10,7 @@ use PHPStan\Command\Output; use PHPStan\Dependency\ExportedNodeFetcher; use PHPStan\Dependency\RootExportedNode; +use PHPStan\DependencyInjection\Container; use PHPStan\DependencyInjection\ProjectConfigHelper; use PHPStan\File\CouldNotReadFileException; use PHPStan\File\FileFinder; @@ -66,6 +67,7 @@ final class ResultCacheManager * @param string[] $scanDirectories */ public function __construct( + private Container $container, private ExportedNodeFetcher $exportedNodeFetcher, private FileFinder $scanFileFinder, private ReflectionProvider $reflectionProvider, @@ -317,6 +319,11 @@ private function isMetaDifferent(array $cachedMeta, array $currentMeta): bool $currentMeta['projectConfig'] = Neon::encode($currentMeta['projectConfig']); } + // Do not invalidate result cache generated before introducing support for result cache meta extensions + if (!isset($currentMeta['phpstanExtensions'])) { + $currentMeta['phpstanExtensions'] = []; + } + return $cachedMeta !== $currentMeta; } @@ -904,6 +911,7 @@ private function getMeta(array $allAnalysedFiles, ?array $projectConfigArray): a return [ 'cacheVersion' => self::CACHE_VERSION, 'phpstanVersion' => ComposerHelper::getPhpStanVersion(), + 'phpstanExtensions' => $this->getMetaFromPhpStanExtensions(), 'phpVersion' => PHP_VERSION_ID, 'projectConfig' => $projectConfigArray, 'analysedPaths' => $this->analysedPaths, @@ -1036,4 +1044,27 @@ private function getStubFiles(): array return $stubFiles; } + /** + * @return array + * @throws ShouldNotHappenException + */ + private function getMetaFromPhpStanExtensions(): array + { + $meta = []; + + /** @var ResultCacheMetaExtension $extension */ + foreach ($this->container->getServicesByTag(ResultCacheMetaExtension::EXTENSION_TAG) as $extension) { + if (array_key_exists($extension->getKey(), $meta)) { + throw new ShouldNotHappenException(sprintf( + 'Duplicate ResultCacheMetaExtension with key "%s" found.', + $extension->getKey(), + )); + } + + $meta[$extension->getKey()] = $extension->getHash(); + } + + return $meta; + } + } diff --git a/src/Analyser/ResultCache/ResultCacheMetaExtension.php b/src/Analyser/ResultCache/ResultCacheMetaExtension.php new file mode 100644 index 0000000000..11aac2512f --- /dev/null +++ b/src/Analyser/ResultCache/ResultCacheMetaExtension.php @@ -0,0 +1,39 @@ + $bool, LazyParameterOutTypeExtensionProvider::STATIC_METHOD_TAG => $bool, DiagnoseExtension::EXTENSION_TAG => $bool, + ResultCacheMetaExtension::EXTENSION_TAG => $bool, ])->min(1)); } From 297586dd76ef9f80ce9fc8ae57558e20d10bb4f4 Mon Sep 17 00:00:00 2001 From: Greg Korba Date: Sat, 4 Jan 2025 12:14:38 +0100 Subject: [PATCH 2/5] Remove unnecessary backward compatibility Did not think that new version of PHPStan invalidates result cache anyway. --- src/Analyser/ResultCache/ResultCacheManager.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php index 692c02fe33..248dd2a913 100644 --- a/src/Analyser/ResultCache/ResultCacheManager.php +++ b/src/Analyser/ResultCache/ResultCacheManager.php @@ -319,11 +319,6 @@ private function isMetaDifferent(array $cachedMeta, array $currentMeta): bool $currentMeta['projectConfig'] = Neon::encode($currentMeta['projectConfig']); } - // Do not invalidate result cache generated before introducing support for result cache meta extensions - if (!isset($currentMeta['phpstanExtensions'])) { - $currentMeta['phpstanExtensions'] = []; - } - return $cachedMeta !== $currentMeta; } From 587a09cb0a6c7d39f9c7fa1173ca029e20f6e5f0 Mon Sep 17 00:00:00 2001 From: Greg Korba Date: Sat, 4 Jan 2025 12:15:06 +0100 Subject: [PATCH 3/5] Change the meta key for extensions' data --- src/Analyser/ResultCache/ResultCacheManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php index 248dd2a913..998c4dfbe6 100644 --- a/src/Analyser/ResultCache/ResultCacheManager.php +++ b/src/Analyser/ResultCache/ResultCacheManager.php @@ -906,7 +906,7 @@ private function getMeta(array $allAnalysedFiles, ?array $projectConfigArray): a return [ 'cacheVersion' => self::CACHE_VERSION, 'phpstanVersion' => ComposerHelper::getPhpStanVersion(), - 'phpstanExtensions' => $this->getMetaFromPhpStanExtensions(), + 'metaExtensions' => $this->getMetaFromPhpStanExtensions(), 'phpVersion' => PHP_VERSION_ID, 'projectConfig' => $projectConfigArray, 'analysedPaths' => $this->analysedPaths, From 9fea1e117556b439069c4601abdc05daf4de3cd4 Mon Sep 17 00:00:00 2001 From: Greg Korba Date: Sat, 4 Jan 2025 12:15:42 +0100 Subject: [PATCH 4/5] Sort extensions' metadata by key --- src/Analyser/ResultCache/ResultCacheManager.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php index 998c4dfbe6..7e997503a3 100644 --- a/src/Analyser/ResultCache/ResultCacheManager.php +++ b/src/Analyser/ResultCache/ResultCacheManager.php @@ -1059,6 +1059,8 @@ private function getMetaFromPhpStanExtensions(): array $meta[$extension->getKey()] = $extension->getHash(); } + ksort($meta); + return $meta; } From 041f6d0f3400fc3741bd918865771d5bf8a1af92 Mon Sep 17 00:00:00 2001 From: Greg Korba Date: Tue, 7 Jan 2025 12:04:12 +0100 Subject: [PATCH 5/5] Add e2e test --- .github/workflows/e2e-tests.yml | 9 ++++++++ composer.json | 3 +++ e2e/result-cache-meta-extension/hash.txt | 1 + e2e/result-cache-meta-extension/phpstan.neon | 10 +++++++++ .../src/DummyResultCacheMetaExtension.php | 21 +++++++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 e2e/result-cache-meta-extension/hash.txt create mode 100644 e2e/result-cache-meta-extension/phpstan.neon create mode 100644 e2e/result-cache-meta-extension/src/DummyResultCacheMetaExtension.php diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 872d536314..5b77182b86 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -233,6 +233,15 @@ jobs: cd e2e/bug-11857 composer install ../../bin/phpstan + - script: | + cd e2e/result-cache-meta-extension + ../../bin/phpstan -vvv + ../../bin/phpstan -vvv --fail-without-result-cache + echo 'modified-hash' > hash.txt + OUTPUT=$(../bashunit -a exit_code "2" "../../bin/phpstan -vvv --fail-without-result-cache") + echo "$OUTPUT" + ../bashunit -a matches "Note: Using configuration file .+phpstan.neon." "$OUTPUT" + ../bashunit -a contains 'Result cache not used because the metadata do not match: metaExtensions' "$OUTPUT" steps: - name: "Checkout" diff --git a/composer.json b/composer.json index 6edf082303..4a524d5ff7 100644 --- a/composer.json +++ b/composer.json @@ -140,6 +140,9 @@ "classmap": [ "tests/e2e", "tests/PHPStan" + ], + "files": [ + "e2e/result-cache-meta-extension/src/DummyResultCacheMetaExtension.php" ] }, "repositories": [ diff --git a/e2e/result-cache-meta-extension/hash.txt b/e2e/result-cache-meta-extension/hash.txt new file mode 100644 index 0000000000..1f34c8dfd2 --- /dev/null +++ b/e2e/result-cache-meta-extension/hash.txt @@ -0,0 +1 @@ +initial-hash diff --git a/e2e/result-cache-meta-extension/phpstan.neon b/e2e/result-cache-meta-extension/phpstan.neon new file mode 100644 index 0000000000..f2f9c41148 --- /dev/null +++ b/e2e/result-cache-meta-extension/phpstan.neon @@ -0,0 +1,10 @@ +parameters: + level: 8 + paths: + - src + +services: + - + class: ResultCacheE2E\MetaExtension\DummyResultCacheMetaExtension + tags: + - phpstan.resultCacheMetaExtension diff --git a/e2e/result-cache-meta-extension/src/DummyResultCacheMetaExtension.php b/e2e/result-cache-meta-extension/src/DummyResultCacheMetaExtension.php new file mode 100644 index 0000000000..81b6332f96 --- /dev/null +++ b/e2e/result-cache-meta-extension/src/DummyResultCacheMetaExtension.php @@ -0,0 +1,21 @@ +