Cache
Using cache is a great way of making your application run quicker. The Symfony cachecomponent is shipped with many adapters to different storages. Every adapter isdeveloped 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 the Cache Contracts, PSR-6/16 and Doctrine Cache interfaces.You can read more about these at the component documentation.
New in version 4.2: The cache contracts were introduced in Symfony 4.2.
Configuring Cache with FrameworkBundle
When configuring the cache component there are a few concepts you should knowof:
- Pool
- This is a service that you will interact with. Each pool will always haveits 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 are using to connect to the storage.Redis and Memcached are example of such adapters. If a DSN is used as theprovider then a service is automatically created.There are two pools that are always enabled by default. They are
cache.app
andcache.system
. The system cache is used for things like annotations, serializer,and validation. Thecache.app
can be used in your code. You can configure whichadapter (template) they use by using theapp
andsystem
key like:
- YAML
- # config/packages/cache.yaml
- framework:
- cache:
- app: cache.adapter.filesystem
- system: cache.adapter.system
- XML
- <!-- config/packages/cache.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:framework="http://symfony.com/schema/dic/symfony"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <framework:config>
- <framework:cache app="cache.adapter.filesystem"
- system="cache.adapter.system"
- />
- </framework:config>
- </container>
- 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:
- cache.adapter.apcu
- cache.adapter.array
- cache.adapter.doctrine
- cache.adapter.filesystem
- cache.adapter.memcached
- cache.adapter.pdo
- cache.adapter.psr6
- cache.adapter.redisSome of these adapters could be configured via shortcuts. Using these shortcutswill create pool with service id of
cache.[type]
- 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'
- XML
- <!-- config/packages/cache.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:framework="http://symfony.com/schema/dic/symfony"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <framework:config>
- <!--
- default_doctrine_provider: Service: cache.doctrine
- default_psr6_provider: Service: cache.psr6
- default_redis_provider: Service: cache.redis
- default_memcached_provider: Service: cache.memcached
- default_pdo_provider: Service: cache.pdo
- -->
- <framework:cache directory="%kernel.cache_dir%/pools"
- default_doctrine_provider="app.doctrine_cache"
- default_psr6_provider="app.my_psr6_service"
- default_redis_provider="redis://localhost"
- default_memcached_provider="memcached://localhost"
- default_pdo_provider="doctrine.dbal.default_connection"
- />
- </framework:config>
- </container>
- 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:
- 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.array
- # 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:[email protected]'
- # 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
- XML
- <!-- config/packages/cache.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:framework="http://symfony.com/schema/dic/symfony"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <framework:config>
- <framework:cache default_memcached_provider="memcached://localhost">
- <framework:pool name="custom_thing.cache" adapter="cache.app"/>
- <framework:pool name="my_cache_pool" adapter="cache.adapter.array"/>
- <framework:pool name="acme.cache" adapter="cache.adapter.memcached"/>
- <framework:pool name="foobar.cache" adapter="cache.adapter.memcached" provider="memcached://user:[email protected]"/>
- <framework:pool name="short_cache" adapter="foobar.cache" default_lifetime="60"/>
- </framework:cache>
- </framework:config>
- </container>
- PHP
- // config/packages/cache.php
- $container->loadFromExtension('framework', [
- 'cache' => [
- 'default_memcached_provider' => 'memcached://localhost',
- 'pools' => [
- 'custom_thing.cache' => [
- 'adapter' => 'cache.app',
- ],
- 'my_cache_pool' => [
- 'adapter' => 'cache.adapter.array',
- ],
- 'acme.cache' => [
- 'adapter' => 'cache.adapter.memcached',
- ],
- 'foobar.cache' => [
- 'adapter' => 'cache.adapter.memcached',
- 'provider' => 'memcached://user:[email protected]',
- ],
- 'short_cache' => [
- 'adapter' => 'foobar.cache',
- 'default_lifetime' => 60,
- ],
- ],
- ],
- ]);
Each pool manages a set of independent cache keys: keys of different poolsnever collide, even if they share the same backend. This is achieved by prefixingkeys with a namespace that's generated by hashing the name of the pool, the nameof the compiled container class and a configurable seedthat defaults to the project directory.
Each custom pool becomes a service where the service id is the name of the pool(e.g. custom_thing.cache
). An autowiring alias is also created for each poolusing the camel case version of its name - e.g. custom_thing.cache
can beinjected automatically by naming the argument $customThingCache
and type-hinting itwith either CacheInterface
orPsr\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. TheRedisAdapter allows you tocreate providers with option timeout
, retry_interval
. etc. To use theseoptions with non-default values you need to create your own \Redis
providerand use that when configuring the pool.
- 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 }
- XML
- <!-- config/packages/cache.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:framework="http://symfony.com/schema/dic/symfony"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <framework:config>
- <framework:cache>
- <framework:pool name="cache.my_redis" adapter="cache.adapter.redis" provider="app.my_custom_redis_provider"/>
- </framework:cache>
- </framework:config>
- <services>
- <service id="app.my_custom_redis_provider" class="\Redis">
- <argument>redis://localhost</argument>
- <argument type="collection">
- <argument key="retry_interval">2</argument>
- <argument key="timeout">10</argument>
- </argument>
- </service>
- </services>
- </container>
- PHP
- // config/packages/cache.php
- $container->loadFromExtension('framework', [
- 'cache' => [
- 'pools' => [
- 'cache.my_redis' => [
- 'adapter' => 'cache.adapter.redis',
- 'provider' => 'app.my_custom_redis_provider',
- ],
- ],
- ],
- ]);
- $container->getDefinition('app.my_custom_redis_provider', \Redis::class)
- ->addArgument('redis://localhost')
- ->addArgument([
- 'retry_interval' => 2,
- 'timeout' => 10
- ]);
Creating a Cache Chain
Different cache adapters have different strengths and weaknesses. Some might be reallyquick but small 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. The idea is tofirst look at the quick adapter and then move on to slower adapters. In the worstcase the value needs to be recalculated.
- YAML
- # config/packages/cache.yaml
- framework:
- cache:
- pools:
- my_cache_pool:
- adapter: cache.adapter.psr6
- provider: app.my_cache_chain_adapter
- cache.my_redis:
- adapter: cache.adapter.redis
- provider: 'redis://user:[email protected]'
- cache.apcu:
- adapter: cache.adapter.apcu
- cache.array:
- adapter: cache.adapter.array
- services:
- app.my_cache_chain_adapter:
- class: Symfony\Component\Cache\Adapter\ChainAdapter
- arguments:
- - ['@cache.array', '@cache.apcu', '@cache.my_redis']
- - 31536000 # One year
- XML
- <!-- config/packages/cache.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:framework="http://symfony.com/schema/dic/symfony"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <framework:config>
- <framework:cache>
- <framework:pool name="my_cache_pool" adapter="cache.adapter.psr6" provider="app.my_cache_chain_adapter"/>
- <framework:pool name="cache.my_redis" adapter="cache.adapter.redis" provider="redis://user:[email protected]"/>
- <framework:pool name="cache.apcu" adapter="cache.adapter.apcu"/>
- <framework:pool name="cache.array" adapter="cache.adapter.array"/>
- </framework:cache>
- </framework:config>
- <services>
- <service id="app.my_cache_chain_adapter" class="Symfony\Component\Cache\Adapter\ChainAdapter">
- <argument type="collection">
- <argument type="service" value="cache.array"/>
- <argument type="service" value="cache.apcu"/>
- <argument type="service" value="cache.my_redis"/>
- </argument>
- <argument>31536000</argument>
- </service>
- </services>
- </container>
- PHP
- // config/packages/cache.php
- $container->loadFromExtension('framework', [
- 'cache' => [
- 'pools' => [
- 'my_cache_pool' => [
- 'adapter' => 'cache.adapter.psr6',
- 'provider' => 'app.my_cache_chain_adapter',
- ],
- 'cache.my_redis' => [
- 'adapter' => 'cache.adapter.redis',
- 'provider' => 'redis://user:[email protected]',
- ],
- 'cache.apcu' => [
- 'adapter' => 'cache.adapter.apcu',
- ],
- 'cache.array' => [
- 'adapter' => 'cache.adapter.array',
- ],
- ],
- ],
- ]);
- $container->getDefinition('app.my_cache_chain_adapter', \Symfony\Component\Cache\Adapter\ChainAdapter::class)
- ->addArgument([
- new Reference('cache.array'),
- new Reference('cache.apcu'),
- new Reference('cache.my_redis'),
- ])
- ->addArgument(31536000);
Note
In this configuration the my_cache_pool
pool is using the cache.adapter.psr6
adapter and the app.my_cache_chain_adapter
service as a provider. That isbecause ChainAdapter
does not support the cache.pool
tag. So it is decoratedwith the ProxyAdapter
.
Using Cache Tags
In applications with many cache keys it could be useful to organize the data storedto be able to invalidate the cache more efficient. One way to achieve that is touse cache tags. One or more tags could be added to the cache item. All items withthe same key could be invalidate 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 TagAwareCacheInterface`
to enable this feature. This could be added by using the following configuration.
- YAML
- # config/packages/cache.yaml
- framework:
- cache:
- pools:
- my_cache_pool:
- adapter: cache.adapter.redis
- tags: true
- XML
- <!-- config/packages/cache.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:framework="http://symfony.com/schema/dic/symfony"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <framework:config>
- <framework:cache>
- <framework:pool name="my_cache_pool" adapter="cache.adapter.redis" tags="true"/>
- </framework:cache>
- </framework:config>
- </container>
- PHP
- // config/packages/cache.php
- $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. Butsometimes it might be better to store the tags in a different pool. That could beachieved by specifying the adapter.
- YAML
- # config/packages/cache.yaml
- framework:
- cache:
- pools:
- my_cache_pool:
- adapter: cache.adapter.redis
- tags: tag_pool
- tag_pool:
- adapter: cache.adapter.apcu
- XML
- <!-- config/packages/cache.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:framework="http://symfony.com/schema/dic/symfony"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <framework:config>
- <framework:cache>
- <framework:pool name="my_cache_pool" adapter="cache.adapter.redis" tags="tag_pool"/>
- <framework:pool name="tag_pool" adapter="cache.adapter.apcu"/>
- </framework:cache>
- </framework:config>
- </container>
- 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 TagAwareCacheInterface
isautowired 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 recalculateall values. You can also group your pools into "cache clearers". There are 3 cacheclearers by default:
cache.global_clearer
cache.system_clearer
cache.app_clearer
The global clearer clears all the cache in every pool. The system cache cleareris used in thebin/console cache:clear
command. The app clearer is the defaultclearer.
To see all available cache pools:
- $ php bin/console cache:pool:list
New in version 4.3: The cache:pool:list
command was introduced in Symfony 4.3.
Clear one pool:
- $ php bin/console cache:pool:clear my_cache_pool
Clear all custom pools:
- $ php bin/console cache:pool:clear cache.app_clearer
Clear all caches everywhere:
- $ php bin/console cache:pool:clear cache.global_clearer