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

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.     public static function isSupported()
  33.     {
  34.         self::$startTime self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
  35.         return \function_exists('opcache_invalidate') && ('cli' !== \PHP_SAPI || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN)) && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN);
  36.     }
  37.     /**
  38.      * @return bool
  39.      */
  40.     public function prune()
  41.     {
  42.         $time time();
  43.         $pruned true;
  44.         $getExpiry true;
  45.         set_error_handler($this->includeHandler);
  46.         try {
  47.             foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
  48.                 try {
  49.                     if (\is_array($expiresAt = include $file)) {
  50.                         $expiresAt $expiresAt[0];
  51.                     }
  52.                 } catch (\ErrorException $e) {
  53.                     $expiresAt $time;
  54.                 }
  55.                 if ($time >= $expiresAt) {
  56.                     $pruned $this->doUnlink($file) && !file_exists($file) && $pruned;
  57.                 }
  58.             }
  59.         } finally {
  60.             restore_error_handler();
  61.         }
  62.         return $pruned;
  63.     }
  64.     /**
  65.      * {@inheritdoc}
  66.      */
  67.     protected function doFetch(array $ids)
  68.     {
  69.         if ($this->appendOnly) {
  70.             $now 0;
  71.             $missingIds = [];
  72.         } else {
  73.             $now time();
  74.             $missingIds $ids;
  75.             $ids = [];
  76.         }
  77.         $values = [];
  78.         begin:
  79.         $getExpiry false;
  80.         foreach ($ids as $id) {
  81.             if (null === $value $this->values[$id] ?? null) {
  82.                 $missingIds[] = $id;
  83.             } elseif ('N;' === $value) {
  84.                 $values[$id] = null;
  85.             } elseif (!\is_object($value)) {
  86.                 $values[$id] = $value;
  87.             } elseif (!$value instanceof LazyValue) {
  88.                 // calling a Closure is for @deprecated BC and should be removed in Symfony 5.0
  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 (\is_array($expiresAt = include $file)) {
  108.                         [$expiresAt$this->values[$id]] = $expiresAt;
  109.                     } elseif ($now $expiresAt) {
  110.                         $this->values[$id] = new LazyValue($file);
  111.                     }
  112.                     if ($now >= $expiresAt) {
  113.                         unset($this->values[$id], $missingIds[$k]);
  114.                     }
  115.                 } catch (\ErrorException $e) {
  116.                     unset($missingIds[$k]);
  117.                 }
  118.             }
  119.         } finally {
  120.             restore_error_handler();
  121.         }
  122.         $ids $missingIds;
  123.         $missingIds = [];
  124.         goto begin;
  125.     }
  126.     /**
  127.      * {@inheritdoc}
  128.      */
  129.     protected function doHave($id)
  130.     {
  131.         if ($this->appendOnly && isset($this->values[$id])) {
  132.             return true;
  133.         }
  134.         set_error_handler($this->includeHandler);
  135.         try {
  136.             $file $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
  137.             $getExpiry true;
  138.             if (\is_array($expiresAt = include $file)) {
  139.                 [$expiresAt$value] = $expiresAt;
  140.             } elseif ($this->appendOnly) {
  141.                 $value = new LazyValue($file);
  142.             }
  143.         } catch (\ErrorException $e) {
  144.             return false;
  145.         } finally {
  146.             restore_error_handler();
  147.         }
  148.         if ($this->appendOnly) {
  149.             $now 0;
  150.             $this->values[$id] = $value;
  151.         } else {
  152.             $now time();
  153.         }
  154.         return $now $expiresAt;
  155.     }
  156.     /**
  157.      * {@inheritdoc}
  158.      */
  159.     protected function doSave(array $values$lifetime)
  160.     {
  161.         $ok true;
  162.         $expiry $lifetime time() + $lifetime 'PHP_INT_MAX';
  163.         $allowCompile self::isSupported();
  164.         foreach ($values as $key => $value) {
  165.             unset($this->values[$key]);
  166.             $isStaticValue true;
  167.             if (null === $value) {
  168.                 $value "'N;'";
  169.             } elseif (\is_object($value) || \is_array($value)) {
  170.                 try {
  171.                     $value VarExporter::export($value$isStaticValue);
  172.                 } catch (\Exception $e) {
  173.                     throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.'$key, \is_object($value) ? \get_class($value) : 'array'), 0$e);
  174.                 }
  175.             } elseif (\is_string($value)) {
  176.                 // Wrap "N;" in a closure to not confuse it with an encoded `null`
  177.                 if ('N;' === $value) {
  178.                     $isStaticValue false;
  179.                 }
  180.                 $value var_export($valuetrue);
  181.             } elseif (!is_scalar($value)) {
  182.                 throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.'$key, \gettype($value)));
  183.             } else {
  184.                 $value var_export($valuetrue);
  185.             }
  186.             if (!$isStaticValue) {
  187.                 // We cannot use a closure here because of https://bugs.php.net/76982
  188.                 $value str_replace('\Symfony\Component\VarExporter\Internal\\'''$value);
  189.                 $value "<?php\n\nnamespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};\n";
  190.             } else {
  191.                 $value "<?php return [{$expiry}{$value}];\n";
  192.             }
  193.             $file $this->files[$key] = $this->getFile($keytrue);
  194.             // Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
  195.             $ok $this->write($file$valueself::$startTime 10) && $ok;
  196.             if ($allowCompile) {
  197.                 @opcache_invalidate($filetrue);
  198.                 @opcache_compile_file($file);
  199.             }
  200.         }
  201.         if (!$ok && !is_writable($this->directory)) {
  202.             throw new CacheException(sprintf('Cache directory is not writable (%s)'$this->directory));
  203.         }
  204.         return $ok;
  205.     }
  206.     /**
  207.      * {@inheritdoc}
  208.      */
  209.     protected function doClear($namespace)
  210.     {
  211.         $this->values = [];
  212.         return $this->doCommonClear($namespace);
  213.     }
  214.     /**
  215.      * {@inheritdoc}
  216.      */
  217.     protected function doDelete(array $ids)
  218.     {
  219.         foreach ($ids as $id) {
  220.             unset($this->values[$id]);
  221.         }
  222.         return $this->doCommonDelete($ids);
  223.     }
  224.     protected function doUnlink($file)
  225.     {
  226.         if (self::isSupported()) {
  227.             @opcache_invalidate($filetrue);
  228.         }
  229.         return @unlink($file);
  230.     }
  231. }
  232. /**
  233.  * @internal
  234.  */
  235. class LazyValue
  236. {
  237.     public $file;
  238.     public function __construct($file)
  239.     {
  240.         $this->file $file;
  241.     }
  242. }