vendor/symfony/cache/Adapter/AbstractAdapter.php line 163

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\Log\LoggerAwareInterface;
  13. use Psr\Log\LoggerInterface;
  14. use Psr\Log\NullLogger;
  15. use Symfony\Component\Cache\CacheItem;
  16. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  17. use Symfony\Component\Cache\ResettableInterface;
  18. use Symfony\Component\Cache\Traits\AbstractTrait;
  19. /**
  20.  * @author Nicolas Grekas <p@tchwork.com>
  21.  */
  22. abstract class AbstractAdapter implements AdapterInterfaceLoggerAwareInterfaceResettableInterface
  23. {
  24.     /**
  25.      * @internal
  26.      */
  27.     const NS_SEPARATOR ':';
  28.     use AbstractTrait;
  29.     private static $apcuSupported;
  30.     private static $phpFilesSupported;
  31.     private $createCacheItem;
  32.     private $mergeByLifetime;
  33.     /**
  34.      * @param string $namespace
  35.      * @param int    $defaultLifetime
  36.      */
  37.     protected function __construct($namespace ''$defaultLifetime 0)
  38.     {
  39.         $this->namespace '' === $namespace '' CacheItem::validateKey($namespace).static::NS_SEPARATOR;
  40.         if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength 24) {
  41.             throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")'$this->maxIdLength 24, \strlen($namespace), $namespace));
  42.         }
  43.         $this->createCacheItem = \Closure::bind(
  44.             static function ($key$value$isHit) use ($defaultLifetime) {
  45.                 $item = new CacheItem();
  46.                 $item->key $key;
  47.                 $item->value $value;
  48.                 $item->isHit $isHit;
  49.                 $item->defaultLifetime $defaultLifetime;
  50.                 return $item;
  51.             },
  52.             null,
  53.             CacheItem::class
  54.         );
  55.         $getId = function ($key) { return $this->getId((string) $key); };
  56.         $this->mergeByLifetime = \Closure::bind(
  57.             static function ($deferred$namespace, &$expiredIds) use ($getId) {
  58.                 $byLifetime = [];
  59.                 $now time();
  60.                 $expiredIds = [];
  61.                 foreach ($deferred as $key => $item) {
  62.                     if (null === $item->expiry) {
  63.                         $byLifetime[$item->defaultLifetime $item->defaultLifetime 0][$getId($key)] = $item->value;
  64.                     } elseif ($item->expiry $now) {
  65.                         $byLifetime[$item->expiry $now][$getId($key)] = $item->value;
  66.                     } else {
  67.                         $expiredIds[] = $getId($key);
  68.                     }
  69.                 }
  70.                 return $byLifetime;
  71.             },
  72.             null,
  73.             CacheItem::class
  74.         );
  75.     }
  76.     /**
  77.      * @param string $namespace
  78.      * @param int    $defaultLifetime
  79.      * @param string $version
  80.      * @param string $directory
  81.      *
  82.      * @return AdapterInterface
  83.      */
  84.     public static function createSystemCache($namespace$defaultLifetime$version$directoryLoggerInterface $logger null)
  85.     {
  86.         if (null === self::$apcuSupported) {
  87.             self::$apcuSupported ApcuAdapter::isSupported();
  88.         }
  89.         if (!self::$apcuSupported && null === self::$phpFilesSupported) {
  90.             self::$phpFilesSupported PhpFilesAdapter::isSupported();
  91.         }
  92.         if (self::$phpFilesSupported) {
  93.             $opcache = new PhpFilesAdapter($namespace$defaultLifetime$directory);
  94.             if (null !== $logger) {
  95.                 $opcache->setLogger($logger);
  96.             }
  97.             return $opcache;
  98.         }
  99.         $fs = new FilesystemAdapter($namespace$defaultLifetime$directory);
  100.         if (null !== $logger) {
  101.             $fs->setLogger($logger);
  102.         }
  103.         if (!self::$apcuSupported) {
  104.             return $fs;
  105.         }
  106.         $apcu = new ApcuAdapter($namespace, (int) $defaultLifetime 5$version);
  107.         if ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
  108.             $apcu->setLogger(new NullLogger());
  109.         } elseif (null !== $logger) {
  110.             $apcu->setLogger($logger);
  111.         }
  112.         return new ChainAdapter([$apcu$fs]);
  113.     }
  114.     public static function createConnection($dsn, array $options = [])
  115.     {
  116.         if (!\is_string($dsn)) {
  117.             throw new InvalidArgumentException(sprintf('The %s() method expect argument #1 to be string, %s given.'__METHOD__, \gettype($dsn)));
  118.         }
  119.         if (=== strpos($dsn'redis://')) {
  120.             return RedisAdapter::createConnection($dsn$options);
  121.         }
  122.         if (=== strpos($dsn'memcached://')) {
  123.             return MemcachedAdapter::createConnection($dsn$options);
  124.         }
  125.         throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.'$dsn));
  126.     }
  127.     /**
  128.      * {@inheritdoc}
  129.      */
  130.     public function getItem($key)
  131.     {
  132.         if ($this->deferred) {
  133.             $this->commit();
  134.         }
  135.         $id $this->getId($key);
  136.         $f $this->createCacheItem;
  137.         $isHit false;
  138.         $value null;
  139.         try {
  140.             foreach ($this->doFetch([$id]) as $value) {
  141.                 $isHit true;
  142.             }
  143.         } catch (\Exception $e) {
  144.             CacheItem::log($this->logger'Failed to fetch key "{key}"', ['key' => $key'exception' => $e]);
  145.         }
  146.         return $f($key$value$isHit);
  147.     }
  148.     /**
  149.      * {@inheritdoc}
  150.      */
  151.     public function getItems(array $keys = [])
  152.     {
  153.         if ($this->deferred) {
  154.             $this->commit();
  155.         }
  156.         $ids = [];
  157.         foreach ($keys as $key) {
  158.             $ids[] = $this->getId($key);
  159.         }
  160.         try {
  161.             $items $this->doFetch($ids);
  162.         } catch (\Exception $e) {
  163.             CacheItem::log($this->logger'Failed to fetch requested items', ['keys' => $keys'exception' => $e]);
  164.             $items = [];
  165.         }
  166.         $ids array_combine($ids$keys);
  167.         return $this->generateItems($items$ids);
  168.     }
  169.     /**
  170.      * {@inheritdoc}
  171.      */
  172.     public function save(CacheItemInterface $item)
  173.     {
  174.         if (!$item instanceof CacheItem) {
  175.             return false;
  176.         }
  177.         $this->deferred[$item->getKey()] = $item;
  178.         return $this->commit();
  179.     }
  180.     /**
  181.      * {@inheritdoc}
  182.      */
  183.     public function saveDeferred(CacheItemInterface $item)
  184.     {
  185.         if (!$item instanceof CacheItem) {
  186.             return false;
  187.         }
  188.         $this->deferred[$item->getKey()] = $item;
  189.         return true;
  190.     }
  191.     /**
  192.      * {@inheritdoc}
  193.      */
  194.     public function commit()
  195.     {
  196.         $ok true;
  197.         $byLifetime $this->mergeByLifetime;
  198.         $byLifetime $byLifetime($this->deferred$this->namespace$expiredIds);
  199.         $retry $this->deferred = [];
  200.         if ($expiredIds) {
  201.             $this->doDelete($expiredIds);
  202.         }
  203.         foreach ($byLifetime as $lifetime => $values) {
  204.             try {
  205.                 $e $this->doSave($values$lifetime);
  206.             } catch (\Exception $e) {
  207.             }
  208.             if (true === $e || [] === $e) {
  209.                 continue;
  210.             }
  211.             if (\is_array($e) || === \count($values)) {
  212.                 foreach (\is_array($e) ? $e array_keys($values) as $id) {
  213.                     $ok false;
  214.                     $v $values[$id];
  215.                     $type = \is_object($v) ? \get_class($v) : \gettype($v);
  216.                     CacheItem::log($this->logger'Failed to save key "{key}" ({type})', ['key' => substr($id, \strlen($this->namespace)), 'type' => $type'exception' => $e instanceof \Exception $e null]);
  217.                 }
  218.             } else {
  219.                 foreach ($values as $id => $v) {
  220.                     $retry[$lifetime][] = $id;
  221.                 }
  222.             }
  223.         }
  224.         // When bulk-save failed, retry each item individually
  225.         foreach ($retry as $lifetime => $ids) {
  226.             foreach ($ids as $id) {
  227.                 try {
  228.                     $v $byLifetime[$lifetime][$id];
  229.                     $e $this->doSave([$id => $v], $lifetime);
  230.                 } catch (\Exception $e) {
  231.                 }
  232.                 if (true === $e || [] === $e) {
  233.                     continue;
  234.                 }
  235.                 $ok false;
  236.                 $type = \is_object($v) ? \get_class($v) : \gettype($v);
  237.                 CacheItem::log($this->logger'Failed to save key "{key}" ({type})', ['key' => substr($id, \strlen($this->namespace)), 'type' => $type'exception' => $e instanceof \Exception $e null]);
  238.             }
  239.         }
  240.         return $ok;
  241.     }
  242.     public function __sleep()
  243.     {
  244.         throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
  245.     }
  246.     public function __wakeup()
  247.     {
  248.         throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
  249.     }
  250.     public function __destruct()
  251.     {
  252.         if ($this->deferred) {
  253.             $this->commit();
  254.         }
  255.     }
  256.     private function generateItems($items, &$keys)
  257.     {
  258.         $f $this->createCacheItem;
  259.         try {
  260.             foreach ($items as $id => $value) {
  261.                 if (!isset($keys[$id])) {
  262.                     $id key($keys);
  263.                 }
  264.                 $key $keys[$id];
  265.                 unset($keys[$id]);
  266.                 yield $key => $f($key$valuetrue);
  267.             }
  268.         } catch (\Exception $e) {
  269.             CacheItem::log($this->logger'Failed to fetch requested items', ['keys' => array_values($keys), 'exception' => $e]);
  270.         }
  271.         foreach ($keys as $key) {
  272.             yield $key => $f($keynullfalse);
  273.         }
  274.     }
  275. }