Symfony2 Where to save Environment Variables - php

I've seen plenty of tutorials on configuring an Apache2 virtual host with environment variables and using those variables as a parameter within the site. However, the issue I'm having is when I run php app/console cache:clear or reload my data fixtures, I get the following error:
You have requested a non-existent parameter ...
If I first export the parameter prior to reloading my fixtures, the error goes away as the environment variable is set. Which makes sense as that function isn't using Apache to execute and needs the environment variable set. But this doesn't fix my issue when I clear my cache manually.
So my question is, where do I actually want to save those environment variables?
I would like to store site specific information that's dependent on which virtual host is used.
I could store them in my ~/.profile bash file, but that doesn't really make sense and all of the documentation I've read simply says store with the virtual host (for Apache). But none of those docs mention executing scripts outside of a web browser accessing the site such as reloading fixtures, running migrations, or clearing cache, which is only an issue when I deploy my source.
Thanks a lot for any help.

You can put application variables in app/config/config.yml. You can put there:
parameters:
your.parameter.1: value1
your.parameter.2: value2
If you use any kind of version control system you can exclude those variables to separate file and ignore this file in version control repository. You also should include this file in config.yml. For example if you put the above variables in app/config/example.yml then you should add in app/config/config.yml:
imports:
- { resource: example.yml }
EDIT
You can always put parameters as follows:
parameters:
domain1:
param1: value1
param2: value2
domain2:
param1: value1
param2: value2
domain3:
param1: value1
param2: value2
and then read these variables in places where you want to use them according to the current domain or you can also set service that sets common variables according to current domain. For command line you can use default values.

So just because this is situation is a pet peeve of mine I urge you not to put environment specific information in the config file. So what is needed are two files that must stay in sync. One file will be for command line and one for Apache (or HTTP server of your choice) environment variables
CL: /var/www/applications/coolapp/coolapp.sh
Apache: /var/www/applications/coolapp/coolapp.apache.conf
Usage
For the command line, any time I run a Symfony command I source my environment (this example is for bash shell)
$ . ./coolapp.sh
$ app/console cache:clear
In the Apache config for the site I add an include such as
/etc/httpd/conf.d/coolapp.local.conf
<VirtualHost coolapp.local:443>
...
Include /var/www/applications/coolapp/coolapp.apache.conf
...
</VirtualHost>
What is in these files?
coolapp.sh would contain something like
export SYMFONY__APP_USER = foo
export SYMFONY__APP_PASS = foobar01
and coolapp.conf would have
SetEnv SYMFONY__APP_USER fop
SetEnv SYMFONY__APP_PASS foobar01
So each one of my environments (production, qa, stage, test, dev, localdev) would have these two files. And they can be changed whenever without going into the code.
Within Symfony you would access these variable like
%app_user%
%app_pass%

Related

Which Symfony environment variable is available in which file (.env, .env.local, etc.)?

I have just started a new Symfony 5 project (only worked on Symfony 3 before) and I am having a hard time to understand how values in .env and .env.local work together.
The Symfony docs say, that values in .env.local will override values from .env:
If you need to override an environment value (e.g. to a different
value on your local machine), you can do that in a .env.local file:
All machines I am working on use the same basic setup (same DB server, etc.) but some values are different (DB name, password, etc.). Thus I wanted to put the common configuration in .env and the device specific values in .env.local:
# .env
...
DB_SERVER="localhost"
DB_VERSION="10.4"
DB_USER=${DB_DBNAME}
DATABASE_URL="mysql://${DB_USER}:${DB_PASS}#${DB_SERVER}/${DB_DBNAME}?serverVersion=${DB_VERSION}"
# .env.local
DB_DBNAME="someDB"
DB_PASS="secretPW"
When using this setup I get an error and php bin/console debug:container --env-vars shows DATABASE_URL "mysql://:#localhost/?serverVersion=10.4". Obviously DATABASE_URL is build ignoring the values from .env.local.
I can easily solve this by moving DATABASE_URL="mysql://... to .env.local but is this the intended way of using .env and .env.local?
I thought values of these files are merged where .env.local values override .env values. Is this not the case? Or did I make some error?
Which Symfony environment variable is available in which file (.env, .env.local, etc.)?
.env is loaded before .env.local, and each file is processed independently of each other.
For interpolation purposes, only the environment variables defined in the same file, or in in a file processed earlier, are available during .env file processing.
While environment variables defined in more specific env files do trump those defined in more general ones, in your example when DB_DBNAME and DB_PASS are overwritten/defined too late to be of use, since DATABASE_URL (which depends on those) is defined earlier and not redefined in the .env.local file .
But, you can accomplish what you want to do, you just have to do it a different way.
First, create your DATABASE_URL environment variable with parameter placeholders, like this:
DATABASE_URL="mysql://%DB_USER%:%DB_PASS%#%DB_SERVER%/%DB_DBNAME%?serverVersion=%DB_VERSION%"
Then, in your services.yaml file, define the container parameters for all these values:
# services.yaml
parameters:
DB_DBNAME: "%env(DB_DBNAME)%"
DB_USER: "%env(DB_USER)%"
DB_PASS: "%env(DB_PASS)%"
DB_SERVER: "%env(DB_SERVER)%"
DB_VERSION: "%env(DB_VERSION)%"
# rest of your services.yaml file follows
It's important that your doctrine configuration reads like this (which is the default), including the resolve bit:
# doctrine.yaml
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
With this you'd get your desired results.
I think the order is relevant in this case: as you can see in the source code, .env is fully loaded and only afterwards .env.local is read. That means setting the variables from .env.local to a variable from .env looks impossible, while it would work the other way around.
The documentation also mentions this pitfall, along with an example:
The order is important when some env var depends on the value of other env vars.

