Вход Регистрация
Файл: vendor/symfony/cache/Adapter/TagAwareAdapter.php
Строк: 351
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SymfonyComponentCacheAdapter;

use 
PsrCacheCacheItemInterface;
use 
PsrCacheInvalidArgumentException;
use 
PsrLogLoggerAwareInterface;
use 
PsrLogLoggerAwareTrait;
use 
SymfonyComponentCacheCacheItem;
use 
SymfonyComponentCachePruneableInterface;
use 
SymfonyComponentCacheResettableInterface;
use 
SymfonyComponentCacheTraitsContractsTrait;
use 
SymfonyContractsCacheTagAwareCacheInterface;

/**
 * Implements simple and robust tag-based invalidation suitable for use with volatile caches.
 *
 * This adapter works by storing a version for each tags. When saving an item, it is stored together with its tags and
 * their corresponding versions. When retrieving an item, those tag versions are compared to the current version of
 * each tags. Invalidation is achieved by deleting tags, thereby ensuring that their versions change even when the
 * storage is out of space. When versions of non-existing tags are requested for item commits, this adapter assigns a
 * new random version to them.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 * @author Sergey Belyshkin <sbelyshkin@gmail.com>
 */
class TagAwareAdapter implements TagAwareAdapterInterfaceTagAwareCacheInterfacePruneableInterfaceResettableInterfaceLoggerAwareInterface
{
    use 
ContractsTrait;
    use 
LoggerAwareTrait;

    public const 
TAGS_PREFIX "1tags1";

    private array 
$deferred = [];
    private 
AdapterInterface $pool;
    private 
AdapterInterface $tags;
    private array 
$knownTagVersions = [];
    private 
float $knownTagVersionsTtl;

    private static 
Closure $setCacheItemTags;
    private static 
Closure $setTagVersions;
    private static 
Closure $getTagsByKey;
    private static 
Closure $saveTags;

    public function 
__construct(AdapterInterface $itemsPool, ?AdapterInterface $tagsPool nullfloat $knownTagVersionsTtl 0.15)
    {
        
$this->pool $itemsPool;
        
$this->tags $tagsPool ?? $itemsPool;
        
$this->knownTagVersionsTtl $knownTagVersionsTtl;
        
self::$setCacheItemTags ??= Closure::bind(
            static function (array 
$items, array $itemTags) {
                foreach (
$items as $key => $item) {
                    
$item->isTaggable true;

                    if (isset(
$itemTags[$key])) {
                        
$tags array_keys($itemTags[$key]);
                        
$item->metadata[CacheItem::METADATA_TAGS] = array_combine($tags$tags);
                    } else {
                        
$item->value null;
                        
$item->isHit false;
                        
$item->metadata = [];
                    }
                }

                return 
$items;
            },
            
null,
            
CacheItem::class
        );
        
self::$setTagVersions ??= Closure::bind(
            static function (array 
$items, array $tagVersions) {
                foreach (
$items as $item) {
                    
$item->newMetadata[CacheItem::METADATA_TAGS] = array_intersect_key($tagVersions$item->newMetadata[CacheItem::METADATA_TAGS] ?? []);
                }
            },
            
null,
            
CacheItem::class
        );
        
self::$getTagsByKey ??= Closure::bind(
            static function (
$deferred) {
                
$tagsByKey = [];
                foreach (
$deferred as $key => $item) {
                    
$tagsByKey[$key] = $item->newMetadata[CacheItem::METADATA_TAGS] ?? [];
                    
$item->metadata $item->newMetadata;
                }

                return 
$tagsByKey;
            },
            
null,
            
CacheItem::class
        );
        
self::$saveTags ??= Closure::bind(
            static function (
AdapterInterface $tagsAdapter, array $tags) {
                
ksort($tags);

                foreach (
$tags as $v) {
                    
$v->expiry 0;
                    
$tagsAdapter->saveDeferred($v);
                }

                return 
$tagsAdapter->commit();
            },
            
null,
            
CacheItem::class
        );
    }

    public function 
invalidateTags(array $tags): bool
    
{
        
$ids = [];
        foreach (
$tags as $tag) {
            
assert('' !== CacheItem::validateKey($tag));
            unset(
$this->knownTagVersions[$tag]);
            
$ids[] = $tag.static::TAGS_PREFIX;
        }

        return !
$tags || $this->tags->deleteItems($ids);
    }

    public function 
hasItem(mixed $key): bool
    
{
        return 
$this->getItem($key)->isHit();
    }

    public function 
getItem(mixed $key): CacheItem
    
{
        foreach (
$this->getItems([$key]) as $item) {
            return 
$item;
        }
    }

