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

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