vendor/symfony/config/ResourceCheckerConfigCache.php line 65

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\Config;
  11. use Symfony\Component\Config\Resource\ResourceInterface;
  12. use Symfony\Component\Filesystem\Exception\IOException;
  13. use Symfony\Component\Filesystem\Filesystem;
  14. /**
  15.  * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface
  16.  * to check whether cached data is still fresh.
  17.  *
  18.  * @author Matthias Pigulla <mp@webfactory.de>
  19.  */
  20. class ResourceCheckerConfigCache implements ConfigCacheInterface
  21. {
  22.     /**
  23.      * @var string
  24.      */
  25.     private $file;
  26.     /**
  27.      * @var iterable|ResourceCheckerInterface[]
  28.      */
  29.     private $resourceCheckers;
  30.     /**
  31.      * @param string                              $file             The absolute cache path
  32.      * @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
  33.      */
  34.     public function __construct(string $fileiterable $resourceCheckers = [])
  35.     {
  36.         $this->file $file;
  37.         $this->resourceCheckers $resourceCheckers;
  38.     }
  39.     /**
  40.      * {@inheritdoc}
  41.      */
  42.     public function getPath()
  43.     {
  44.         return $this->file;
  45.     }
  46.     /**
  47.      * Checks if the cache is still fresh.
  48.      *
  49.      * This implementation will make a decision solely based on the ResourceCheckers
  50.      * passed in the constructor.
  51.      *
  52.      * The first ResourceChecker that supports a given resource is considered authoritative.
  53.      * Resources with no matching ResourceChecker will silently be ignored and considered fresh.
  54.      *
  55.      * @return bool true if the cache is fresh, false otherwise
  56.      */
  57.     public function isFresh()
  58.     {
  59.         if (!is_file($this->file)) {
  60.             return false;
  61.         }
  62.         if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) {
  63.             $this->resourceCheckers iterator_to_array($this->resourceCheckers);
  64.         }
  65.         if (!\count($this->resourceCheckers)) {
  66.             return true// shortcut - if we don't have any checkers we don't need to bother with the meta file at all
  67.         }
  68.         $metadata $this->getMetaFile();
  69.         if (!is_file($metadata)) {
  70.             return false;
  71.         }
  72.         $meta $this->safelyUnserialize($metadata);
  73.         if (false === $meta) {
  74.             return false;
  75.         }
  76.         $time filemtime($this->file);
  77.         foreach ($meta as $resource) {
  78.             /* @var ResourceInterface $resource */
  79.             foreach ($this->resourceCheckers as $checker) {
  80.                 if (!$checker->supports($resource)) {
  81.                     continue; // next checker
  82.                 }
  83.                 if ($checker->isFresh($resource$time)) {
  84.                     break; // no need to further check this resource
  85.                 }
  86.                 return false// cache is stale
  87.             }
  88.             // no suitable checker found, ignore this resource
  89.         }
  90.         return true;
  91.     }
  92.     /**
  93.      * Writes cache.
  94.      *
  95.      * @param string              $content  The content to write in the cache
  96.      * @param ResourceInterface[] $metadata An array of metadata
  97.      *
  98.      * @throws \RuntimeException When cache file can't be written
  99.      */
  100.     public function write(string $content, array $metadata null)
  101.     {
  102.         $mode 0666;
  103.         $umask umask();
  104.         $filesystem = new Filesystem();
  105.         $filesystem->dumpFile($this->file$content);
  106.         try {
  107.             $filesystem->chmod($this->file$mode$umask);
  108.         } catch (IOException $e) {
  109.             // discard chmod failure (some filesystem may not support it)
  110.         }
  111.         if (null !== $metadata) {
  112.             $filesystem->dumpFile($this->getMetaFile(), serialize($metadata));
  113.             try {
  114.                 $filesystem->chmod($this->getMetaFile(), $mode$umask);
  115.             } catch (IOException $e) {
  116.                 // discard chmod failure (some filesystem may not support it)
  117.             }
  118.         }
  119.         if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) {
  120.             @opcache_invalidate($this->filetrue);
  121.         }
  122.     }
  123.     /**
  124.      * Gets the meta file path.
  125.      */
  126.     private function getMetaFile(): string
  127.     {
  128.         return $this->file.'.meta';
  129.     }
  130.     private function safelyUnserialize(string $file)
  131.     {
  132.         $meta false;
  133.         $content file_get_contents($file);
  134.         $signalingException = new \UnexpectedValueException();
  135.         $prevUnserializeHandler ini_set('unserialize_callback_func'self::class.'::handleUnserializeCallback');
  136.         $prevErrorHandler set_error_handler(function ($type$msg$file$line$context = []) use (&$prevErrorHandler$signalingException) {
  137.             if (__FILE__ === $file) {
  138.                 throw $signalingException;
  139.             }
  140.             return $prevErrorHandler $prevErrorHandler($type$msg$file$line$context) : false;
  141.         });
  142.         try {
  143.             $meta unserialize($content);
  144.         } catch (\Throwable $e) {
  145.             if ($e !== $signalingException) {
  146.                 throw $e;
  147.             }
  148.         } finally {
  149.             restore_error_handler();
  150.             ini_set('unserialize_callback_func'$prevUnserializeHandler);
  151.         }
  152.         return $meta;
  153.     }
  154.     /**
  155.      * @internal
  156.      */
  157.     public static function handleUnserializeCallback($class)
  158.     {
  159.         trigger_error('Class not found: '.$class);
  160.     }
  161. }