vendor/symfony/cache/Traits/AbstractAdapterTrait.php line 220

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 Psr\Cache\CacheItemInterface;
  12. use Psr\Log\LoggerAwareTrait;
  13. use Symfony\Component\Cache\CacheItem;
  14. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  15. /**
  16.  * @author Nicolas Grekas <p@tchwork.com>
  17.  *
  18.  * @internal
  19.  */
  20. trait AbstractAdapterTrait
  21. {
  22.     use LoggerAwareTrait;
  23.     /**
  24.      * @var \Closure needs to be set by class, signature is function(string <key>, mixed <value>, bool <isHit>)
  25.      */
  26.     private $createCacheItem;
  27.     /**
  28.      * @var \Closure needs to be set by class, signature is function(array <deferred>, string <namespace>, array <&expiredIds>)
  29.      */
  30.     private $mergeByLifetime;
  31.     private $namespace;
  32.     private $namespaceVersion '';
  33.     private $versioningIsEnabled false;
  34.     private $deferred = [];
  35.     private $ids = [];
  36.     /**
  37.      * @var int|null The maximum length to enforce for identifiers or null when no limit applies
  38.      */
  39.     protected $maxIdLength;
  40.     /**
  41.      * Fetches several cache items.
  42.      *
  43.      * @param array $ids The cache identifiers to fetch
  44.      *
  45.      * @return array|\Traversable The corresponding values found in the cache
  46.      */
  47.     abstract protected function doFetch(array $ids);
  48.     /**
  49.      * Confirms if the cache contains specified cache item.
  50.      *
  51.      * @param string $id The identifier for which to check existence
  52.      *
  53.      * @return bool True if item exists in the cache, false otherwise
  54.      */
  55.     abstract protected function doHave(string $id);
  56.     /**
  57.      * Deletes all items in the pool.
  58.      *
  59.      * @param string $namespace The prefix used for all identifiers managed by this pool
  60.      *
  61.      * @return bool True if the pool was successfully cleared, false otherwise
  62.      */
  63.     abstract protected function doClear(string $namespace);
  64.     /**
  65.      * Removes multiple items from the pool.
  66.      *
  67.      * @param array $ids An array of identifiers that should be removed from the pool
  68.      *
  69.      * @return bool True if the items were successfully removed, false otherwise
  70.      */
  71.     abstract protected function doDelete(array $ids);
  72.     /**
  73.      * Persists several cache items immediately.
  74.      *
  75.      * @param array $values   The values to cache, indexed by their cache identifier
  76.      * @param int   $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
  77.      *
  78.      * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not
  79.      */
  80.     abstract protected function doSave(array $valuesint $lifetime);
  81.     /**
  82.      * {@inheritdoc}
  83.      *
  84.      * @return bool
  85.      */
  86.     public function hasItem($key)
  87.     {
  88.         $id $this->getId($key);
  89.         if (isset($this->deferred[$key])) {
  90.             $this->commit();
  91.         }
  92.         try {
  93.             return $this->doHave($id);
  94.         } catch (\Exception $e) {
  95.             CacheItem::log($this->logger'Failed to check if key "{key}" is cached: '.$e->getMessage(), ['key' => $key'exception' => $e'cache-adapter' => get_debug_type($this)]);
  96.             return false;
  97.         }
  98.     }
  99.     /**
  100.      * {@inheritdoc}
  101.      *
  102.      * @return bool
  103.      */
  104.     public function clear(string $prefix '')
  105.     {
  106.         $this->deferred = [];
  107.         if ($cleared $this->versioningIsEnabled) {
  108.             if ('' === $namespaceVersionToClear $this->namespaceVersion) {
  109.                 foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
  110.                     $namespaceVersionToClear $v;
  111.                 }
  112.             }
  113.             $namespaceToClear $this->namespace.$namespaceVersionToClear;
  114.             $namespaceVersion strtr(substr_replace(base64_encode(pack('V'mt_rand())), static::NS_SEPARATOR5), '/''_');
  115.             try {
  116.                 $cleared $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0);
  117.             } catch (\Exception $e) {
  118.                 $cleared false;
  119.             }
  120.             if ($cleared true === $cleared || [] === $cleared) {
  121.                 $this->namespaceVersion $namespaceVersion;
  122.                 $this->ids = [];
  123.             }
  124.         } else {
  125.             $namespaceToClear $this->namespace.$prefix;
  126.         }
  127.         try {
  128.             return $this->doClear($namespaceToClear) || $cleared;
  129.         } catch (\Exception $e) {
  130.             CacheItem::log($this->logger'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e'cache-adapter' => get_debug_type($this)]);
  131.             return false;
  132.         }
  133.     }
  134.     /**
  135.      * {@inheritdoc}
  136.      *
  137.      * @return bool
  138.      */
  139.     public function deleteItem($key)
  140.     {
  141.         return $this->deleteItems([$key]);
  142.     }
  143.     /**
  144.      * {@inheritdoc}
  145.      *
  146.      * @return bool
  147.      */
  148.     public function deleteItems(array $keys)
  149.     {
  150.         $ids = [];
  151.         foreach ($keys as $key) {
  152.             $ids[$key] = $this->getId($key);
  153.             unset($this->deferred[$key]);
  154.         }
  155.         try {
  156.             if ($this->doDelete($ids)) {
  157.                 return true;
  158.             }
  159.         } catch (\Exception $e) {
  160.         }
  161.         $ok true;
  162.         // When bulk-delete failed, retry each item individually
  163.         foreach ($ids as $key => $id) {
  164.             try {
  165.                 $e null;
  166.                 if ($this->doDelete([$id])) {
  167.                     continue;
  168.                 }
  169.             } catch (\Exception $e) {
  170.             }
  171.             $message 'Failed to delete key "{key}"'.($e instanceof \Exception ': '.$e->getMessage() : '.');
  172.             CacheItem::log($this->logger$message, ['key' => $key'exception' => $e'cache-adapter' => get_debug_type($this)]);
  173.             $ok false;
  174.         }
  175.         return $ok;
  176.     }
  177.     /**
  178.      * {@inheritdoc}
  179.      */
  180.     public function getItem($key)
  181.     {
  182.         if ($this->deferred) {
  183.             $this->commit();
  184.         }
  185.         $id $this->getId($key);
  186.         $f $this->createCacheItem;
  187.         $isHit false;
  188.         $value null;
  189.         try {
  190.             foreach ($this->doFetch([$id]) as $value) {
  191.                 $isHit true;
  192.             }
  193.             return $f($key$value$isHit);
  194.         } catch (\Exception $e) {
  195.             CacheItem::log($this->logger'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key'exception' => $e'cache-adapter' => get_debug_type($this)]);
  196.         }
  197.         return $f($keynullfalse);
  198.     }
  199.     /**
  200.      * {@inheritdoc}
  201.      */
  202.     public function getItems(array $keys = [])
  203.     {
  204.         if ($this->deferred) {
  205.             $this->commit();
  206.         }
  207.         $ids = [];
  208.         foreach ($keys as $key) {
  209.             $ids[] = $this->getId($key);
  210.         }
  211.         try {
  212.             $items $this->doFetch($ids);
  213.         } catch (\Exception $e) {
  214.             CacheItem::log($this->logger'Failed to fetch items: '.$e->getMessage(), ['keys' => $keys'exception' => $e'cache-adapter' => get_debug_type($this)]);
  215.             $items = [];
  216.         }
  217.         $ids array_combine($ids$keys);
  218.         return $this->generateItems($items$ids);
  219.     }
  220.     /**
  221.      * {@inheritdoc}
  222.      *
  223.      * @return bool
  224.      */
  225.     public function save(CacheItemInterface $item)
  226.     {
  227.         if (!$item instanceof CacheItem) {
  228.             return false;
  229.         }
  230.         $this->deferred[$item->getKey()] = $item;
  231.         return $this->commit();
  232.     }
  233.     /**
  234.      * {@inheritdoc}
  235.      *
  236.      * @return bool
  237.      */
  238.     public function saveDeferred(CacheItemInterface $item)
  239.     {
  240.         if (!$item instanceof CacheItem) {
  241.             return false;
  242.         }
  243.         $this->deferred[$item->getKey()] = $item;
  244.         return true;
  245.     }
  246.     /**
  247.      * Enables/disables versioning of items.
  248.      *
  249.      * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed,
  250.      * but old keys may need garbage collection and extra round-trips to the back-end are required.
  251.      *
  252.      * Calling this method also clears the memoized namespace version and thus forces a resynchonization of it.
  253.      *
  254.      * @return bool the previous state of versioning
  255.      */
  256.     public function enableVersioning(bool $enable true)
  257.     {
  258.         $wasEnabled $this->versioningIsEnabled;
  259.         $this->versioningIsEnabled $enable;
  260.         $this->namespaceVersion '';
  261.         $this->ids = [];
  262.         return $wasEnabled;
  263.     }
  264.     /**
  265.      * {@inheritdoc}
  266.      */
  267.     public function reset()
  268.     {
  269.         if ($this->deferred) {
  270.             $this->commit();
  271.         }
  272.         $this->namespaceVersion '';
  273.         $this->ids = [];
  274.     }
  275.     /**
  276.      * @return array
  277.      */
  278.     public function __sleep()
  279.     {
  280.         throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
  281.     }
  282.     public function __wakeup()
  283.     {
  284.         throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
  285.     }
  286.     public function __destruct()
  287.     {
  288.         if ($this->deferred) {
  289.             $this->commit();
  290.         }
  291.     }
  292.     private function generateItems(iterable $items, array &$keys): \Generator
  293.     {
  294.         $f $this->createCacheItem;
  295.         try {
  296.             foreach ($items as $id => $value) {
  297.                 if (!isset($keys[$id])) {
  298.                     throw new InvalidArgumentException(sprintf('Could not match value id "%s" to keys "%s".'$idimplode('", "'$keys)));
  299.                 }
  300.                 $key $keys[$id];
  301.                 unset($keys[$id]);
  302.                 yield $key => $f($key$valuetrue);
  303.             }
  304.         } catch (\Exception $e) {
  305.             CacheItem::log($this->logger'Failed to fetch items: '.$e->getMessage(), ['keys' => array_values($keys), 'exception' => $e'cache-adapter' => get_debug_type($this)]);
  306.         }
  307.         foreach ($keys as $key) {
  308.             yield $key => $f($keynullfalse);
  309.         }
  310.     }
  311.     private function getId($key)
  312.     {
  313.         if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
  314.             $this->ids = [];
  315.             $this->namespaceVersion '1'.static::NS_SEPARATOR;
  316.             try {
  317.                 foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
  318.                     $this->namespaceVersion $v;
  319.                 }
  320.                 if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) {
  321.                     $this->namespaceVersion strtr(substr_replace(base64_encode(pack('V'time())), static::NS_SEPARATOR5), '/''_');
  322.                     $this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0);
  323.                 }
  324.             } catch (\Exception $e) {
  325.             }
  326.         }
  327.         if (\is_string($key) && isset($this->ids[$key])) {
  328.             return $this->namespace.$this->namespaceVersion.$this->ids[$key];
  329.         }
  330.         CacheItem::validateKey($key);
  331.         $this->ids[$key] = $key;
  332.         if (null === $this->maxIdLength) {
  333.             return $this->namespace.$this->namespaceVersion.$key;
  334.         }
  335.         if (\strlen($id $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
  336.             // Use MD5 to favor speed over security, which is not an issue here
  337.             $this->ids[$key] = $id substr_replace(base64_encode(hash('md5'$keytrue)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 2));
  338.             $id $this->namespace.$this->namespaceVersion.$id;
  339.         }
  340.         return $id;
  341.     }
  342.     /**
  343.      * @internal
  344.      */
  345.     public static function handleUnserializeCallback(string $class)
  346.     {
  347.         throw new \DomainException('Class not found: '.$class);
  348.     }
  349. }