vendor/symfony/cache/Adapter/ChainAdapter.php line 132

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\Adapter;
  11. use Psr\Cache\CacheItemInterface;
  12. use Psr\Cache\CacheItemPoolInterface;
  13. use Symfony\Component\Cache\CacheItem;
  14. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  15. use Symfony\Component\Cache\PruneableInterface;
  16. use Symfony\Component\Cache\ResettableInterface;
  17. use Symfony\Component\Cache\Traits\ContractsTrait;
  18. use Symfony\Contracts\Cache\CacheInterface;
  19. use Symfony\Contracts\Service\ResetInterface;
  20. /**
  21.  * Chains several adapters together.
  22.  *
  23.  * Cached items are fetched from the first adapter having them in its data store.
  24.  * They are saved and deleted in all adapters at once.
  25.  *
  26.  * @author Kévin Dunglas <dunglas@gmail.com>
  27.  */
  28. class ChainAdapter implements AdapterInterfaceCacheInterfacePruneableInterfaceResettableInterface
  29. {
  30.     use ContractsTrait;
  31.     private $adapters = [];
  32.     private $adapterCount;
  33.     private $defaultLifetime;
  34.     private static $syncItem;
  35.     /**
  36.      * @param CacheItemPoolInterface[] $adapters        The ordered list of adapters used to fetch cached items
  37.      * @param int                      $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones
  38.      */
  39.     public function __construct(array $adaptersint $defaultLifetime 0)
  40.     {
  41.         if (!$adapters) {
  42.             throw new InvalidArgumentException('At least one adapter must be specified.');
  43.         }
  44.         foreach ($adapters as $adapter) {
  45.             if (!$adapter instanceof CacheItemPoolInterface) {
  46.                 throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.'get_debug_type($adapter), CacheItemPoolInterface::class));
  47.             }
  48.             if (\in_array(\PHP_SAPI, ['cli''phpdbg'], true) && $adapter instanceof ApcuAdapter && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
  49.                 continue; // skip putting APCu in the chain when the backend is disabled
  50.             }
  51.             if ($adapter instanceof AdapterInterface) {
  52.                 $this->adapters[] = $adapter;
  53.             } else {
  54.                 $this->adapters[] = new ProxyAdapter($adapter);
  55.             }
  56.         }
  57.         $this->adapterCount = \count($this->adapters);
  58.         $this->defaultLifetime $defaultLifetime;
  59.         self::$syncItem ?? self::$syncItem = \Closure::bind(
  60.             static function ($sourceItem$item$defaultLifetime$sourceMetadata null) {
  61.                 $sourceItem->isTaggable false;
  62.                 $sourceMetadata $sourceMetadata ?? $sourceItem->metadata;
  63.                 unset($sourceMetadata[CacheItem::METADATA_TAGS]);
  64.                 $item->value $sourceItem->value;
  65.                 $item->isHit $sourceItem->isHit;
  66.                 $item->metadata $item->newMetadata $sourceItem->metadata $sourceMetadata;
  67.                 if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) {
  68.                     $item->expiresAt(\DateTime::createFromFormat('U.u'sprintf('%.6F'$item->metadata[CacheItem::METADATA_EXPIRY])));
  69.                 } elseif ($defaultLifetime) {
  70.                     $item->expiresAfter($defaultLifetime);
  71.                 }
  72.                 return $item;
  73.             },
  74.             null,
  75.             CacheItem::class
  76.         );
  77.     }
  78.     /**
  79.      * {@inheritdoc}
  80.      */
  81.     public function get(string $key, callable $callbackfloat $beta null, array &$metadata null)
  82.     {
  83.         $doSave true;
  84.         $callback = static function (CacheItem $itembool &$save) use ($callback, &$doSave) {
  85.             $value $callback($item$save);
  86.             $doSave $save;
  87.             return $value;
  88.         };
  89.         $lastItem null;
  90.         $i 0;
  91.         $wrap = function (CacheItem $item nullbool &$save true) use ($key$callback$beta, &$wrap, &$i, &$doSave, &$lastItem, &$metadata) {
  92.             $adapter $this->adapters[$i];
  93.             if (isset($this->adapters[++$i])) {
  94.                 $callback $wrap;
  95.                 $beta = \INF === $beta ? \INF 0;
  96.             }
  97.             if ($adapter instanceof CacheInterface) {
  98.                 $value $adapter->get($key$callback$beta$metadata);
  99.             } else {
  100.                 $value $this->doGet($adapter$key$callback$beta$metadata);
  101.             }
  102.             if (null !== $item) {
  103.                 (self::$syncItem)($lastItem $lastItem ?? $item$item$this->defaultLifetime$metadata);
  104.             }
  105.             $save $doSave;
  106.             return $value;
  107.         };
  108.         return $wrap();
  109.     }
  110.     /**
  111.      * {@inheritdoc}
  112.      */
  113.     public function getItem($key)
  114.     {
  115.         $syncItem self::$syncItem;
  116.         $misses = [];
  117.         foreach ($this->adapters as $i => $adapter) {
  118.             $item $adapter->getItem($key);
  119.             if ($item->isHit()) {
  120.                 while (<= --$i) {
  121.                     $this->adapters[$i]->save($syncItem($item$misses[$i], $this->defaultLifetime));
  122.                 }
  123.                 return $item;
  124.             }
  125.             $misses[$i] = $item;
  126.         }
  127.         return $item;
  128.     }
  129.     /**
  130.      * {@inheritdoc}
  131.      */
  132.     public function getItems(array $keys = [])
  133.     {
  134.         return $this->generateItems($this->adapters[0]->getItems($keys), 0);
  135.     }
  136.     private function generateItems(iterable $itemsint $adapterIndex): \Generator
  137.     {
  138.         $missing = [];
  139.         $misses = [];
  140.         $nextAdapterIndex $adapterIndex 1;
  141.         $nextAdapter $this->adapters[$nextAdapterIndex] ?? null;
  142.         foreach ($items as $k => $item) {
  143.             if (!$nextAdapter || $item->isHit()) {
  144.                 yield $k => $item;
  145.             } else {
  146.                 $missing[] = $k;
  147.                 $misses[$k] = $item;
  148.             }
  149.         }
  150.         if ($missing) {
  151.             $syncItem self::$syncItem;
  152.             $adapter $this->adapters[$adapterIndex];
  153.             $items $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex);
  154.             foreach ($items as $k => $item) {
  155.                 if ($item->isHit()) {
  156.                     $adapter->save($syncItem($item$misses[$k], $this->defaultLifetime));
  157.                 }
  158.                 yield $k => $item;
  159.             }
  160.         }
  161.     }
  162.     /**
  163.      * {@inheritdoc}
  164.      *
  165.      * @return bool
  166.      */
  167.     public function hasItem($key)
  168.     {
  169.         foreach ($this->adapters as $adapter) {
  170.             if ($adapter->hasItem($key)) {
  171.                 return true;
  172.             }
  173.         }
  174.         return false;
  175.     }
  176.     /**
  177.      * {@inheritdoc}
  178.      *
  179.      * @return bool
  180.      */
  181.     public function clear(string $prefix '')
  182.     {
  183.         $cleared true;
  184.         $i $this->adapterCount;
  185.         while ($i--) {
  186.             if ($this->adapters[$i] instanceof AdapterInterface) {
  187.                 $cleared $this->adapters[$i]->clear($prefix) && $cleared;
  188.             } else {
  189.                 $cleared $this->adapters[$i]->clear() && $cleared;
  190.             }
  191.         }
  192.         return $cleared;
  193.     }
  194.     /**
  195.      * {@inheritdoc}
  196.      *
  197.      * @return bool
  198.      */
  199.     public function deleteItem($key)
  200.     {
  201.         $deleted true;
  202.         $i $this->adapterCount;
  203.         while ($i--) {
  204.             $deleted $this->adapters[$i]->deleteItem($key) && $deleted;
  205.         }
  206.         return $deleted;
  207.     }
  208.     /**
  209.      * {@inheritdoc}
  210.      *
  211.      * @return bool
  212.      */
  213.     public function deleteItems(array $keys)
  214.     {
  215.         $deleted true;
  216.         $i $this->adapterCount;
  217.         while ($i--) {
  218.             $deleted $this->adapters[$i]->deleteItems($keys) && $deleted;
  219.         }
  220.         return $deleted;
  221.     }
  222.     /**
  223.      * {@inheritdoc}
  224.      *
  225.      * @return bool
  226.      */
  227.     public function save(CacheItemInterface $item)
  228.     {
  229.         $saved true;
  230.         $i $this->adapterCount;
  231.         while ($i--) {
  232.             $saved $this->adapters[$i]->save($item) && $saved;
  233.         }
  234.         return $saved;
  235.     }
  236.     /**
  237.      * {@inheritdoc}
  238.      *
  239.      * @return bool
  240.      */
  241.     public function saveDeferred(CacheItemInterface $item)
  242.     {
  243.         $saved true;
  244.         $i $this->adapterCount;
  245.         while ($i--) {
  246.             $saved $this->adapters[$i]->saveDeferred($item) && $saved;
  247.         }
  248.         return $saved;
  249.     }
  250.     /**
  251.      * {@inheritdoc}
  252.      *
  253.      * @return bool
  254.      */
  255.     public function commit()
  256.     {
  257.         $committed true;
  258.         $i $this->adapterCount;
  259.         while ($i--) {
  260.             $committed $this->adapters[$i]->commit() && $committed;
  261.         }
  262.         return $committed;
  263.     }
  264.     /**
  265.      * {@inheritdoc}
  266.      */
  267.     public function prune()
  268.     {
  269.         $pruned true;
  270.         foreach ($this->adapters as $adapter) {
  271.             if ($adapter instanceof PruneableInterface) {
  272.                 $pruned $adapter->prune() && $pruned;
  273.             }
  274.         }
  275.         return $pruned;
  276.     }
  277.     /**
  278.      * {@inheritdoc}
  279.      */
  280.     public function reset()
  281.     {
  282.         foreach ($this->adapters as $adapter) {
  283.             if ($adapter instanceof ResetInterface) {
  284.                 $adapter->reset();
  285.             }
  286.         }
  287.     }
  288. }