Symfony 4 - .env Variables Not Available

I'm running in to a brick wall here. Everything so far with Symfony 4 has been sunshine and daisies but now I can't seem to add any environment variables to my project.
In my project root there is the default .env file populated with the usual APP_ENV, APP_SECRET and DATABASE_URL; that's all great. However, adding any custom variable there doesn't seem to actually do anything! If I add in ANOTHER_VAR=hello_world, for example, and then run bin/console debug:container --env-vars, it only dumps the APP_SECRET and DATABASE_URL variables. If I run bin/console about, however, it shows me all of the variables inside .env.
I thought it might be a cached variable issue but if I change the value of APP_SECRET and run the console command again, it dumps the changed value but still not my custom variables.
I suspect that the env vars are not available to the container by default but that doesn't seem right to me (plus it seems somewhat contradictory to what the Symfony docs themselves say).
What do I need to do to register these variables so I can access them inside a Controller, for example?
As is typical of me, I stumble across the answer within half an hour of getting frustrated and asking for help.
Contrary to how the documentation reads, simply adding a variable to the .env file does not make it available inside the Container (and so not listed when you run bin/console debug:container --env-vars nor available inside a Controller using $this->getParameter('env(VAR)')). And thinking about it now, this kinda makes sense because you wouldn't want all of your protected variables available to everything that runs from the Container.
In order to access an env var you need to parameterise it first and this way you can limit access to it as well.
Any env vars that you want available to all of your services you can set within the config/services.yaml file like so (assuming autowiring is enabled):
parameters:
variable_a: '%env(VAR_A)%'
If you would prefer to restrict the env var to just a few services, then you can inject it in to your service as an argument within config/services.yaml like so:
services:
App\Service\Foo:
arguments:
$bar: '%env(BAZ)%'
With the parameter set up like the above, I can now access that env var inside my Controller with $this->getParameter('variable_a'). Running bin/console debug:container --env-vars also outputs it.
It's a bit of a pain since I assumed that simply calling $this->getParameter('env(BAZ)') would do the trick but I suppose it makes sense (there is actually no parameter called env(BAZ)).
Hopefully this helps someone else struggling to wrap their head around this.

Symfony4: Access DEV environment on production server

