.. index::
single: Cache
Cache
=====
Using a cache is a great way of making your application run quicker. The Symfony cache
component ships with many adapters to different storages. Every adapter is
developed for high performance.
The following example shows a typical usage of the cache::
use Symfony\Contracts\Cache\ItemInterface;
// The callable will only be executed on a cache miss.
$value = $pool->get('my_cache_key', function (ItemInterface $item) {
$item->expiresAfter(3600);
// ... do some HTTP request or heavy computations
$computedValue = 'foobar';
return $computedValue;
});
echo $value; // 'foobar'
// ... and to remove the cache key
$pool->delete('my_cache_key');
Symfony supports Cache Contracts, PSR-6/16 and Doctrine Cache interfaces.
You can read more about these at the :doc:`component documentation `.
.. _cache-configuration-with-frameworkbundle:
Configuring Cache with FrameworkBundle
--------------------------------------
When configuring the cache component there are a few concepts you should know
of:
**Pool**
This is a service that you will interact with. Each pool will always have
its own namespace and cache items. There is never a conflict between pools.
**Adapter**
An adapter is a *template* that you use to create pools.
**Provider**
A provider is a service that some adapters use to connect to the storage.
Redis and Memcached are example of such adapters. If a DSN is used as the
provider then a service is automatically created.
There are two pools that are always enabled by default. They are ``cache.app`` and
``cache.system``. The system cache is used for things like annotations, serializer,
and validation. The ``cache.app`` can be used in your code. You can configure which
adapter (template) they use by using the ``app`` and ``system`` key like:
.. configuration-block::
.. code-block:: yaml
# config/packages/cache.yaml
framework:
cache:
app: cache.adapter.filesystem
system: cache.adapter.system
.. code-block:: xml
.. code-block:: php
// config/packages/cache.php
$container->loadFromExtension('framework', [
'cache' => [
'app' => 'cache.adapter.filesystem',
'system' => 'cache.adapter.system',
],
]);
The Cache component comes with a series of adapters pre-configured:
* :doc:`cache.adapter.apcu `
* :doc:`cache.adapter.array `
* :doc:`cache.adapter.doctrine `
* :doc:`cache.adapter.filesystem `
* :doc:`cache.adapter.memcached `
* :doc:`cache.adapter.pdo `
* :doc:`cache.adapter.psr6 `
* :doc:`cache.adapter.redis `
Some of these adapters could be configured via shortcuts. Using these shortcuts
will create pools with service IDs that follow the pattern ``cache.[type]``.
.. configuration-block::
.. code-block:: yaml
# config/packages/cache.yaml
framework:
cache:
directory: '%kernel.cache_dir%/pools' # Only used with cache.adapter.filesystem
# service: cache.doctrine
default_doctrine_provider: 'app.doctrine_cache'
# service: cache.psr6
default_psr6_provider: 'app.my_psr6_service'
# service: cache.redis
default_redis_provider: 'redis://localhost'
# service: cache.memcached
default_memcached_provider: 'memcached://localhost'
# service: cache.pdo
default_pdo_provider: 'doctrine.dbal.default_connection'
.. code-block:: xml
.. code-block:: php
// config/packages/cache.php
$container->loadFromExtension('framework', [
'cache' => [
// Only used with cache.adapter.filesystem
'directory' => '%kernel.cache_dir%/pools',
// Service: cache.doctrine
'default_doctrine_provider' => 'app.doctrine_cache',
// Service: cache.psr6
'default_psr6_provider' => 'app.my_psr6_service',
// Service: cache.redis
'default_redis_provider' => 'redis://localhost',
// Service: cache.memcached
'default_memcached_provider' => 'memcached://localhost',
// Service: cache.pdo
'default_pdo_provider' => 'doctrine.dbal.default_connection',
],
]);
Creating Custom (Namespaced) Pools
----------------------------------
You can also create more customized pools:
.. configuration-block::
.. code-block:: yaml
# config/packages/cache.yaml
framework:
cache:
default_memcached_provider: 'memcached://localhost'
pools:
# creates a "custom_thing.cache" service
# autowireable via "CacheInterface $customThingCache"
# uses the "app" cache configuration
custom_thing.cache:
adapter: cache.app
# creates a "my_cache_pool" service
# autowireable via "CacheInterface $myCachePool"
my_cache_pool:
adapter: cache.adapter.filesystem
# uses the default_memcached_provider from above
acme.cache:
adapter: cache.adapter.memcached
# control adapter's configuration
foobar.cache:
adapter: cache.adapter.memcached
provider: 'memcached://user:password@example.com'
# uses the "foobar.cache" pool as its backend but controls
# the lifetime and (like all pools) has a separate cache namespace
short_cache:
adapter: foobar.cache
default_lifetime: 60
.. code-block:: xml
.. code-block:: php
// config/packages/cache.php
$container->loadFromExtension('framework', [
'cache' => [
'default_memcached_provider' => 'memcached://localhost',
'pools' => [
// creates a "custom_thing.cache" service
// autowireable via "CacheInterface $customThingCache"
// uses the "app" cache configuration
'custom_thing.cache' => [
'adapter' => 'cache.app',
],
// creates a "my_cache_pool" service
// autowireable via "CacheInterface $myCachePool"
'my_cache_pool' => [
'adapter' => 'cache.adapter.filesystem',
],
// uses the default_memcached_provider from above
'acme.cache' => [
'adapter' => 'cache.adapter.memcached',
],
// control adapter's configuration
'foobar.cache' => [
'adapter' => 'cache.adapter.memcached',
'provider' => 'memcached://user:password@example.com',
],
// uses the "foobar.cache" pool as its backend but controls
// the lifetime and (like all pools) has a separate cache namespace
'short_cache' => [
'adapter' => 'foobar.cache',
'default_lifetime' => 60,
],
],
],
]);
Each pool manages a set of independent cache keys: keys from different pools
*never* collide, even if they share the same backend. This is achieved by prefixing
keys with a namespace that's generated by hashing the name of the pool, the name
of the compiled container class and a :ref:`configurable seed`
that defaults to the project directory.
Each custom pool becomes a service whose service ID is the name of the pool
(e.g. ``custom_thing.cache``). An autowiring alias is also created for each pool
using the camel case version of its name - e.g. ``custom_thing.cache`` can be
injected automatically by naming the argument ``$customThingCache`` and type-hinting it
with either :class:`Symfony\\Contracts\\Cache\\CacheInterface` or
``Psr\Cache\CacheItemPoolInterface``::
use Symfony\Contracts\Cache\CacheInterface;
// from a controller method
public function listProducts(CacheInterface $customThingCache)
{
// ...
}
// in a service
public function __construct(CacheInterface $customThingCache)
{
// ...
}
Custom Provider Options
-----------------------
Some providers have specific options that can be configured. The
:doc:`RedisAdapter ` allows you to
create providers with the options ``timeout``, ``retry_interval``. etc. To use these
options with non-default values you need to create your own ``\Redis`` provider
and use that when configuring the pool.
.. configuration-block::
.. code-block:: yaml
# config/packages/cache.yaml
framework:
cache:
pools:
cache.my_redis:
adapter: cache.adapter.redis
provider: app.my_custom_redis_provider
services:
app.my_custom_redis_provider:
class: \Redis
factory: ['Symfony\Component\Cache\Adapter\RedisAdapter', 'createConnection']
arguments:
- 'redis://localhost'
- { retry_interval: 2, timeout: 10 }
.. code-block:: xml
redis://localhost
2
10
.. code-block:: php
// config/packages/cache.php
use Symfony\Component\Cache\Adapter\RedisAdapter;
$container->loadFromExtension('framework', [
'cache' => [
'pools' => [
'cache.my_redis' => [
'adapter' => 'cache.adapter.redis',
'provider' => 'app.my_custom_redis_provider',
],
],
],
]);
$container->register('app.my_custom_redis_provider', \Redis::class)
->setFactory([RedisAdapter::class, 'createConnection'])
->addArgument('redis://localhost')
->addArgument([
'retry_interval' => 2,
'timeout' => 10
])
;
Creating a Cache Chain
----------------------
Different cache adapters have different strengths and weaknesses. Some might be
really quick but optimized to store small items and some may be able to contain
a lot of data but are quite slow. To get the best of both worlds you may use a
chain of adapters.
A cache chain combines several cache pools into a single one. When storing an
item in a cache chain, Symfony stores it in all pools sequentially. When
retrieving an item, Symfony tries to get it from the first pool. If it's not
found, it tries the next pools until the item is found or an exception is thrown.
Because of this behavior, it's recommended to define the adapters in the chain
in order from fastest to slowest.
If an error happens when storing an item in a pool, Symfony stores it in the
other pools and no exception is thrown. Later, when the item is retrieved,
Symfony stores the item automatically in all the missing pools.
.. configuration-block::
.. code-block:: yaml
# config/packages/cache.yaml
framework:
cache:
pools:
my_cache_pool:
default_lifetime: 31536000 # One year
adapters:
- cache.adapter.array
- cache.adapter.apcu
- {name: cache.adapter.redis, provider: 'redis://user:password@example.com'}
.. code-block:: xml
.. code-block:: php
// config/packages/cache.php
$container->loadFromExtension('framework', [
'cache' => [
'pools' => [
'my_cache_pool' => [
'default_lifetime' => 31536000, // One year
'adapters' => [
'cache.adapter.array',
'cache.adapter.apcu',
['name' => 'cache.adapter.redis', 'provider' => 'redis://user:password@example.com'],
],
],
],
],
]);
Using Cache Tags
----------------
In applications with many cache keys it could be useful to organize the data stored
to be able to invalidate the cache more efficiently. One way to achieve that is to
use cache tags. One or more tags could be added to the cache item. All items with
the same key could be invalidated with one function call::
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
class SomeClass
{
private $myCachePool;
// using autowiring to inject the cache pool
public function __construct(TagAwareCacheInterface $myCachePool)
{
$this->myCachePool = $myCachePool;
}
public function someMethod()
{
$value0 = $this->myCachePool->get('item_0', function (ItemInterface $item) {
$item->tag(['foo', 'bar']);
return 'debug';
});
$value1 = $this->myCachePool->get('item_1', function (ItemInterface $item) {
$item->tag('foo');
return 'debug';
});
// Remove all cache keys tagged with "bar"
$this->myCachePool->invalidateTags(['bar']);
}
}
The cache adapter needs to implement :class:`Symfony\\Contracts\\Cache\\TagAwareCacheInterface`
to enable this feature. This could be added by using the following configuration.
.. configuration-block::
.. code-block:: yaml
# config/packages/cache.yaml
framework:
cache:
pools:
my_cache_pool:
adapter: cache.adapter.redis
tags: true
.. code-block:: xml
.. code-block:: php
// config/packages/cache.php
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\DependencyInjection\Reference;
$container->loadFromExtension('framework', [
'cache' => [
'pools' => [
'my_cache_pool' => [
'adapter' => 'cache.adapter.redis',
'tags' => true,
],
],
],
]);
Tags are stored in the same pool by default. This is good in most scenarios. But
sometimes it might be better to store the tags in a different pool. That could be
achieved by specifying the adapter.
.. configuration-block::
.. code-block:: yaml
# config/packages/cache.yaml
framework:
cache:
pools:
my_cache_pool:
adapter: cache.adapter.redis
tags: tag_pool
tag_pool:
adapter: cache.adapter.apcu
.. code-block:: xml
.. code-block:: php
// config/packages/cache.php
$container->loadFromExtension('framework', [
'cache' => [
'pools' => [
'my_cache_pool' => [
'adapter' => 'cache.adapter.redis',
'tags' => 'tag_pool',
],
'tag_pool' => [
'adapter' => 'cache.adapter.apcu',
],
],
],
]);
.. note::
The interface :class:`Symfony\\Contracts\\Cache\\TagAwareCacheInterface` is
autowired to the ``cache.app`` service.
Clearing the Cache
------------------
To clear the cache you can use the ``bin/console cache:pool:clear [pool]`` command.
That will remove all the entries from your storage and you will have to recalculate
all the values. You can also group your pools into "cache clearers". There are 3 cache
clearers by default:
* ``cache.global_clearer``
* ``cache.system_clearer``
* ``cache.app_clearer``
The global clearer clears all the cache items in every pool. The system cache clearer
is used in the ``bin/console cache:clear`` command. The app clearer is the default
clearer.
To see all available cache pools:
.. code-block:: terminal
$ php bin/console cache:pool:list
Clear one pool:
.. code-block:: terminal
$ php bin/console cache:pool:clear my_cache_pool
Clear all custom pools:
.. code-block:: terminal
$ php bin/console cache:pool:clear cache.app_clearer
Clear all caches everywhere:
.. code-block:: terminal
$ php bin/console cache:pool:clear cache.global_clearer