Symfony Internals #1: Inside the Framework Configuration
How does Symfony processes configuration you’re passing it?
This article is the first one of the blog serie Symfony Internals, which dissects the internal workings of the framework and its components. Whether it’s to contribute to Symfony development or for your personal knowledge, you’ll find here a vulgarization of the solutions implemented within the internal classes to meet our needs.
When you’re using the whole Symfony framework and not only its standalone components, you are configuring your application with YAML files. Or maybe XML files. Or even PHP files. Doesn’t matter which configuration format you choose, it is completely up to you and each of them will offer the same features. Whatever you chose, you’re going to use the FrameworkBundle of Symfony. Roughly, this bundle is using a lot of Symfony’s standalone components to offer a pleasant and unified development experience. It’s the glue between components that enables you to write applications fast.
When setting your internal application parameters to match your needs, you must respect configuration keys, value formats, and many other constraints. I’m talking about this type of configuration I’m sure you already used:
But do you know how the framework processes the whole thing internally to apply this configuration? That’s what we’re going to see by diving first in the Config component, used by the framework to achieve this goal.
The Config Component (Symfony Docs)
The Config component provides several classes to help you find, load, combine, fill and validate configuration values…
Even if, at first sight, it could seem a complicated part of the framework, we’ll see that’s actually really straightforward and there are only three key-parts in the process.
The very heart of all of this is the Config component. I’m going to quote the documentation that summarize this component super clearly:
The Config component provides several classes to help you find, load, combine, fill and validate configuration values of any kind, whatever their source may be
Symfony is giving choice when it comes to the configuration format. It can even handle multiple file formats to be used in the same application. In this first example, we’ll use YAML format and the configuration of Symfony’s secrets as an example, because it is trivial and straightforward.
Symfony secrets vault explained in less than 5 minutes
This fully-integrated way of storing sensitive production information will definitely make your life easier
A basic configuration of secrets in Symfony with all available options looks like this:
Here are three things which can be noted in these 5 lines:
- We have a root node, named
secretsnode is present, and it is an array of nodes. It can be disabled ;
- Three named nodes, each with a default value, and
vault_directorythat can’t be empty.
How do I know a particular node can be empty or not? Remember, at the beginning of the article, I talked about the FrameworkBundle, which you use when developing a Symfony application.
Well, if we take a look at the DependencyInjection folder of the FrameworkBundle of Symfony, we’ll find the Configuration.php file. This is where the magic happens:
You can note the creation of the main
framework node thanks to the TreeBuilder, and also how the secrets section of the configuration is written. If you go see the file in its entirety, you’ll find that the whole available configuration of the framework is described like this.
Configuration is way more powerful than describing default values! Just take a look at the PhpErrors section of the same file to realize it:
Now we’re talking. Of course there are many more features available like triggering deprecations on an option use, converting scalar values to arrays and so on. Everything is described in the documentation of the framework. If want to go deeper, you can start by checking out this page:
Once our configuration tree is built thanks to the TreeBuilder, configuration files are loaded, then processed by the Processor class of the Config component. The Processor does three things on the configuration tree, in this order:
- It normalizes the configuration for easier processing, executes closures (like in the last code snippet above, line 17), validates types and resolves placeholders ;
- It merges all different configurations together to have only one “global” configuration array ;
- It finalizes everything by processing the final result again, validating types, etc.
We now know how to validate our configuration. The next actual step is to apply this configuration and load it in the FrameworkBundle itself.
And this one’s done thanks to the Symfony\Component\DependencyInjection\Extension class of the DependencyInjection component, extended by the FrameworkExtension class of the FrameworkBundle in our case.
The DependencyInjection Component (Symfony Docs)
For an introduction to Dependency Injection and service containers see Service Container. This article explains how to…
Indeed, the Extension class instantiate a Processor described above, then processes the configuration in a method called
processConfiguration. This one’s called internally by FrameworkExtension, which allows it to fetch the whole valid configuration. Then the FrameworkExtension applies this (your) configuration to the different services, setting different internal parameters, etc.
And voilà, we learned how Symfony loads its configuration!
To summarize, there are three main parts to remember about the loading of your configuration in the framework. These three main parts are the following ones:
- A TreeBuilder to write validation rules of our configuration ;
- A Processor that actually validates our configuration thanks to the TreeBuilder ;
- An Extension that instantiates the Processor, fetch the configuration and applies it to the differents services.
Doesn’t it sound simple when said like that? And remember, that’s something you could use in your very own projects if needed.