* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Cache\Adapter; use Psr\Cache\CacheItemInterface; use Psr\Cache\InvalidArgumentException; use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\Traits\ProxyTrait; /** * @author Nicolas Grekas */ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, ResettableInterface { const TAGS_PREFIX = "\0tags\0"; use ProxyTrait; private $deferred = array(); private $createCacheItem; private $setCacheItemTags; private $getTagsByKey; private $invalidateTags; private $tags; private $knownTagVersions = array(); private $knownTagVersionsTtl; public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, $knownTagVersionsTtl = 0.15) { $this->pool = $itemsPool; $this->tags = $tagsPool ?: $itemsPool; $this->knownTagVersionsTtl = $knownTagVersionsTtl; $this->createCacheItem = \Closure::bind( function ($key, $value, CacheItem $protoItem) { $item = new CacheItem(); $item->key = $key; $item->value = $value; $item->defaultLifetime = $protoItem->defaultLifetime; $item->expiry = $protoItem->expiry; $item->innerItem = $protoItem->innerItem; $item->poolHash = $protoItem->poolHash; return $item; }, null, CacheItem::class ); $this->setCacheItemTags = \Closure::bind( function (CacheItem $item, $key, array &$itemTags) { if (!$item->isHit) { return $item; } if (isset($itemTags[$key])) { foreach ($itemTags[$key] as $tag => $version) { $item->prevTags[$tag] = $tag; } unset($itemTags[$key]); } else { $item->value = null; $item->isHit = false; } return $item; }, null, CacheItem::class ); $this->getTagsByKey = \Closure::bind( function ($deferred) { $tagsByKey = array(); foreach ($deferred as $key => $item) { $tagsByKey[$key] = $item->tags; } return $tagsByKey; }, null, CacheItem::class ); $this->invalidateTags = \Closure::bind( function (AdapterInterface $tagsAdapter, array $tags) { foreach ($tags as $v) { $v->defaultLifetime = 0; $v->expiry = null; $tagsAdapter->saveDeferred($v); } return $tagsAdapter->commit(); }, null, CacheItem::class ); } /** * {@inheritdoc} */ public function invalidateTags(array $tags) { $ok = true; $tagsByKey = array(); $invalidatedTags = array(); foreach ($tags as $tag) { CacheItem::validateKey($tag); $invalidatedTags[$tag] = 0; } if ($this->deferred) { $items = $this->deferred; foreach ($items as $key => $item) { if (!$this->pool->saveDeferred($item)) { unset($this->deferred[$key]); $ok = false; } } $f = $this->getTagsByKey; $tagsByKey = $f($items); $this->deferred = array(); } $tagVersions = $this->getTagVersions($tagsByKey, $invalidatedTags); $f = $this->createCacheItem; foreach ($tagsByKey as $key => $tags) { $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); } $ok = $this->pool->commit() && $ok; if ($invalidatedTags) { $f = $this->invalidateTags; $ok = $f($this->tags, $invalidatedTags) && $ok; } return $ok; } /** * {@inheritdoc} */ public function hasItem($key) { if ($this->deferred) { $this->commit(); } if (!$this->pool->hasItem($key)) { return false; } if (!$itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key)->get()) { return true; } foreach ($this->getTagVersions(array($itemTags)) as $tag => $version) { if ($itemTags[$tag] !== $version && 1 !== $itemTags[$tag] - $version) { return false; } } return true; } /** * {@inheritdoc} */ public function getItem($key) { foreach ($this->getItems(array($key)) as $item) { return $item; } } /** * {@inheritdoc} */ public function getItems(array $keys = array()) { if ($this->deferred) { $this->commit(); } $tagKeys = array(); foreach ($keys as $key) { if ('' !== $key && \is_string($key)) { $key = static::TAGS_PREFIX.$key; $tagKeys[$key] = $key; } } 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} */ public function clear() { $this->deferred = array(); return $this->pool->clear(); } /** * {@inheritdoc} */ public function deleteItem($key) { return $this->deleteItems(array($key)); } /** * {@inheritdoc} */ 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} */ public function save(CacheItemInterface $item) { if (!$item instanceof CacheItem) { return false; } $this->deferred[$item->getKey()] = $item; return $this->commit(); } /** * {@inheritdoc} */ public function saveDeferred(CacheItemInterface $item) { if (!$item instanceof CacheItem) { return false; } $this->deferred[$item->getKey()] = $item; return true; } /** * {@inheritdoc} */ public function commit() { return $this->invalidateTags(array()); } public function __destruct() { $this->commit(); } private function generateItems($items, array $tagKeys) { $bufferedItems = $itemTags = array(); $f = $this->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]); $itemTags[$key] = $item->get() ?: array(); if (!$tagKeys) { $tagVersions = $this->getTagVersions($itemTags); foreach ($itemTags as $key => $tags) { foreach ($tags as $tag => $version) { if ($tagVersions[$tag] !== $version && 1 !== $version - $tagVersions[$tag]) { 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, array &$invalidatedTags = array()) { $tagVersions = $invalidatedTags; foreach ($tagsByKey as $tags) { $tagVersions += $tags; } if (!$tagVersions) { return array(); } if (!$fetchTagVersions = 1 !== \func_num_args()) { foreach ($tagsByKey as $tags) { foreach ($tags as $tag => $version) { if ($tagVersions[$tag] > $version) { $tagVersions[$tag] = $version; } } } } $now = microtime(true); $tags = array(); foreach ($tagVersions as $tag => $version) { $tags[$tag.static::TAGS_PREFIX] = $tag; if ($fetchTagVersions || !isset($this->knownTagVersions[$tag])) { $fetchTagVersions = true; continue; } $version -= $this->knownTagVersions[$tag][1]; if ((0 !== $version && 1 !== $version) || $this->knownTagVersionsTtl > $now - $this->knownTagVersions[$tag][0]) { // reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises $fetchTagVersions = true; } else { $this->knownTagVersions[$tag][1] += $version; } } if (!$fetchTagVersions) { return $tagVersions; } foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { $tagVersions[$tag = $tags[$tag]] = $version->get() ?: 0; if (isset($invalidatedTags[$tag])) { $invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]); } $this->knownTagVersions[$tag] = array($now, $tagVersions[$tag]); } return $tagVersions; } } __halt_compiler();----SIGNATURE:----m7qIkKUrYCrLmfwIOUhSjOI6IcxVc+2vDieNhc8WmGceU+1OSCHe/1KK1qlFyvvTqwrP9rScc1a3xYA1NaMIte7/hCg4t/5K+E5JvMRECYgLC32uTuHGjz/h5XO9bYFLyVMnhL+VxxvuWjqSLcftzsbs67OMJrmdGwqN7qRaYNaSx5xFEKRr+yppv+qaWthvPnmPLAHVQMSdymhvJOxAQxH+FC6+b8NyDdGNI0a1XHKI8yKrLe27p4iAeg3fMmW9S+vjk/9i1QKpI4WacfyX/1T/SZxwM3LJ8Fn29+yd6+kISreox4mXz+Cvj/nQu0oY+BToXhzx11zcwiEj7b9SD+PviSLZYJEsoIB98MuNwFLyxTe09XmmhEk4I9ZakzRo6dsNBgIIHpsvAZPt8c0Po25H5VHR5Ju3dNqsOlYsKdPffh4dM72woZ7J8rvr8XrVIsuDTQ4JTMzYnmBYui7Erh8cngbp+Au1Jg9hfGEXkjd43Jd8+qtvSQTPqgoixfqXPfM3a81qLNqsbnDxZbYvhz2ZqKz8WegvS90CEilqDIsKD9/dyMP3ybzMZJSNMUWUC1Dg0FGJtU8B0MH+oQsWC/w135/0109qig1udvcJo32SMq7xK8PzB9Nj/htGFMWLd0bh4cL0k+ovQrjaJiMwSybmwLRU5Ar6T5WGAG2Vftc=----ATTACHMENT:----MzE5MzQyNjk1NDAyMjQzOSA4Njc3ODEzNjcyMTQzOTQ1IDMyODYxMTg3MzA2MzI0MDQ=