B Pi VP-Gf j xj  g d Rkpd +f #A Pi +oe j d r @pe >@ Pi   `g Pg j hg d @"d  g (5@ Pi   0 g  r j CC d #d @g g Pi r V8+g j j DC d #d h (oD Pi r (g j j DC d $d g `h Pi r 0g Ȁj j  DC d  @%d g `4@ Pi `r (Vg ؀j Ѐj 0DC d &d g F Pi  V <d j j @DC d &4&d d  Pi   <@\j PDC j 8g d >VzA "_j  Pi  ' &lh 0h j `d d bph [h ph Pi   C j j d d  `!C Ьd  h  sV@we ve s ); Gi  indd  @d   +i xh  d +i (^KV`o +i V8pd  +i V +i achmg url e j d 58d  e  +i ttacЧg 80> f j * {@inheritdoc} * * @return bool */ public function hasItem($key) { if (\is_string($key) && isset($this->deferred[$key])) { $this->commit(); } if (!$this->pool->hasItem($key)) { return false; } $itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key); if (!$itemTags->isHit()) { return false; } if (!$itemTags = $itemTags->get()) { return true; } foreach ($this->getTagVersions([$itemTags]) as $tag => $version) { if ($itemTags[$tag] !== $version) { return false; } } return true; } /** * {@inheritdoc} */ public function getItem($key) { foreach ($this->getItems([$key]) as $item) { return $item; } return null; } /** * {@inheritdoc} */ public function getItems(array $keys = []) { $tagKeys = []; $commit = false; foreach ($keys as $key) { if ('' !== $key && \is_string($key)) { $commit = $commit || isset($this->deferred[$key]); $key = static::TAGS_PREFIX.$key; $tagKeys[$key] = $key; } } if ($commit) { $this->commit(); } try { $items = $this->pool->getItems($tagKeys + $keys); } catch (InvalidArgumentException $e) { $this->pool->getItems($keys); // Should throw an exception throw $e; } return $this->generateItems($items, $tagKeys); } /** * {@inheritdoc} * * @return bool */ public function clear(string $prefix = '') { if ('' !== $prefix) { foreach ($this->deferred as $key => $item) { if (str_starts_with($key, $prefix)) { unset($this->deferred[$key]); } } } else { $this->deferred = []; } if ($this->pool instanceof AdapterInterface) { return $this->pool->clear($prefix); } return $this->pool->clear(); } /** * {@inheritdoc} * * @return bool */ public function deleteItem($key) { return $this->deleteItems([$key]); } /** * {@inheritdoc} * * @return bool */ public function deleteItems(array $keys) { foreach ($keys as $key) { if ('' !== $key && \is_string($key)) { $keys[] = static::TAGS_PREFIX.$key; } } return $this->pool->deleteItems($keys); } /** * {@inheritdoc} * * @return bool */ public function save(CacheItemInterface $item) { if (!$item instanceof CacheItem) { return false; } $this->deferred[$item->getKey()] = $item; return $this->commit(); } /** * {@inheritdoc} * * @return bool */ public function saveDeferred(CacheItemInterface $item) { if (!$item instanceof CacheItem) { return false; } $this->deferred[$item->getKey()] = $item; return true; } /** * {@inheritdoc} * * @return bool */ public function commit() { if (!$this->deferred) { return true; } $ok = true; foreach ($this->deferred as $key => $item) { if (!$this->pool->saveDeferred($item)) { unset($this->deferred[$key]); $ok = false; } } $items = $this->deferred; $tagsByKey = (self::$getTagsByKey)($items); $this->deferred = []; $tagVersions = $this->getTagVersions($tagsByKey); $f = self::$createCacheItem; foreach ($tagsByKey as $key => $tags) { $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); } return $this->pool->commit() && $ok; } /** * @return array */ public function __sleep() { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } public function __wakeup() { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } public function __destruct() { $this->commit(); } private function generateItems(iterable $items, array $tagKeys): \Generator { $bufferedItems = $itemTags = []; $f = self::$setCacheItemTags; foreach ($items as $key => $item) { if (!$tagKeys) { yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); continue; } if (!isset($tagKeys[$key])) { $bufferedItems[$key] = $item; continue; } unset($tagKeys[$key]); if ($item->isHit()) { $itemTags[$key] = $item->get() ?: []; } if (!$tagKeys) { $tagVersions = $this->getTagVersions($itemTags); foreach ($itemTags as $key => $tags) { foreach ($tags as $tag => $version) { if ($tagVersions[$tag] !== $version) { unset($itemTags[$key]); continue 2; } } } $tagVersions = $tagKeys = null; foreach ($bufferedItems as $key => $item) { yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); } $bufferedItems = null; } } } private function getTagVersions(array $tagsByKey) { $tagVersions = []; $fetchTagVersions = false; foreach ($tagsByKey as $tags) { $tagVersions += $tags; foreach ($tags as $tag => $version) { if ($tagVersions[$tag] !== $version) { unset($this->knownTagVersions[$tag]); } } } if (!$tagVersions) { return []; } $now = microtime(true); $tags = []; foreach ($tagVersions as $tag => $version) { $tags[$tag.static::TAGS_PREFIX] = $tag; if ($fetchTagVersions || ($this->knownTagVersions[$tag][1] ?? null) !== $version || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) { // reuse previously fetched tag versions up to the ttl $fetchTagVersions = true; } } if (!$fetchTagVersions) { return $tagVersions; } $newTags = []; $newVersion = null; foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { if (!$version->isHit()) { $newTags[$tag] = $version->set($newVersion ?? $newVersion = random_int(\PHP_INT_MIN, \PHP_INT_MAX)); } $tagVersions[$tag = $tags[$tag]] = $version->get(); $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; } if ($newTags) { (self::$saveTags)($this->tags, $newTags); } return $tagVersions; } }