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

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