From 86d63c3b60d76871e943556c5db2e6f711fa8863 Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Thu, 11 Jun 2026 09:02:44 +0200 Subject: [PATCH] Use xxh128 instead of sha256 for file content hashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All hash_file() calls here are cache-invalidation keys, not security boundaries. xxh128 hashes a file in ~31 µs vs ~266 µs for sha256, which adds up across result-cache validation, source-locator scans, per-identifier reflection cache keys, and PHPDoc cache validation. Existing caches invalidate once (different hash values), then rebuild. Co-Authored-By: Claude Fable 5 --- src/Analyser/ResultCache/ResultCacheManager.php | 3 ++- src/Command/AnalyseApplication.php | 3 ++- src/DependencyInjection/Configurator.php | 5 +++-- src/File/FileMonitor.php | 3 ++- src/Internal/FileHashing.php | 17 +++++++++++++++++ .../OptimizedDirectorySourceLocator.php | 3 ++- .../OptimizedDirectorySourceLocatorFactory.php | 5 +++-- .../OptimizedSingleFileSourceLocator.php | 3 ++- src/Type/FileTypeMapper.php | 5 +++-- 9 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 src/Internal/FileHashing.php diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php index 0bc1e756e5..aff2094076 100644 --- a/src/Analyser/ResultCache/ResultCacheManager.php +++ b/src/Analyser/ResultCache/ResultCacheManager.php @@ -21,6 +21,7 @@ use PHPStan\File\FileWriter; use PHPStan\Internal\ArrayHelper; use PHPStan\Internal\ComposerHelper; +use PHPStan\Internal\FileHashing; use PHPStan\PhpDoc\StubFilesProvider; use PHPStan\ShouldNotHappenException; use ReflectionClass; @@ -1206,7 +1207,7 @@ private function getFileHash(string $path): string return $this->fileHashes[$path]; } - $hash = hash_file('sha256', $path); + $hash = hash_file(FileHashing::ALGORITHM, $path); if ($hash === false) { throw new CouldNotReadFileException($path); } diff --git a/src/Command/AnalyseApplication.php b/src/Command/AnalyseApplication.php index e78212f492..c88ddc0efe 100644 --- a/src/Command/AnalyseApplication.php +++ b/src/Command/AnalyseApplication.php @@ -11,6 +11,7 @@ use PHPStan\Collectors\CollectedData; use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Internal\BytesHelper; +use PHPStan\Internal\FileHashing; use PHPStan\PhpDoc\StubFilesProvider; use PHPStan\PhpDoc\StubValidator; use PHPStan\ShouldNotHappenException; @@ -160,7 +161,7 @@ public function analyse( continue; } - $newHash = hash_file('sha256', $file); + $newHash = hash_file(FileHashing::ALGORITHM, $file); if ($newHash === $hash) { continue; } diff --git a/src/DependencyInjection/Configurator.php b/src/DependencyInjection/Configurator.php index 39cade4fc9..45bdb5f72c 100644 --- a/src/DependencyInjection/Configurator.php +++ b/src/DependencyInjection/Configurator.php @@ -11,6 +11,7 @@ use PHPStan\File\CouldNotWriteFileException; use PHPStan\File\FileReader; use PHPStan\File\FileWriter; +use PHPStan\Internal\FileHashing; use PHPStan\Turbo\TurboExtensionEnabler; use function array_keys; use function count; @@ -106,7 +107,7 @@ public function loadContainer(): string array_keys($this->dynamicParameters), $this->configs, PHP_VERSION_ID - PHP_RELEASE_VERSION, - is_file($attributesPhp) ? hash_file('sha256', $attributesPhp) : 'attributes-missing', + is_file($attributesPhp) ? hash_file(FileHashing::ALGORITHM, $attributesPhp) : 'attributes-missing', NeonAdapter::CACHE_KEY, $this->getAllConfigFilesHashes(), var_export(TurboExtensionEnabler::isLoaded(), true), @@ -242,7 +243,7 @@ private function getAllConfigFilesHashes(): array { $hashes = []; foreach ($this->allConfigFiles as $file) { - $hash = hash_file('sha256', $file); + $hash = hash_file(FileHashing::ALGORITHM, $file); if ($hash === false) { throw new CouldNotReadFileException($file); diff --git a/src/File/FileMonitor.php b/src/File/FileMonitor.php index 0778fd0c64..8c70b79fa1 100644 --- a/src/File/FileMonitor.php +++ b/src/File/FileMonitor.php @@ -4,6 +4,7 @@ use PHPStan\DependencyInjection\AutowiredParameter; use PHPStan\DependencyInjection\AutowiredService; +use PHPStan\Internal\FileHashing; use PHPStan\ShouldNotHappenException; use function array_diff; use function array_key_exists; @@ -107,7 +108,7 @@ public function getChanges(): FileMonitorResult private function getFileHash(string $filePath): string { - $hash = hash_file('sha256', $filePath); + $hash = hash_file(FileHashing::ALGORITHM, $filePath); if ($hash === false) { throw new CouldNotReadFileException($filePath); diff --git a/src/Internal/FileHashing.php b/src/Internal/FileHashing.php new file mode 100644 index 0000000000..bc9009678c --- /dev/null +++ b/src/Internal/FileHashing.php @@ -0,0 +1,17 @@ += 80100 ? 'xxh128' : 'sha256'; + +} diff --git a/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php b/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php index f2b0f7e3a6..937df50eed 100644 --- a/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php +++ b/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocator.php @@ -17,6 +17,7 @@ use PHPStan\Cache\Cache; use PHPStan\File\CouldNotReadFileException; use PHPStan\Internal\ComposerHelper; +use PHPStan\Internal\FileHashing; use PHPStan\Php\PhpVersion; use PHPStan\Reflection\ConstantNameHelper; use PHPStan\ShouldNotHappenException; @@ -51,7 +52,7 @@ public function __construct( */ private function getCacheKeys(string $file, Identifier $identifier): array { - $fileHash = hash_file('sha256', $file); + $fileHash = hash_file(FileHashing::ALGORITHM, $file); if ($fileHash === false) { throw new CouldNotReadFileException($file); } diff --git a/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocatorFactory.php b/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocatorFactory.php index 48b1404ee9..6af03e4dd7 100644 --- a/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocatorFactory.php +++ b/src/Reflection/BetterReflection/SourceLocator/OptimizedDirectorySourceLocatorFactory.php @@ -6,6 +6,7 @@ use PHPStan\DependencyInjection\AutowiredParameter; use PHPStan\DependencyInjection\AutowiredService; use PHPStan\File\FileFinder; +use PHPStan\Internal\FileHashing; use PHPStan\Php\PhpVersion; use function array_key_exists; use function array_keys; @@ -32,7 +33,7 @@ public function createByDirectory(string $directory): OptimizedDirectorySourceLo $files = $this->fileFinder->findFiles([$directory])->getFiles(); $fileHashes = []; foreach ($files as $file) { - $hash = hash_file('sha256', $file); + $hash = hash_file(FileHashing::ALGORITHM, $file); if ($hash === false) { continue; } @@ -105,7 +106,7 @@ public function createByFiles(array $files, string $uniqueCacheIdentifier): Opti { $fileHashes = []; foreach ($files as $file) { - $hash = hash_file('sha256', $file); + $hash = hash_file(FileHashing::ALGORITHM, $file); if ($hash === false) { continue; } diff --git a/src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php b/src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php index f6680f016f..5bfe133cbd 100644 --- a/src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php +++ b/src/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocator.php @@ -18,6 +18,7 @@ use PHPStan\DependencyInjection\GenerateFactory; use PHPStan\File\CouldNotReadFileException; use PHPStan\Internal\ComposerHelper; +use PHPStan\Internal\FileHashing; use PHPStan\Php\PhpVersion; use PHPStan\Reflection\ConstantNameHelper; use PHPStan\ShouldNotHappenException; @@ -45,7 +46,7 @@ public function __construct( private function getVariableCacheKey(string $file): string { - $fileHash = hash_file('sha256', $file); + $fileHash = hash_file(FileHashing::ALGORITHM, $file); if ($fileHash === false) { throw new CouldNotReadFileException($file); } diff --git a/src/Type/FileTypeMapper.php b/src/Type/FileTypeMapper.php index 431954eb38..5ae8367d29 100644 --- a/src/Type/FileTypeMapper.php +++ b/src/Type/FileTypeMapper.php @@ -14,6 +14,7 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\File\FileHelper; use PHPStan\Internal\ComposerHelper; +use PHPStan\Internal\FileHashing; use PHPStan\Parser\Parser; use PHPStan\PhpDoc\NameScopeAlreadyBeingCreatedException; use PHPStan\PhpDoc\PhpDocNodeResolver; @@ -344,7 +345,7 @@ private function getNameScopeMap(string $fileName): array [$nameScopeMap, $files] = $this->createPhpDocNodeMap($fileName, null, null, [], $fileName); $filesWithHashes = []; foreach ($files as $file) { - $newHash = hash_file('sha256', $file); + $newHash = hash_file(FileHashing::ALGORITHM, $file); $filesWithHashes[$file] = $newHash; } $this->cache->save($cacheKey, $variableCacheKey, [$nameScopeMap, $filesWithHashes]); @@ -381,7 +382,7 @@ private function loadCachedPhpDocNodeMap(string $cacheKey, string $variableCache [$nameScopeMap, $filesWithHashes] = $cached; $useCache = true; foreach ($filesWithHashes as $file => $hash) { - $newHash = @hash_file('sha256', $file); + $newHash = @hash_file(FileHashing::ALGORITHM, $file); if ($newHash === false) { $useCache = false; break;