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

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