    public function 
getItems(array $keys = []): iterable
    
{
        
$tagKeys = [];
        
$commit false;

        foreach (
$keys as $key) {
            if (
'' !== $key && is_string($key)) {
                
$commit $commit || isset($this->deferred[$key]);
            }
        }

        if (
$commit) {
            
$this->commit();
        }

        try {
            
$items $this->pool->getItems($keys);
        } catch (
InvalidArgumentException $e) {
            
$this->pool->getItems($keys); // Should throw an exception

            
throw $e;
        }

        
$bufferedItems $itemTags = [];

        foreach (
$items as $key => $item) {
            if (
null !== $tags $item->getMetadata()[CacheItem::METADATA_TAGS] ?? null) {
                
$itemTags[$key] = $tags;
            }

            
$bufferedItems[$key] = $item;

            if (
null === $tags) {
                
$key "tags".$key;
                
$tagKeys[$key] = $key// BC with pools populated before v6.1
            
}
        }

        if (
$tagKeys) {
            foreach (
$this->pool->getItems($tagKeys) as $key => $item) {
                if (
$item->isHit()) {
                    
$itemTags[substr($keystrlen("tags"))] = $item->get() ?: [];
                }
            }
        }

        
$tagVersions $this->getTagVersions($itemTagsfalse);
        foreach (
$itemTags as $key => $tags) {
            foreach (
$tags as $tag => $version) {
                if (
$tagVersions[$tag] !== $version) {
                    unset(
$itemTags[$key]);
                    continue 
2;
                }
            }
        }
        
$tagVersions null;

        return (
self::$setCacheItemTags)($bufferedItems$itemTags);
    }

    public function 
clear(string $prefix ''): bool
    
{
        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();
    }

    public function 
deleteItem(mixed $key): bool
    
{
        return 
$this->deleteItems([$key]);
    }

    public function 
deleteItems(array $keys): bool
    
{
        foreach (
$keys as $key) {
            if (
'' !== $key && is_string($key)) {
                
$keys[] = "tags".$key// BC with pools populated before v6.1
            
}
        }

        return 
$this->pool->deleteItems($keys);
    }

    public function 
save(CacheItemInterface $item): bool
    
{
        if (!
$item instanceof CacheItem) {
            return 
false;
        }
        
$this->deferred[$item->getKey()] = $item;

        return 
$this->commit();
    }

    public function 
saveDeferred(CacheItemInterface $item): bool
    
{
        if (!
$item instanceof CacheItem) {
            return 
false;
        }
        
$this->deferred[$item->getKey()] = $item;

        return 
true;
    }

    public function 
commit(): bool
    
{
        if (!
$items $this->deferred) {
            return 
true;
        }

        
$tagVersions $this->getTagVersions((self::$getTagsByKey)($items), true);
        (
self::$setTagVersions)($items$tagVersions);

        
$ok true;
        foreach (
$items as $key => $item) {
            if (
$this->pool->saveDeferred($item)) {
                unset(
$this->deferred[$key]);
            } else {
                
$ok false;
            }
        }
        
$ok $this->pool->commit() && $ok;

        
$tagVersions array_keys($tagVersions);
        (
self::$setTagVersions)($itemsarray_combine($tagVersions$tagVersions));

        return 
$ok;
    }

    public function 
prune(): bool
    
{
        return 
$this->pool instanceof PruneableInterface && $this->pool->prune();
    }

    
/**
     * @return void
     */
    
public function reset()
    {
        
$this->commit();
        
$this->knownTagVersions = [];
        
$this->pool instanceof ResettableInterface && $this->pool->reset();
        
$this->tags instanceof ResettableInterface && $this->tags->reset();
    }

    public function 
__sleep(): array
    {
        throw new 
BadMethodCallException('Cannot serialize '.__CLASS__);
    }

    
/**
     * @return void
     */
    
public function __wakeup()
    {
        throw new 
BadMethodCallException('Cannot unserialize '.__CLASS__);
    }

    public function 
__destruct()
    {
        
$this->commit();
    }

    private function 
getTagVersions(array $tagsByKeybool $persistTags): array
    {
        
$tagVersions = [];
        
$fetchTagVersions $persistTags;

        foreach (
$tagsByKey as $tags) {
            
$tagVersions += $tags;
            if (
$fetchTagVersions) {
                continue;
            }
            foreach (
$tags as $tag => $version) {
                if (
$tagVersions[$tag] !== $version) {
                    
$fetchTagVersions true;
                }
            }
        }

        if (!
$tagVersions) {
            return [];
        }

        
$now microtime(true);
        
$tags = [];
        foreach (
$tagVersions as $tag => $version) {
            
$tags[$tag.static::TAGS_PREFIX] = $tag;
            
$knownTagVersion $this->knownTagVersions[$tag] ?? [0null];
            if (
$fetchTagVersions || $now $knownTagVersion[0] || $knownTagVersion[1] !== $version) {
                
// reuse previously fetched tag versions until the expiration
                
$fetchTagVersions true;
            }
        }

        if (!
$fetchTagVersions) {
            return 
$tagVersions;
        }

        
$newTags = [];
        
$newVersion null;
        
$expiration $now $this->knownTagVersionsTtl;
        foreach (
$this->tags->getItems(array_keys($tags)) as $tag => $version) {
            unset(
$this->knownTagVersions[$tag $tags[$tag]]); // update FIFO
            
if (null !== $tagVersions[$tag] = $version->get()) {
                
$this->knownTagVersions[$tag] = [$expiration$tagVersions[$tag]];
            } elseif (
$persistTags) {
                
$newTags[$tag] = $version->set($newVersion ??= random_bytes(6));
                
$tagVersions[$tag] = $newVersion;
                
$this->knownTagVersions[$tag] = [$expiration$newVersion];
            }
        }

        if (
$newTags) {
            (
self::$saveTags)($this->tags$newTags);
        }

        while (
$now > ($this->knownTagVersions[$tag array_key_first($this->knownTagVersions)][0] ?? INF)) {
            unset(
$this->knownTagVersions[$tag]);
        }

        return 
$tagVersions;
    }
}
Онлайн: 0
Реклама