Configuring the Cache Service

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

At your terminal, get a list of all the services in the container matching the word "markdown" by running:

php bin/console debug:container markdown

Ah, recognize markdown.parser.light? That was what we used for our parser key! Select "1" and hit enter to get more info. No surprise: its class name is Light, which is the exact class that's being dump back in our browser, from our controller.

So, on a high level, by adding the parser config, we were basically telling the bundle that we want the "main" markdown parser service to be this one. In other words: when we autowire with MarkdownParserInterface, please give us markdown.parser.light.

Figuring out how to Configure the Cache

Anyways, one of our initial goals was to figure out how we could change the cache service to stop caching on the filesystem and instead cache somewhere else. In our controller, replace the dd() with dump($cache):

... lines 1 - 11
class QuestionController extends AbstractController
{
... lines 14 - 31
public function show($slug, MarkdownParserInterface $markdownParser, CacheInterface $cache)
{
... lines 34 - 44
dump($cache);
... lines 46 - 51
}
}

I'm using dump() so that the page still renders - it'll make things easier.

Now, move over, refresh and... interesting. The cache object is an instance of TraceableAdapter but inside it... ah, there's something called FilesystemAdapter. So that kind of proves that the cache is being stored somewhere on the filesystem.

Ok, so how can we control that? In reality... you'll probably just Google that to find what config you need. But let's see if we can figure this out ourselves. But, hmm, we don't really know which bundle this service comes from.

Open up config/bundles.php. When we started the project, the only bundle here was FrameworkBundle - the core Symfony bundle:

... lines 1 - 2
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
... lines 5 - 12
];

Every other bundle was installed by us. And... since I don't really see any "CacheBundle", it's a good guess that the cache service comes from FrameworkBundle.

Let's test that theory! Find your terminal, pet your cat, and run:

php bin/console config:dump FrameworkBundle

Search this giant config for cache... I'm looking to see if there is maybe a cache section. Here it is! Under framework, this bundle has a sub-key called cache with quite a lot of example config. Because this is a bit hard to read, re-run this command with FrameworkBundle cache.

php bin/console config:dump FrameworkBundle cache

This only shows the cache section beneath framework.

So... this give us some nice information: we can see a key called app set to cache.adapter.filesystem... that kind of looks like something we might want to tweak... but I'm not sure... and I don't know what I would change it to.

So this is helpful... but not that helpful.

The debug:config Command

Another way that you can look at bundle configuration is to pass the exact same arguments to another command called debug:config:

php bin/console debug:config FrameworkBundle cache

The difference is subtle: config:dump shows you examples of all possible config whereas debug:config shows you your real, current values. Let's rerun this without the cache argument to see all our FrameworkBundle config:

php bin/console debug:config FrameworkBundle

Seeing our real values is cool... and we can see that under cache, the app key is set to cache.adapter.filesystem. But... we still don't really know what config we should change... or what to change it to!

Changing the Cache Adapter to APCu

Let's go see if the config file for this bundle can help. Logically, because we're configuring the framework key, open up config/packages/framework.yaml:

framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
#http_method_override: true
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
session:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
#esi: true
#fragments: true
php_errors:
log: true

Huh, I don't see a cache key! And it's possible that there is no cache key in our config and that the values we saw were the bundle's defaults. But actually, we do have some cache config... it's just hiding in its own file: cache.yaml. Inside, it has framework then cache:

framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name
# The "app" cache stores to the filesystem by default.
# The data in this cache should persist between deploys.
# Other options include:
# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
#app: cache.adapter.apcu
# Namespaced pools use the above "app" backend by default
#pools:
#my.dedicated.cache: null

It's not very common for a bundle's config to be separated into two files like this, but it is totally legal. Remember: the names of these files are not important at all. The cache config was separated because it's complicated enough to have its own file.

Anyways, this file is full of useful comments: it tells us how we could use Redis for cache or how we could use APCu, which is a simple in-memory cache. Let's use that: uncomment the cache.adapter.apcu line:

framework:
cache:
... lines 3 - 13
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
app: cache.adapter.apcu
... lines 16 - 20

Before we even try that, find your terminal and run the debug:config command again:

php bin/console debug:config FrameworkBundle

Scroll up to the cache section: yes! This sees our new config! But... what difference does that make in our app? Find your browser, refresh, then hover over the target icon on the web debug toolbar to see the dump. This time the adapter object inside is ApcuAdapter! It's caching in memory! We made one little tweak and FrameworkBundle did all the heavy lifting to change the behavior of that service.

Oh, and if you get the error:

APCu is not enabled

It means you need to install the APCu extension. How you do that varies on each system but it's usually installed with pecl - like:

pecl install apcu

After you install it, make sure to restart your web server. You can do that by running

symfony server:stop

And then re-run the command to start the server:

symfony server:start

If installing this is causing you problems, don't worry about it. For example purposes, you can use the key cache.adapter.array instead. That's a service that actually does no caching, but it will allow you to see how the class changes.

Next, we've started to modify files in this config/packages/ directory. Now I want to talk more about the structure of this directory - specifically about Symfony environments, which will explain these dev/, prod/ and test/ sub-folders.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.5",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.8", // 1.8.1
        "sensio/framework-extra-bundle": "^5.5", // v5.5.4
        "sentry/sentry-symfony": "^3.4", // 3.4.4
        "symfony/asset": "5.0.*", // v5.0.8
        "symfony/console": "5.0.*", // v5.0.8
        "symfony/debug-bundle": "5.0.*", // v5.0.8
        "symfony/dotenv": "5.0.*", // v5.0.8
        "symfony/flex": "^1.3.1", // v1.9.10
        "symfony/framework-bundle": "5.0.*", // v5.0.8
        "symfony/monolog-bundle": "^3.0", // v3.5.0
        "symfony/profiler-pack": "*", // v1.0.4
        "symfony/twig-pack": "^1.0", // v1.0.0
        "symfony/var-dumper": "5.0.*", // v5.0.8
        "symfony/webpack-encore-bundle": "^1.7", // v1.7.3
        "symfony/yaml": "5.0.*" // v5.0.8
    },
    "require-dev": {
        "symfony/maker-bundle": "^1.15", // v1.15.0
        "symfony/profiler-pack": "^1.0" // v1.0.4
    }
}