Symfony parameters and environment variables

An Vo
5 min readApr 30, 2020

--

services.yaml

Understand parameters and environment variables will does great help in Symfony configuration.

Parameter

Symfony parameter is variable stored in service container. Use parameter when you want to separate out values that regularly change as well as for reusable purpose.
We should prefix with ‘app’ to prevent conflicting with parameters from Symfony and 3rd party bundle, the following example is parameter for sender email:

# config/services.yaml
parameters:
app.sender_email: 'join@example.com'

You can get parameters in Controller or in Service.
Getting parameter in Controller is pretty simple:

$this->getParameters('app.sender_email');

To get parameter in Service, we have 2 solutions: via binding or via ParameterBag service:
Solution 1: Binding, we have 2 ways:

  • Bind in specific service constructor:
# config/services.yaml
parameters:
app.sender_email: 'john@example.com'
services:
App\Service\AcmeService:
arguments:
$senderEmail: '%app.sender_email%'
  • Bind globally to all services (in case we want to resuse the parameter in many services)
# config/services.yaml
parameters:
app.sender_email: 'join@example.com'
services:
_defaults:
bind:
$senderEmail: '%app.sender_email%'

After setup either binding way above, we can get the parameter from service constructor:

class AcmeService
{
$private $senderEmail;
public function __construct(string $senderEmail)
{
$this->senderEmail = $senderEmail;
}
}

Solution 2: Injecting Parameter Bag service:

class AcmeService
{
private $params;
public function __construct(ContainerBagInterface $params)
{
$this->params = $params;
}
public function someMethod()
{
$sender = $this->params->get('app.sender_email');
}
}

Actually, the getParameter() method we previous used in Controller is from the AbstractController which eventually use Parameter Bag service:

protected function getParameter(string $name)
{
return $this->container->get('parameter_bag')->get($name);
}

Environment Variables

In general, environment variable is a dynamic-named value that can affect the way running processes on a computer. By leverage environment variable, we can change behavior of different machines without the need of changing source code. For example: DATABASE_URL=localhost for development machine and DATABASE_URL=some_google_cloud_sql_url for production server.

In our terminal, we can run the command printenv to check our environment variables, the result would look like:

SHELL=/usr/bin/zsh
PATH=/usr/local/sbin:/usr/local/bin
DESKTOP_SESSION=ubuntu
...

In PHP, to check current server environment variables, we can dump the $_SERVER variable via function:

print_r($_SERVER);die;

The result will contain the environment variables above, plus some PHP specific values, for example:

[
"SHELL" => "/usr/bin/zsh",
"PHP_BINARY" => "/usr/bin/php7.3",
"SERVER_NAME" => "localhost:8000"
...
]

To create new environment variable, e.g: FOO=baz, we can directly type in our terminal:

export FOO=baz

From that we can create many environment variables for our machine such as DATABASE_URL, AWS_S3_BUCKET_NAME for our local machine which are different from our teammate’s machines and different from production server.

Thank to the dotenv component, we can declare all environment variables we need in a file name .env in the root of Symfony project directory. Symfony will eventually add these variables in PHP array $_SERVER. This is very convenience in development because we don’t need to add environment variable one by one in the shell command. One note to take away is that the value in .env file will not override the machine environment variable. Therefore, in many production servers they can setup environment variables by their ways (may via CI/CD tools) and not worry about the predefined values in the .env file.

In our Symfony application, we can get these environment variables directly in the $_SERVER or $_ENV, for example:

$databaseUrl = $_ENV['DATABASE_URL'];

However, I think the more appropriate way is to create a parameter to store the environment variable first, then get the parameter via binding or via Parameter Bag service mentioned previous.
Here is the config in services.yaml to set parameter:

# config/services.yaml
parameters:
app.database_url: '%env(DATABASE_URL)%'

By assigning environment variable to parameter, we have 3 benefits:
The first is, thank to Environment Variable Processors, we can cast the environment variable to the type we want such as int, string, bool, json decode…
The second benefit is we can define default value if there is no such environment variable.
The third benefit is we limit the way service can get external variable. By using parameter, the only way a service can get parameter is via its constructor. It increase our application predictability, yet parameter is easier to mock in automated test.
Let’s see the example below:

# .env
MAINTENANCE_MODE=true# config/services.yaml
parameters:
env(MAINTENANCE_MODE): "false"
app.maintenance_mode: '%env(bool:MAINTENANCE_MODE)%'

In this example. First, we cast the environment into boolean:

'%env(bool:MAINTENANCE_MODE)%'

By doing that, we get the final value is the boolean true instead of string “true”.
Second, if MAINTENANCE_MODE environment variable is omit, it will have the default value which is “false” by the syntax:

env(MAINTENANCE_MODE): "false"

When we use environment variable?

Use environment variable if the value is changed between machines, otherwise just use parameter is enough. For example, sender_email is not change between machines and should be store in parameter only

Performance Improvement tip:

Run this command in your production server or deployment tool (CI/CD tool):

composer dump-env prod

After running this command, Symfony will generate the .env.local.php file, and will load data from this PHP file to get the environment variables instead of spending time parsing the .env files.

Debug environment & parameter:

You can use some helpful commands to debug your current environment variables / parameters:

bin/console debug:container --env-vars
bin/console debug:container --env-vars foo
bin/console debug:container --parameters

Some terms you may confuse

People coming to Symfony often confuse between Symfony Environment vs Server Environment.
Symfony Environment is declare via APP_ENV environment variable, by default symfony provide 3 symfony environments: prod, dev and test. The idea is to able to run the same code base with different configurations.

  • dev environment is designed for debug purpose, it show verbose errors, log, request information, and can generate fixtures (dummy data generation)
  • prod environment is design for production server, not showing verbose error, just keep necessary packages, optimize for performance
  • test environment is for automated test.

Server environment is totally different, we may have many server environments such as localhost, test, nightly, staging, production, feature. In my own need, I config Symfony environment “dev” in my “localhost” machine for the ease of debugging, and config Symfony environment “prod” in all other servers, so that I have more sure that if something work in nightly server, it most likely work in staging & production server.

--

--