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

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\Log\LoggerAwareInterface;
  12. use Psr\Log\LoggerInterface;
  13. use Symfony\Component\Cache\CacheItem;
  14. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  15. use Symfony\Component\Cache\ResettableInterface;
  16. use Symfony\Component\Cache\Traits\AbstractAdapterTrait;
  17. use Symfony\Component\Cache\Traits\ContractsTrait;
  18. use Symfony\Contracts\Cache\CacheInterface;
  19. /**
  20.  * @author Nicolas Grekas <p@tchwork.com>
  21.  */
  22. abstract class AbstractAdapter implements AdapterInterfaceCacheInterfaceLoggerAwareInterfaceResettableInterface
  23. {
  24.     use AbstractAdapterTrait;
  25.     use ContractsTrait;
  26.     /**
  27.      * @internal
  28.      */
  29.     protected const NS_SEPARATOR ':';
  30.     private static $apcuSupported;
  31.     private static $phpFilesSupported;
  32.     protected function __construct(string $namespace ''int $defaultLifetime 0)
  33.     {
  34.         $this->namespace '' === $namespace '' CacheItem::validateKey($namespace).static::NS_SEPARATOR;
  35.         $this->defaultLifetime $defaultLifetime;
  36.         if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength 24) {
  37.             throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").'$this->maxIdLength 24, \strlen($namespace), $namespace));
  38.         }
  39.         self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
  40.             static function ($key$value$isHit) {
  41.                 $item = new CacheItem();
  42.                 $item->key $key;
  43.                 $item->value $v $value;
  44.                 $item->isHit $isHit;
  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 = (string) array_key_first($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.         self::$mergeByLifetime ?? self::$mergeByLifetime = \Closure::bind(
  60.             static function ($deferred$namespace, &$expiredIds$getId$defaultLifetime) {
  61.                 $byLifetime = [];
  62.                 $now microtime(true);
  63.                 $expiredIds = [];
  64.                 foreach ($deferred as $key => $item) {
  65.                     $key = (string) $key;
  66.                     if (null === $item->expiry) {
  67.                         $ttl $defaultLifetime $defaultLifetime 0;
  68.                     } elseif (!$item->expiry) {
  69.                         $ttl 0;
  70.                     } elseif (>= $ttl = (int) (0.1 $item->expiry $now)) {
  71.                         $expiredIds[] = $getId($key);
  72.                         continue;
  73.                     }
  74.                     if (isset(($metadata $item->newMetadata)[CacheItem::METADATA_TAGS])) {
  75.                         unset($metadata[CacheItem::METADATA_TAGS]);
  76.                     }
  77.                     // For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators
  78.                     $byLifetime[$ttl][$getId($key)] = $metadata ? ["\x9D".pack('VN', (int) (0.1 $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item->value] : $item->value;
  79.                 }
  80.                 return $byLifetime;
  81.             },
  82.             null,
  83.             CacheItem::class
  84.         );
  85.     }
  86.     /**
  87.      * Returns the best possible adapter that your runtime supports.
  88.      *
  89.      * Using ApcuAdapter makes system caches compatible with read-only filesystems.
  90.      *
  91.      * @return AdapterInterface
  92.      */
  93.     public static function createSystemCache(string $namespaceint $defaultLifetimestring $versionstring $directory, ?LoggerInterface $logger null)
  94.     {
  95.         $opcache = new PhpFilesAdapter($namespace$defaultLifetime$directorytrue);
  96.         if (null !== $logger) {
  97.             $opcache->setLogger($logger);
  98.         }
  99.         if (!self::$apcuSupported self::$apcuSupported ?? ApcuAdapter::isSupported()) {
  100.             return $opcache;
  101.         }
  102.         if (\in_array(\PHP_SAPI, ['cli''phpdbg'], true) && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
  103.             return $opcache;
  104.         }
  105.         $apcu = new ApcuAdapter($namespaceintdiv($defaultLifetime5), $version);
  106.         if (null !== $logger) {
  107.             $apcu->setLogger($logger);
  108.         }
  109.         return new ChainAdapter([$apcu$opcache]);
  110.     }
  111.     public static function createConnection(string $dsn, array $options = [])
  112.     {
  113.         if (str_starts_with($dsn'redis:') || str_starts_with($dsn'rediss:')) {
  114.             return RedisAdapter::createConnection($dsn$options);
  115.         }
  116.         if (str_starts_with($dsn'memcached:')) {
  117.             return MemcachedAdapter::createConnection($dsn$options);
  118.         }
  119.         if (=== strpos($dsn'couchbase:')) {
  120.             if (CouchbaseBucketAdapter::isSupported()) {
  121.                 return CouchbaseBucketAdapter::createConnection($dsn$options);
  122.             }
  123.             return CouchbaseCollectionAdapter::createConnection($dsn$options);
  124.         }
  125.         throw new InvalidArgumentException('Unsupported DSN: it does not start with "redis[s]:", "memcached:" nor "couchbase:".');
  126.     }
  127.     /**
  128.      * {@inheritdoc}
  129.      *
  130.      * @return bool
  131.      */
  132.     public function commit()
  133.     {
  134.         $ok true;
  135.         $byLifetime = (self::$mergeByLifetime)($this->deferred$this->namespace$expiredIds, \Closure::fromCallable([$this'getId']), $this->defaultLifetime);
  136.         $retry $this->deferred = [];
  137.         if ($expiredIds) {
  138.             try {
  139.                 $this->doDelete($expiredIds);
  140.             } catch (\Exception $e) {
  141.                 $ok false;
  142.                 CacheItem::log($this->logger'Failed to delete expired items: '.$e->getMessage(), ['exception' => $e'cache-adapter' => get_debug_type($this)]);
  143.             }
  144.         }
  145.         foreach ($byLifetime as $lifetime => $values) {
  146.             try {
  147.                 $e $this->doSave($values$lifetime);
  148.             } catch (\Exception $e) {
  149.             }
  150.             if (true === $e || [] === $e) {
  151.                 continue;
  152.             }
  153.             if (\is_array($e) || === \count($values)) {
  154.                 foreach (\is_array($e) ? $e array_keys($values) as $id) {
  155.                     $ok false;
  156.                     $v $values[$id];
  157.                     $type get_debug_type($v);
  158.                     $message sprintf('Failed to save key "{key}" of type %s%s'$type$e instanceof \Exception ': '.$e->getMessage() : '.');
  159.                     CacheItem::log($this->logger$message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception $e null'cache-adapter' => get_debug_type($this)]);
  160.                 }
  161.             } else {
  162.                 foreach ($values as $id => $v) {
  163.                     $retry[$lifetime][] = $id;
  164.                 }
  165.             }
  166.         }
  167.         // When bulk-save failed, retry each item individually
  168.         foreach ($retry as $lifetime => $ids) {
  169.             foreach ($ids as $id) {
  170.                 try {
  171.                     $v $byLifetime[$lifetime][$id];
  172.                     $e $this->doSave([$id => $v], $lifetime);
  173.                 } catch (\Exception $e) {
  174.                 }
  175.                 if (true === $e || [] === $e) {
  176.                     continue;
  177.                 }
  178.                 $ok false;
  179.                 $type get_debug_type($v);
  180.                 $message sprintf('Failed to save key "{key}" of type %s%s'$type$e instanceof \Exception ': '.$e->getMessage() : '.');
  181.                 CacheItem::log($this->logger$message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception $e null'cache-adapter' => get_debug_type($this)]);
  182.             }
  183.         }
  184.         return $ok;
  185.     }
  186. }