In Symfony3, when I want to browse the website in DEV environment on the "live" server, I just enter my ip address in /web/app_dev.php and open http://www.example.com/app_dev.php/ in the browser.
Since in Symfony4, the environment is now set in /.env, how can I see the DEV environment on the production machine?
EDIT: I'm looking for a solution that works in production, so applying any global changes (like e.g. setting APP_ENV=dev in /.env) is not an option.
You can change inside your .env file APP_ENV to dev like this:
APP_ENV=dev
If you set that variable symfony load the system into dev enviroment because inside Kernel.php there is this line that check that variable:
$kernel = new Kernel($_SERVER['APP_ENV'] ?? 'dev', $_SERVER['APP_DEBUG'] ?? false);
If you want to do it without change .env file you can for example set a variable in the Apache vhost or Nginx FastCgi configuration, based on the URL you are visiting from - such as APP_ENV=/home/user/app-name/dev.env or on a live server: APP_ENV=/etc/app-name.prod.env
So in this case you have many .env file but you can use rule based on url
At first this is a bad idea and that's why it wasn't possible by default to access app_dev.php on production server (symfony < 4). You're giving a lot of internal information to public and especially to attackers.
From symfony docs:
After you deploy to production, make sure that you cannot access the app_dev.php or config.php scripts (i.e. http://example.com/app_dev.php and http://example.com/config.php). If you can access these, be sure to remove the DEV section from the above configuration.
You should be able to debug most of the things from logs.
But if you really want to do it, you can just remove public/index.php and create public/app.php and public/app_dev.php like it was in symfony 3 and make it work with env variables. - https://github.com/symfony/symfony-standard/tree/3.4/web
EDIT: To be clear: you can just remove public/index.php, create public/app.php, public/app_dev.php (copies of index.php). And get inspiration from symfony 3 standard edition to adjust it to your needs.
EDIT2: As #Cerad mentioned it's a better idea to have index.php and index_dev.php file names (following Symfony4 decisions).

Symfony 4, .env files and production

.env files are very handy with docker, kubernetes, etc
But what if I have simple nginx server without any orchestration and a pack of cron workers and a pack of daemons(systemd/supervisord/etc)?
I can write these env variables to nginx server section, but I have to set up hundreds of env variables to each cron worker or daemon.
I found a quick solution: using symfony/dotenv component in production.
But it seems to me dirty. Who can suggest better solution?
First of all, not all variables need to be specified using environment variables. Keep variables that do not differ per system in a separate yaml file.
When you have just one environment per server you can specify the environment variables globally in /etc/environment. (Might be different depending on your Linux flavour)
Personally I find that using DotEnv poses more difficulties than solutions when you run multiple environments on the same server. Specifying the variables in a global configuration like /etc/environment doesn't work in that case.
Specifying the environment variables in nginx isn't a solution either since, as you mentioned, they won't be picked up by cron, supervisor, the console, etc. For me, this was the reason to completely remove DotEnv and work with the good old parameters.yaml file again. Nothing will stop you from doing that.
Another solution however is to keep using DotEnv in your development environment and to include a separate parameters.yaml in production. You can then define the environment variables as follows:
parameters:
env(APP_ENV): prod
env(APP_SECRET): 3d05afda019ed4e3faaf936e3ce393ba
...
A way to include this file is to put the following in your services.yaml file:
imports:
- { resource: parameters.yaml, ignore_errors: true }
This way, the import will be ignored when no parameters.yaml file exists. Another solution is to add a line to configureContainer() in your Kernel class:
$loader->load($confDir.'/parameters'.self::CONFIG_EXTS, 'glob');
if you want to centralize your environment variables for cli and fpm you can define them once in your system. And then reference them all in your php-fpm.conf:
....
[www]
env[APP_VAR1] = $APP_VAR1
env[APP_VAR2] = $APP_VAR2
...
In that way you can avoid using DotEnv in production which is encouraged by best practices.
Hope this helps.

Artisan unable to use $_SERVER variables from database config file

I am currently deploying a Laravel project on my shared hosting account. It is an open project and hosted on GitHub as a public repository. As a result I'm using dynamic variables set by an .htaccess file in my database.php configuration file for my production environment. This allows me to also update my deployment using a git pull command on my host which helps speed up work.
The database.php file has something similar to
$database = $_SERVER['DBNAME'];
$database_user = $_SERVER['DBUSER'];
This is much like what is done when deploying to PagodaBox & works perfectly fine for the application with all things functioning as expected in the browser, no complaints.
The problem I have is that artisan is unable to use these variables and will attempt instead to connect to the database using what I believe to empty variables when processing a migrate instruction. I get an error that artisan tried to connect to the databases with no password. I have been calling artisan using --env=production and have tested this but found that it will only work if the database.php file has the variables specified explicitly instead of as environment variables.
Is there a way of causing artisan to "see" these environment variables?
answers that have proved useful to me so far:
http://forums.laravel.io/viewtopic.php?pid=8455
and
Environment driven database settings in Laravel?
Because Artisan is a CLI PHP request - the request never hits the .htaccess file - and therefore your variables are never set.
As a workaround - you could define the variables inside the artisan file itself on line 3 (just after the <?php)
$_SERVER['DBNAME'] = 'test';
$_SERVER['DBUSER'] = 'something';
edit: I just noticed you said this is public hosted on github - so you wont want to include your username/password in the file? Maybe put the artisan file as part of the .gitignore group - so you dont push/pull that single file?
The capability to set up environment variables is built in to Laravel, so there's no reason to do it in .htaccess. Laravel's built-in way works with artisan without any trouble.
See this part of the docs about environment variables you would like to protect.
http://laravel.com/docs/configuration#protecting-sensitive-configuration
Quoting:
... create a .env.local.php file within the root of your project [...] The .env.local.php should return an array of key-value pairs, much like a typical Laravel configuration file:
<?php
return array(
'TEST_STRIPE_KEY' => 'super-secret-sauce',
);
All of the key-value pairs returned by this file will automatically be available via the $_ENV and $_SERVER PHP "superglobals". You may now reference these globals from within your configuration files:
'key' => $_ENV['TEST_STRIPE_KEY']
Be sure to add the .env.local.php file to your .gitignore file. This will allow other developers on your team to create their own local environment configuration, as well as hide your sensitive configuration items from source control.
Add your private environment variables
<?php
return array(
'MY_SECRET_KEY' => 'super-secret-sauce',
);

Categories