Parameter
The Symfony parameter is a variable stored in the service container. Use parameters when you want to separate out values that regularly change as well as for reusable purposes.
We should prefix with ‘app’ to prevent conflicting with parameters from Symfony and 3rd party bundle, the following example is a 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 reuse 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 the 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 previously used in Controller is from the AbstractController which eventually uses the Parameter Bag service:
protected function getParameter(string $name)
{
return $this->container->get('parameter_bag')->get($name);
}
Environment Variables
In general, the environment variable is a dynamic-named value that can affect the way running processes on a computer. By leveraging environment variables, we can change the 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 this:
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 a 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 machines such as DATABASE_URL, and AWS_S3_BUCKET_NAME for our local machine which is different from our teammate’s machines and different from the production server.
Thank to the dotenv component, we can declare all environment variables we need in a file name .env in the root of the Symfony project directory. Symfony will eventually add these variables in the PHP array $_SERVER. This is very convenient 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 the .env file will not override the machine environment variable. Therefore, in many production servers, they can set up environment variables in 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 the Parameter Bag service mentioned previously.
Here is the config in services.yaml to set the parameter:
# config/services.yaml
parameters:
app.database_url: '%env(DATABASE_URL)%'
By assigning environment variable to parameter, we have 3 benefits:
The first is, thanks 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 the default value if there is no such environment variable.
The third benefit is we limit the way service can get external variables. By using parameters, the only way a service can get parameters is via its constructor. It increases our application predictability, yet the parameter is easier to mock in automated tests.
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 as the boolean true instead of the string “true”.
Second, if the MAINTENANCE_MODE environment variable is omitted, it will have the default value which is “false” by the syntax:
env(MAINTENANCE_MODE): "false"
When we use the environment variable?
Use environment variable if the value is changed between machines, otherwise, just using parameter is enough. For example, sender_email is not changed between machines and should be stored 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 Symfony Environment vs Server Environment.
Symfony Environment is declared via APP_ENV environment variable, by default Symfony provides 3 Symfony environments: prod, dev, and test. The idea is able to run the same code base with different configurations.
- a dev environment is designed for debugging purposes, it shows verbose errors, log, request information, and can generate fixtures (dummy data generation)
- The prod environment is designed for a production server, not showing verbose error, just keeping necessary packages, optimizing for the performance
- test environment is for automated test.
The server environment is totally different, we may have many server environments such as localhost, test, nightly, staging, production, and 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 the nightly server, it most likely works in staging & production server.