diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php index 2163eb16f1dc8..e78536794ede2 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php @@ -25,6 +25,7 @@ class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface { use FilesystemTrait { + prune as private doPrune; doClear as private doClearCache; doSave as private doSaveCache; } @@ -41,6 +42,48 @@ public function __construct(string $namespace = '', int $defaultLifetime = 0, st $this->init($namespace, $directory); } + public function prune(): bool + { + $ok = $this->doPrune(); + + set_error_handler(static function () {}); + $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + + try { + foreach ($this->scanHashDir($this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR) as $dir) { + $dir .= \DIRECTORY_SEPARATOR; + $keepDir = false; + for ($i = 0; $i < 38; ++$i) { + if (!is_dir($dir.$chars[$i])) { + continue; + } + for ($j = 0; $j < 38; ++$j) { + if (!is_dir($d = $dir.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) { + continue; + } + foreach (scandir($d, \SCANDIR_SORT_NONE) ?: [] as $link) { + if ('.' === $link || '..' === $link) { + continue; + } + if ('_' !== $dir[-2] && realpath($d.\DIRECTORY_SEPARATOR.$link)) { + $keepDir = true; + } else { + unlink($d.\DIRECTORY_SEPARATOR.$link); + } + } + $keepDir ?: rmdir($d); + } + $keepDir ?: rmdir($dir.$chars[$i]); + } + $keepDir ?: rmdir($dir); + } + } finally { + restore_error_handler(); + } + + return $ok; + } + protected function doClear(string $namespace): bool { $ok = $this->doClearCache($namespace); @@ -52,9 +95,11 @@ protected function doClear(string $namespace): bool set_error_handler(static function () {}); $chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $this->tmpSuffix ??= str_replace('/', '-', base64_encode(random_bytes(6))); + try { foreach ($this->scanHashDir($this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR) as $dir) { - if (rename($dir, $renamed = substr_replace($dir, bin2hex(random_bytes(4)), -8))) { + if (rename($dir, $renamed = substr_replace($dir, $this->tmpSuffix.'_', -9))) { $dir = $renamed.\DIRECTORY_SEPARATOR; } else { $dir .= \DIRECTORY_SEPARATOR; @@ -178,10 +223,12 @@ protected function doInvalidate(array $tagIds): bool continue; } + $this->tmpSuffix ??= str_replace('/', '-', base64_encode(random_bytes(6))); + set_error_handler(static function () {}); try { - if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -9))) { + if (rename($tagFolder, $renamed = substr_replace($tagFolder, $this->tmpSuffix.'_', -10))) { $tagFolder = $renamed.\DIRECTORY_SEPARATOR; } else { $renamed = null; diff --git a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php index 612631f176bc6..1182d0c095fc4 100644 --- a/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php +++ b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php @@ -21,7 +21,7 @@ trait FilesystemCommonTrait { private string $directory; - private string $tmp; + private string $tmpSuffix; private function init(string $namespace, ?string $directory) { @@ -86,27 +86,25 @@ private function write(string $file, string $data, int $expiresAt = null) { set_error_handler(__CLASS__.'::throwError'); try { - if (!isset($this->tmp)) { - $this->tmp = $this->directory.bin2hex(random_bytes(6)); - } + $tmp = $this->directory.$this->tmpSuffix ??= str_replace('/', '-', base64_encode(random_bytes(6))); try { - $h = fopen($this->tmp, 'x'); + $h = fopen($tmp, 'x'); } catch (\ErrorException $e) { if (!str_contains($e->getMessage(), 'File exists')) { throw $e; } - $this->tmp = $this->directory.bin2hex(random_bytes(6)); - $h = fopen($this->tmp, 'x'); + $tmp = $this->directory.$this->tmpSuffix = str_replace('/', '-', base64_encode(random_bytes(6))); + $h = fopen($tmp, 'x'); } fwrite($h, $data); fclose($h); if (null !== $expiresAt) { - touch($this->tmp, $expiresAt ?: time() + 31556952); // 1 year in seconds + touch($tmp, $expiresAt ?: time() + 31556952); // 1 year in seconds } - return rename($this->tmp, $file); + return rename($tmp, $file); } finally { restore_error_handler(); } @@ -180,8 +178,8 @@ public function __destruct() if (method_exists(parent::class, '__destruct')) { parent::__destruct(); } - if (isset($this->tmp) && is_file($this->tmp)) { - unlink($this->tmp); + if (isset($this->tmpSuffix) && is_file($this->directory.$this->tmpSuffix)) { + unlink($this->directory.$this->tmpSuffix); } } }