From 466ad51740d629c9137a77dac28a676b71ef7197 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 8 Oct 2024 15:13:38 +0200 Subject: [PATCH] Clean file cache from unused items --- src/Cache/FileCacheStorage.php | 110 +++++++++++++++++++++++++++++++++ src/Command/CommandHelper.php | 5 ++ 2 files changed, 115 insertions(+) diff --git a/src/Cache/FileCacheStorage.php b/src/Cache/FileCacheStorage.php index 5ba76a768a..8337b859ec 100644 --- a/src/Cache/FileCacheStorage.php +++ b/src/Cache/FileCacheStorage.php @@ -4,16 +4,32 @@ use InvalidArgumentException; use Nette\Utils\Random; +use PHPStan\File\CouldNotReadFileException; +use PHPStan\File\CouldNotWriteFileException; +use PHPStan\File\FileReader; use PHPStan\File\FileWriter; use PHPStan\Internal\DirectoryCreator; use PHPStan\Internal\DirectoryCreatorException; use PHPStan\ShouldNotHappenException; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use function array_keys; +use function closedir; +use function dirname; use function error_get_last; +use function is_dir; use function is_file; +use function opendir; +use function readdir; use function rename; +use function rmdir; use function sha1; use function sprintf; +use function str_contains; +use function str_starts_with; +use function strlen; use function substr; +use function uksort; use function unlink; use function var_export; use const DIRECTORY_SEPARATOR; @@ -21,6 +37,8 @@ final class FileCacheStorage implements CacheStorage { + private const CACHED_CLEARED_VERSION = 'v1-variadic'; + public function __construct(private string $directory) { } @@ -100,4 +118,96 @@ private function getFilePaths(string $key): array ]; } + public function clearUnusedFiles(): void + { + if (!is_dir($this->directory)) { + return; + } + + $cachedClearedFile = $this->directory . '/cache-cleared'; + if (is_file($cachedClearedFile)) { + try { + $cachedClearedContents = FileReader::read($cachedClearedFile); + if ($cachedClearedContents === self::CACHED_CLEARED_VERSION) { + return; + } + } catch (CouldNotReadFileException) { + return; + } + } + + $iterator = new RecursiveDirectoryIterator($this->directory); + $iterator->setFlags(RecursiveDirectoryIterator::SKIP_DOTS); + $files = new RecursiveIteratorIterator($iterator); + $beginFunction = sprintf( + "getPathname(); + $contents = FileReader::read($path); + if (str_contains($contents, 'odsl-')) { + continue; + } + if ( + !str_starts_with($contents, $beginFunction) + && !str_starts_with($contents, $beginMethod) + && !str_starts_with($contents, $beginOld) + ) { + continue; + } + + $emptyDirectoriesToCheck[dirname($path)] = true; + $emptyDirectoriesToCheck[dirname($path, 2)] = true; + + @unlink($path); + } catch (CouldNotReadFileException) { + continue; + } + } + + uksort($emptyDirectoriesToCheck, static fn ($a, $b) => strlen($b) - strlen($a)); + + foreach (array_keys($emptyDirectoriesToCheck) as $directory) { + if (!$this->isDirectoryEmpty($directory)) { + continue; + } + + @rmdir($directory); + } + + try { + FileWriter::write($cachedClearedFile, self::CACHED_CLEARED_VERSION); + } catch (CouldNotWriteFileException) { + // pass + } + } + + private function isDirectoryEmpty(string $directory): bool + { + $handle = opendir($directory); + if ($handle === false) { + return false; + } + while (($entry = readdir($handle)) !== false) { + if ($entry !== '.' && $entry !== '..') { + closedir($handle); + return false; + } + } + + closedir($handle); + return true; + } + } diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index 7c81447cd8..65b059073e 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -12,6 +12,7 @@ use Nette\Schema\ValidationException; use Nette\Utils\AssertionException; use Nette\Utils\Strings; +use PHPStan\Cache\FileCacheStorage; use PHPStan\Command\Symfony\SymfonyOutput; use PHPStan\Command\Symfony\SymfonyStyle; use PHPStan\DependencyInjection\Container; @@ -434,6 +435,10 @@ public static function begin( if ($cleanupContainerCache) { $containerFactory->clearOldContainers($tmpDir); + $cacheStorage = $container->getService('cacheStorage'); + if ($cacheStorage instanceof FileCacheStorage) { + $cacheStorage->clearUnusedFiles(); + } } /** @var bool|null $customRulesetUsed */