vendor/symfony/cache/Traits/PhpFilesTrait.php line 81

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\CacheException;
  12. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  13. use Symfony\Component\VarExporter\VarExporter;
  14. /**
  15.  * @author Piotr Stankowski <git@trakos.pl>
  16.  * @author Nicolas Grekas <p@tchwork.com>
  17.  * @author Rob Frawley 2nd <rmf@src.run>
  18.  *
  19.  * @internal
  20.  */
  21. trait PhpFilesTrait
  22. {
  23.     use FilesystemCommonTrait {
  24.         doClear as private doCommonClear;
  25.         doDelete as private doCommonDelete;
  26.     }
  27.     private $includeHandler;
  28.     private $appendOnly;
  29.     private $values = [];
  30.     private $files = [];
  31.     private static $startTime;
  32.     private static $valuesCache = [];
  33.     public static function isSupported()
  34.     {
  35.         self::$startTime self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
  36.         return \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli''phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN));
  37.     }
  38.     /**
  39.      * @return bool
  40.      */
  41.     public function prune()
  42.     {
  43.         $time time();
  44.         $pruned true;
  45.         $getExpiry true;
  46.         set_error_handler($this->includeHandler);
  47.         try {
  48.             foreach ($this->scanHashDir($this->directory) as $file) {
  49.                 try {
  50.                     if (\is_array($expiresAt = include $file)) {
  51.                         $expiresAt $expiresAt[0];
  52.                     }
  53.                 } catch (\ErrorException $e) {
  54.                     $expiresAt $time;
  55.                 }
  56.                 if ($time >= $expiresAt) {
  57.                     $pruned $this->doUnlink($file) && !file_exists($file) && $pruned;
  58.                 }
  59.             }
  60.         } finally {
  61.             restore_error_handler();
  62.         }
  63.         return $pruned;
  64.     }
  65.     /**
  66.      * {@inheritdoc}
  67.      */
  68.     protected function doFetch(array $ids)
  69.     {
  70.         if ($this->appendOnly) {
  71.             $now 0;
  72.             $missingIds = [];
  73.         } else {
  74.             $now time();
  75.             $missingIds $ids;
  76.             $ids = [];
  77.         }
  78.         $values = [];
  79.         begin:
  80.         $getExpiry false;
  81.         foreach ($ids as $id) {
  82.             if (null === $value $this->values[$id] ?? null) {
  83.                 $missingIds[] = $id;
  84.             } elseif ('N;' === $value) {
  85.                 $values[$id] = null;
  86.             } elseif (!\is_object($value)) {
  87.                 $values[$id] = $value;
  88.             } elseif (!$value instanceof LazyValue) {
  89.                 $values[$id] = $value();
  90.             } elseif (false === $values[$id] = include $value->file) {
  91.                 unset($values[$id], $this->values[$id]);
  92.                 $missingIds[] = $id;
  93.             }
  94.             if (!$this->appendOnly) {
  95.                 unset($this->values[$id]);
  96.             }
  97.         }
  98.         if (!$missingIds) {
  99.             return $values;
  100.         }
  101.         set_error_handler($this->includeHandler);
  102.         try {
  103.             $getExpiry true;
  104.             foreach ($missingIds as $k => $id) {
  105.                 try {
  106.                     $file $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
  107.                     if (isset(self::$valuesCache[$file])) {
  108.                         [$expiresAt$this->values[$id]] = self::$valuesCache[$file];
  109.                     } elseif (\is_array($expiresAt = include $file)) {
  110.                         if ($this->appendOnly) {
  111.                             self::$valuesCache[$file] = $expiresAt;
  112.                         }
  113.                         [$expiresAt$this->values[$id]] = $expiresAt;
  114.                     } elseif ($now $expiresAt) {
  115.                         $this->values[$id] = new LazyValue($file);
  116.                     }
  117.                     if ($now >= $expiresAt) {
  118.                         unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
  119.                     }
  120.                 } catch (\ErrorException $e) {
  121.                     unset($missingIds[$k]);
  122.                 }
  123.             }
  124.         } finally {
  125.             restore_error_handler();
  126.         }
  127.         $ids $missingIds;
  128.         $missingIds = [];
  129.         goto begin;
  130.     }
  131.     /**
  132.      * {@inheritdoc}
  133.      */
  134.     protected function doHave($id)
  135.     {
  136.         if ($this->appendOnly && isset($this->values[$id])) {
  137.             return true;
  138.         }
  139.         set_error_handler($this->includeHandler);
  140.         try {
  141.             $file $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
  142.             $getExpiry true;
  143.             if (isset(self::$valuesCache[$file])) {
  144.                 [$expiresAt$value] = self::$valuesCache[$file];
  145.             } elseif (\is_array($expiresAt = include $file)) {
  146.                 if ($this->appendOnly) {
  147.                     self::$valuesCache[$file] = $expiresAt;
  148.                 }
  149.                 [$expiresAt$value] = $expiresAt;
  150.             } elseif ($this->appendOnly) {
  151.                 $value = new LazyValue($file);
  152.             }
  153.         } catch (\ErrorException $e) {
  154.             return false;
  155.         } finally {
  156.             restore_error_handler();
  157.         }
  158.         if ($this->appendOnly) {
  159.             $now 0;
  160.             $this->values[$id] = $value;
  161.         } else {
  162.             $now time();
  163.         }
  164.         return $now $expiresAt;
  165.     }
  166.     /**
  167.      * {@inheritdoc}
  168.      */
  169.     protected function doSave(array $valuesint $lifetime)
  170.     {
  171.         $ok true;
  172.         $expiry $lifetime time() + $lifetime 'PHP_INT_MAX';
  173.         $allowCompile self::isSupported();
  174.         foreach ($values as $key => $value) {
  175.             unset($this->values[$key]);
  176.             $isStaticValue true;
  177.             if (null === $value) {
  178.                 $value "'N;'";
  179.             } elseif (\is_object($value) || \is_array($value)) {
  180.                 try {
  181.                     $value VarExporter::export($value$isStaticValue);
  182.                 } catch (\Exception $e) {
  183.                     throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.'$key, \is_object($value) ? \get_class($value) : 'array'), 0$e);
  184.                 }
  185.             } elseif (\is_string($value)) {
  186.                 // Wrap "N;" in a closure to not confuse it with an encoded `null`
  187.                 if ('N;' === $value) {
  188.                     $isStaticValue false;
  189.                 }
  190.                 $value var_export($valuetrue);
  191.             } elseif (!\is_scalar($value)) {
  192.                 throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.'$key, \gettype($value)));
  193.             } else {
  194.                 $value var_export($valuetrue);
  195.             }
  196.             $encodedKey rawurlencode($key);
  197.             if ($isStaticValue) {
  198.                 $value "return [{$expiry}{$value}];";
  199.             } elseif ($this->appendOnly) {
  200.                 $value "return [{$expiry}, static function () { return {$value}; }];";
  201.             } else {
  202.                 // We cannot use a closure here because of https://bugs.php.net/76982
  203.                 $value str_replace('\Symfony\Component\VarExporter\Internal\\'''$value);
  204.                 $value "namespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};";
  205.             }
  206.             $file $this->files[$key] = $this->getFile($keytrue);
  207.             // Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
  208.             $ok $this->write($file"<?php //{$encodedKey}\n\n{$value}\n"self::$startTime 10) && $ok;
  209.             if ($allowCompile) {
  210.                 @opcache_invalidate($filetrue);
  211.                 @opcache_compile_file($file);
  212.             }
  213.             unset(self::$valuesCache[$file]);
  214.         }
  215.         if (!$ok && !is_writable($this->directory)) {
  216.             throw new CacheException(sprintf('Cache directory is not writable (%s).'$this->directory));
  217.         }
  218.         return $ok;
  219.     }
  220.     /**
  221.      * {@inheritdoc}
  222.      */
  223.     protected function doClear($namespace)
  224.     {
  225.         $this->values = [];
  226.         return $this->doCommonClear($namespace);
  227.     }
  228.     /**
  229.      * {@inheritdoc}
  230.      */
  231.     protected function doDelete(array $ids)
  232.     {
  233.         foreach ($ids as $id) {
  234.             unset($this->values[$id]);
  235.         }
  236.         return $this->doCommonDelete($ids);
  237.     }
  238.     protected function doUnlink($file)
  239.     {
  240.         unset(self::$valuesCache[$file]);
  241.         if (self::isSupported()) {
  242.             @opcache_invalidate($filetrue);
  243.         }
  244.         return @unlink($file);
  245.     }
  246.     private function getFileKey(string $file): string
  247.     {
  248.         if (!$h = @fopen($file'r')) {
  249.             return '';
  250.         }
  251.         $encodedKey substr(fgets($h), 8);
  252.         fclose($h);
  253.         return rawurldecode(rtrim($encodedKey));
  254.     }
  255. }
  256. /**
  257.  * @internal
  258.  */
  259. class LazyValue
  260. {
  261.     public $file;
  262.     public function __construct(string $file)
  263.     {
  264.         $this->file $file;
  265.     }
  266. }