vendor/symfony/cache/Traits/FilesystemCommonTrait.php line 120

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Cache\Traits;
  11. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  12. /**
  13.  * @author Nicolas Grekas <p@tchwork.com>
  14.  *
  15.  * @internal
  16.  */
  17. trait FilesystemCommonTrait
  18. {
  19.     private string $directory;
  20.     private string $tmpSuffix;
  21.     private function init(string $namespace, ?string $directory)
  22.     {
  23.         if (!isset($directory[0])) {
  24.             $directory sys_get_temp_dir().\DIRECTORY_SEPARATOR.'symfony-cache';
  25.         } else {
  26.             $directory realpath($directory) ?: $directory;
  27.         }
  28.         if (isset($namespace[0])) {
  29.             if (preg_match('#[^-+_.A-Za-z0-9]#'$namespace$match)) {
  30.                 throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.'$match[0]));
  31.             }
  32.             $directory .= \DIRECTORY_SEPARATOR.$namespace;
  33.         } else {
  34.             $directory .= \DIRECTORY_SEPARATOR.'@';
  35.         }
  36.         if (!is_dir($directory)) {
  37.             @mkdir($directory0777true);
  38.         }
  39.         $directory .= \DIRECTORY_SEPARATOR;
  40.         // On Windows the whole path is limited to 258 chars
  41.         if ('\\' === \DIRECTORY_SEPARATOR && \strlen($directory) > 234) {
  42.             throw new InvalidArgumentException(sprintf('Cache directory too long (%s).'$directory));
  43.         }
  44.         $this->directory $directory;
  45.     }
  46.     protected function doClear(string $namespace): bool
  47.     {
  48.         $ok true;
  49.         foreach ($this->scanHashDir($this->directory) as $file) {
  50.             if ('' !== $namespace && !str_starts_with($this->getFileKey($file), $namespace)) {
  51.                 continue;
  52.             }
  53.             $ok = ($this->doUnlink($file) || !file_exists($file)) && $ok;
  54.         }
  55.         return $ok;
  56.     }
  57.     protected function doDelete(array $ids): bool
  58.     {
  59.         $ok true;
  60.         foreach ($ids as $id) {
  61.             $file $this->getFile($id);
  62.             $ok = (!is_file($file) || $this->doUnlink($file) || !file_exists($file)) && $ok;
  63.         }
  64.         return $ok;
  65.     }
  66.     protected function doUnlink(string $file)
  67.     {
  68.         return @unlink($file);
  69.     }
  70.     private function write(string $filestring $dataint $expiresAt null)
  71.     {
  72.         set_error_handler(__CLASS__.'::throwError');
  73.         try {
  74.             $tmp $this->directory.$this->tmpSuffix ??= str_replace('/''-'base64_encode(random_bytes(6)));
  75.             try {
  76.                 $h fopen($tmp'x');
  77.             } catch (\ErrorException $e) {
  78.                 if (!str_contains($e->getMessage(), 'File exists')) {
  79.                     throw $e;
  80.                 }
  81.                 $tmp $this->directory.$this->tmpSuffix str_replace('/''-'base64_encode(random_bytes(6)));
  82.                 $h fopen($tmp'x');
  83.             }
  84.             fwrite($h$data);
  85.             fclose($h);
  86.             if (null !== $expiresAt) {
  87.                 touch($tmp$expiresAt ?: time() + 31556952); // 1 year in seconds
  88.             }
  89.             return rename($tmp$file);
  90.         } finally {
  91.             restore_error_handler();
  92.         }
  93.     }
  94.     private function getFile(string $idbool $mkdir falsestring $directory null)
  95.     {
  96.         // Use MD5 to favor speed over security, which is not an issue here
  97.         $hash str_replace('/''-'base64_encode(hash('md5', static::class.$idtrue)));
  98.         $dir = ($directory ?? $this->directory).strtoupper($hash[0].\DIRECTORY_SEPARATOR.$hash[1].\DIRECTORY_SEPARATOR);
  99.         if ($mkdir && !is_dir($dir)) {
  100.             @mkdir($dir0777true);
  101.         }
  102.         return $dir.substr($hash220);
  103.     }
  104.     private function getFileKey(string $file): string
  105.     {
  106.         return '';
  107.     }
  108.     private function scanHashDir(string $directory): \Generator
  109.     {
  110.         if (!is_dir($directory)) {
  111.             return;
  112.         }
  113.         $chars '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  114.         for ($i 0$i 38; ++$i) {
  115.             if (!is_dir($directory.$chars[$i])) {
  116.                 continue;
  117.             }
  118.             for ($j 0$j 38; ++$j) {
  119.                 if (!is_dir($dir $directory.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) {
  120.                     continue;
  121.                 }
  122.                 foreach (@scandir($dir\SCANDIR_SORT_NONE) ?: [] as $file) {
  123.                     if ('.' !== $file && '..' !== $file) {
  124.                         yield $dir.\DIRECTORY_SEPARATOR.$file;
  125.                     }
  126.                 }
  127.             }
  128.         }
  129.     }
  130.     /**
  131.      * @internal
  132.      */
  133.     public static function throwError(int $typestring $messagestring $fileint $line)
  134.     {
  135.         throw new \ErrorException($message0$type$file$line);
  136.     }
  137.     public function __sleep(): array
  138.     {
  139.         throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
  140.     }
  141.     public function __wakeup()
  142.     {
  143.         throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
  144.     }
  145.     public function __destruct()
  146.     {
  147.         if (method_exists(parent::class, '__destruct')) {
  148.             parent::__destruct();
  149.         }
  150.         if (isset($this->tmpSuffix) && is_file($this->directory.$this->tmpSuffix)) {
  151.             unlink($this->directory.$this->tmpSuffix);
  152.         }
  153.     }
  154. }