在Symfony 3.1缓存组件中缓存失效

时间:2017-05-16 17:20:31

标签: php symfony caching symfony-3.1 cache-invalidation

我使用Symfony 3.1的cache component为我的实体存储一些自定义元数据。我想在与这些元数据相关联的任何文件中进行更改后立即使其无效。

我没有办法告诉Symfony或缓存组件关注特定文件集中的更改,我遗漏了什么?

在我用来创建缓存项池的代码下面:

<?php
class MetadataCacheFactory implements MetadataCacheFactoryInterface
{
    const CACHE_NAMESPACE = 'my_namespace';

    /** @var string */
    protected $cacheDir;

    public function __construct(KernelInterface $kernel)
    {
        $this->cacheDir = $kernel->getCacheDir();
    }

    /**
     * {@inheritdoc}
     */
    public function create(): CacheItemPoolInterface
    {
        return AbstractAdapter::createSystemCache(self::CACHE_NAMESPACE, 0, null, $this->cacheDir);
    }
}

使用它的代码:

<?php
class ExampleMetadataFactory implements MetadataFactoryInterface
{
    const CACHE_KEY = 'example_metadata';

    [...]

    /** @var ExampleMetadata */
    protected $metadata;

    public function __construct(MetadataCacheFactoryInterface $cacheFactory)
    {
        $this->cache = $cacheFactory->create();
        $this->metadata = null;
    }

    /**
     * {@inheritdoc}
     */
    public function create(): ExampleMetadata
    {
        if ($this->metadata !== null) {
            return $this->metadata;
        }
        try {
            $cacheItem = $this->cache->getItem(md5(self::CACHE_KEY));
            if ($cacheItem->isHit()) {
                return $cacheItem->get();
            }
        } catch (CacheException $e) {
            // Ignore
        }
        $this->metadata = $this->createMetadata();
        if (!isset($cacheItem)) {
            return $this->metadata;
        }
        $cacheItem->set($this->metadata);
        $this->cache->save($cacheItem);
        return $this->metadata;
    }
}

当我在运行时查看代码时,AbstractAdapter选择给我一个PhpFilesAdapter(我认为在开发中足够公平)。

但是当我查看这个适配器的代码时,我发现了这个:

<?php
protected function doFetch(array $ids) {
    [...]

    foreach ($ids as $id) {
        try {
            $file = $this->getFile($id);
            list($expiresAt, $values[$id]) = include $file;
            if ($now >= $expiresAt) {
                unset($values[$id]);
            }
        } catch (\Exception $e) {
            continue;
        }
    }
}

因此除了过期日期之外,没有任何逻辑可以检查到期日期。

您是否知道在文件更改时使缓存无效的方法(当然在开发环境中)?或者我自己实施它??

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

我找到了一个解决方案:ConfigCache。您可以为其提供一组要监视的资源,并在每次读取缓存时检查它们的上次修改时间。

上面的例子变成了:

<?php
class MetadataCacheFactory implements MetadataCacheFactoryInterface
{
    const CACHE_NAMESPACE = 'my_namespace';

    /** @var \Symfony\Component\HttpKernel\KernelInterface */
    protected $kernel;

    public function __construct(KernelInterface $kernel)
    {
        $this->kernel = $kernel;
    }

    /**
     * {@inheritdoc}
     */
    public function create(): ConfigCache
    {
        return new ConfigCache($this->kernel->getCacheDir().'/'.self::CACHE_NAMESPACE, $this->kernel->isDebug());
    }
}

使用它的代码:

<?php
class ExampleMetadataFactory implements MetadataFactoryInterface
{
    const CACHE_KEY = 'example_metadata';

    [...]

    /** @var ExampleMetadata */
    protected $metadata;

    public function __construct(MetadataCacheFactoryInterface $cacheFactory)
    {
        $this->cache = $cacheFactory->create();
        $this->metadata = null;
    }

    /**
     * {@inheritdoc}
     */
    public function create(): ExampleMetadata
    {
        if ($this->metadata !== null) {
            return $this->metadata;
        }
        try {
            if ($this->cache->isFresh()) {
                $this->metadata = unserialize(file_get_contents($this->cache->getPath()));
                return $this->metadata;
            }
        } catch (CacheException $e) {
            // Ignore
        }
        $resources = [];
        $this->metadata = $this->createMetadata($resources);
        $this->cache->write(serialize($this->metadata), $resources);
        return $this->metadata;
    }

    /**
     * Creates the metadata.
     *
     * @param array $resources
     *
     * @return ExampleMetadata
     */
    protected function createMetadata(&$resources): ExampleMetadata
    {
        $metadata = new ExampleMetadata();
        $resourcesCollection = $this->resourcesCollectionFactory->create();
        foreach ($resourcesCollection as $resourceClass => $resourcePath) {
            // The important part, it's an instance of \Symfony\Component\Config\Resource\FileResource.
            $resources[] = new FileResource($resourcePath);
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
            $metadata->registerResourceMetadata($resourceMetadata);
        }
        return $metadata;
    }
}

希望它可以帮助别人。