Symfony 3 — From mutable to immutable framework
Why make Symfony 3 immutable?
In my job we work with Docker and Kubernetes, and ours applications must be immutable because we play with containers. And a container must run as read-only where possible!
How to make Symfony 3 immutable?
As you might know already, Symfony use a bundle to manage its configuration. The most troubling factor in my opinion.
How does the bundle work?
It’s very simple, you have two files ; “parameters.yml.dist” and “parameters.yml” in the “app/config” directory.
When you run composer for installing or updating, a special script handler named “buildParameters” is executed.
And this script builds a new version of “parameters.yml”.
#FAILED mutation detected!
Environment variables to the rescue!
In the world of containers, the environment variables are widespread. In fact, it’s the way to configure a container.
Since the version 3.2, Symfony introduces a new syntax to read environment variables from YAML files.
In the “app/config/parameters.yml” you can declare that:
parameters:
env(MY_ENV_VAR): "default"
In the “app/config/config.yml” you can read that:
i_need_a_value: "%env(MY_ENV_VAR)%"
#YUCK we need again the “parameters.yml” and the rest… Really? Nooooo!
Our solution!
The simple solution is before our eyes! Remove this crazy f***ing bundle.
First step, copy the “parameters” node data of “app/config/parameters.yml” in the “app/config/config.yml” and replace it by the environment variables.
Do not forget to add the default values.
parameters:
env(DATABASE_HOST): "pdo_pgsql"
... database_driver: "%env(DATABASE_DRIVER)%"
database_host: "%env(DATABASE_HOST)%"
database_port: "%env(DATABASE_PORT)%"
database_name: "%env(DATABASE_NAME)%"
database_user: "%env(DATABASE_USER)%"
database_password: "%env(DATABASE_PASSWORD)%" mailer_transport: "%env(SMTP_TRANSPORT)%"
mailer_host: "%env(SMTP_HOST)%"
mailer_user: "%env(SMTP_USER)%"
mailer_password: "%env(SMTP_PASSWORD)%"
secret: "%env(APP_SECRET)%"
locale: "%env(APP_LOCALE)%"framework:
translator: { fallbacks: ["%locale%"] }
...
Remove this import line from the “app/config/config.yml”:
- { resource: parameters.yml }
Remove “app/config/parameters.*” files.
Remove these lines from your “composer.json”:
"incenteev/composer-parameter-handler": "^X.Y"..."Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",..."incenteev-parameters": {
"file": "app/config/parameters.yml"
},
Execute a small “composer(.phar) update” to clean the “vendor” directory.
Job done!
Now, you’ve just to set your environment variables to change the execution of this amazing PHP framework.
Bonus
You can create an “.env.dist” file with your different environment variables. See below:
DATABASE_DRIVER=pdo_pgsql
DATABASE_HOST=symfony
DATABASE_PORT=5432
DATABASE_NAME=symfony
DATABASE_USER=symfony
DATABASE_PASSWORD=symfonySMTP_TRANSPORT=smtp
SMTP_HOST=0.0.0.0
SMTP_USER=symfony
SMTP_PASSWORD=symfony
APP_SECRET=HeyIAmSecret
APP_LOCALE=en
And you can use a tool (like that: https://github.com/kennethreitz/autoenv) that help you to load environment variables when you enter in directory.
In your “Dockerfile” or “docker-compose.yml” don’t forget to declare this environment variables.
I hope that this story helps you to make Symfony 3 immutable!
Next time, we will discuss about how to run Symfony 3 in a Kubernetes cluster.