Buy
Buy

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Our Slack feature is working... but this kind of sucks! Our "secret" URL is hardcoded in the middle of a config file!

nexy_slack:
endpoint: 'https://hooks.slack.com/services/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'

This is a bummer because I don't want to commit this to version control! And what if I need to use a different value on production?

We're going to have this problem a bunch more times - for example - with our database password! We need some good way of isolating any sensitive or server-specific config so that they're not stuck in the middle of our code.

Intro to Environment Variables

One of the best ways to do this - and the way that Symfony recommends - is via environment variables. OooOOoo. But... environment variables are still kind of a mystery to a lot of PHP devs. A mostly accurate description is that they're variables that are set on your operating system, that can then be read by your code. How? Usually with the getenv() function or $_SERVER.

Actually, open public/index.php. Hey! Our code is already reading an environment variable: APP_ENV:

40 lines public/index.php
... lines 1 - 9
// The check is to ensure we don't use .env in production
if (!isset($_SERVER['APP_ENV'])) {
if (!class_exists(Dotenv::class)) {
throw new \RuntimeException('APP_ENV environment variable is not defined. You need to define environment variables for configuration or add "symfony/dotenv" as a Composer dependency to load variables from a .env file.');
}
(new Dotenv())->load(__DIR__.'/../.env');
}
$env = $_SERVER['APP_ENV'] ?? 'dev';
... lines 19 - 40

Tip

If you start a new project today, you won't see this APP_ENV logic. It's been moved to a config/bootstrap.php file.

But here's the question: how can we remove this hardcoded URL, and instead tell the NexySlackBundle to read from some environment variable? I mean, it's not like we can just use the getenv() PHP function in the middle of YAML!

Copy the URL - we'll need it later, then empty the value. Symfony has a special syntax that can be used in config files to read from environment variables. It's a little weird at first, but stick with me: %env()%. Between the parentheses, put the name of the environment variable. We'll be setting a new environment variable, so how about, SLACK_WEBHOOK_ENDPOINT:

nexy_slack:
endpoint: '%env(SLACK_WEBHOOK_ENDPOINT)%'

By convention, environment variables are uppercase. And huh... this looks like a parameter: it has the % at the beginning and at the end. And... internally, it is! It's just a special parameter that will eventually resolve to this environment variable.

If we refresh now... error! Perfect!

Environment variable not found: SLACK_WEBHOOK_ENDPOINT.

Setting Environment Variables in .env

I love clear errors. So... how the heck do we set environment variables? Well... unfortunately, it totally depends on your setup! The solution is different if you're using Apache, Nginx, Docker or some Platform-as-a-Service. I'll talk more about that later.

But since setting environment variables can be a pain, Symfony gives us a much easier way to set them while developing. How? Open the .env file at the root of our project:

# This file is a "template" of which env vars need to be defined for your application
# Copy this file to .env file for development, create environment variables when deploying to production
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration

###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=5ea3114a349591bd131296e00f21c20a
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
#TRUSTED_HOSTS=localhost,example.com
###< symfony/framework-bundle ###

This file is loaded inside index.php... as long as the APP_ENV environment variable isn't set some other way:

40 lines public/index.php
... lines 1 - 9
// The check is to ensure we don't use .env in production
if (!isset($_SERVER['APP_ENV'])) {
if (!class_exists(Dotenv::class)) {
throw new \RuntimeException('APP_ENV environment variable is not defined. You need to define environment variables for configuration or add "symfony/dotenv" as a Composer dependency to load variables from a .env file.');
}
(new Dotenv())->load(__DIR__.'/../.env');
}
... lines 17 - 40

And... it's pretty simple: it reads all of these keys and sets each as a new environment variable. This file was originally added by a recipe and - this is really cool - other recipes will update this file: adding new environment variables for their libraries.

But, we're totally free to add our own stuff. Let's invent a new section on top:

# This file is a "template" of which env vars need to be defined for your application
# Copy this file to .env file for development, create environment variables when deploying to production
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration

### CUSTOM VARS

### END CUSTOM VARS

###> symfony/framework-bundle ###
# ...
###< symfony/framework-bundle ###

The fancy code comments around the framework-bundle section were added by Flex: it's so that it knows where the environment variables live for that library... basically so that it can remove them if we remove that bundle.

Our new section is just for clarity.

Add SLACK_WEBHOOK_ENDPOINT= and then our URL:

# This file is a "template" of which env vars need to be defined for your application
# Copy this file to .env file for development, create environment variables when deploying to production
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration

### CUSTOM VARS
SLACK_WEBHOOK_ENDPOINT=https://hooks.slack.com/services/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
### END CUSTOM VARS

###> symfony/framework-bundle ###
# ...
###< symfony/framework-bundle ###

And yep! That's all we need! Refresh! It works!

Seeing all Environment Variables

If you want to see all the environment variables that are currently set, there's a handy bin/console command for that:

php bin/console about

This shows your Symfony version, some system info and - hello! - environment variables!

Updating .env.dist

Tip

New projects will not have a .env.dist file. Instead, your .env file is committed to your repository and should hold sensible, but not "secret" default values. To override these defaults with values specific to your machine, create a .env.local file. This file will be ignored by git.

In addition to the .env file, there is another file: .env.dist. Copy our new section, open that file, and paste! Remove the sensitive part of the URL:

15 lines .env.dist
# This file is a "template" of which env vars need to be defined for your application
# Copy this file to .env file for development, create environment variables when deploying to production
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
### CUSTOM VARS
SLACK_WEBHOOK_ENDPOINT=https://hooks.slack.com/...
### END CUSTOM VARS
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=5ea3114a349591bd131296e00f21c20a
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
#TRUSTED_HOSTS=localhost,example.com
###

This file is not read by Symfony: it's just meant to be a template file that contains all the environment variables our app needs. Why? Well, this file will be committed to the repository... but .env will not: it's in our .gitignore. So, when a new dev works on the project for the first time, they can copy .env.dist to .env and then fill in their custom values.

And... yea! That's basically it! There is one fancy syntax in config files to read environment variables, and a .env file to help set them during development.

Environment Variables on Production

But... what about on production? Let's talk a bit more about this.

Leave a comment!

  • 2018-11-09 weaverryan

    Hey Jérôme !

    That's the tricky thing about environment variables - if you have them set on your system, then even perfect code will behave a bit differently. So, let's debug this step by step:

    1) In your public/index.php file, put a die; statement right before this line -https://github.com/symfony/... - does it execute? Or not? If it does NOT execute, we know that (somehow) the APP_ENV environment variable IS set. Btw, what web server are you using? The built-in php server we use or another one?

    2) If the above die statement IS executed, then right after this line in that same file -https://github.com/symfony/... - add this: var_dump($_SERVER['SYMFONY_DOTENV_VARS']);die;. What prints out?

    We'll figure it out ;).

    Cheers!

  • 2018-11-07 Jérôme 

    Thanks for the response, Ryan! I tried your solution, but it doesn't seem to work. I ran "unset" on "APP_ENV", "SLACK_WEBHOOK_ENDPOINT", "APP_SECRET", & "DATABASE_URL". I even tried to restart the server... That's weird, because I followed the tutorial to the letter, didn't do anything else, and yet there's this problem. You cloned the project and didn't have it... I'm lost.

  • 2018-11-06 weaverryan

    Hey Jérôme !

    Ok! Thanks for posting your code! I cloned it, ran composer install, then ran php bin/console about and... surprise! Or maybe not a surprise, I see the "Environments (.env)" section.

    I'm pretty sure I know what's going on: somehow, at some point, you DID set the APP_ENV real environment variable. I mean, of course, when you did source .env, that set this environment variable. But, you only did this AFTER having issues. I think that, somehow, even before your ran source .env you somehow had an APP_ENV environment variable setup. HOW that happened is the mystery. But everything after that makes perfect sense: when you have APP_ENV set as a true environment variable, Symfony does not load your .env file (that's the code right inside public/index.php). And so, nothing you do in .env has any effect.

    So, here is (should be) the fix. Run:


    unset APP_ENV
    unset SLACK_WEBHOOK_ENDPOINT

    ... and any other environment variables that exist in your .env file. Really, the APP_ENV is the important one: when you remove this, your .env file will now be parsed again! But, by default, entries in your .env file do NOT override real environment variables. That's why you should also unset any other variables, so that the values in .env are used. You did a good workaround by originally running source .env</code.>

  • 2018-11-06 Jérôme 

    I've been told this is an known issue: https://github.com/symfony/...

  • 2018-11-05 Jérôme 

    Yes, now on Github: https://github.com/ajie62/s... (will be deleted after we find a fix (if possible)).

  • 2018-11-05 Diego Aguiar

    Hmm, can you upload your project into Github's so I can clone it and give it a check?

  • 2018-11-05 Jérôme 

    On localhost:8000/_profiler/phpinfo, I can see an environment section. In it, I can see lots of variables including APP_SECRET, SLACK_WEBHOOK_ENDPOINT which points to the URL I wrote in .env, and there's an APP_ENV var with "dev" as a value... So... I'm lost right now! :/

  • 2018-11-05 Diego Aguiar

    Ohh, you don't have an Environment section! So this confirms that your app is definetely not loading your ".env" file.
    I also believe that you have an "APP_ENV" env variable set. Open your phpinfo page and check the "Environment" section to confirm this (http://localhost:8000/_profiler/phpinfo)

  • 2018-11-05 Jérôme 

    No problem, Ryan!

    I haven't learned Laravel yet. I'm loyal to Symfony (fell in love with it!). Ok, so after I read your response, here's what I did:
    - Go to .env and change APP_ENV from 'dev' to 'prod'.
    - bin/console c:c (in case it changes sth)
    - bin/console about

    And here's what I see:
    -------------------- -------------------------------------------
    Symfony
    -------------------- -------------------------------------------
    Version 4.1.6
    End of maintenance 01/2019
    End of life 07/2019
    -------------------- -------------------------------------------
    Kernel
    -------------------- -------------------------------------------
    Type App\Kernel
    Name src
    Environment dev
    Debug true
    Charset UTF-8
    Root directory ./src
    Cache directory ./var/cache/dev (2.3 MiB)
    Log directory ./var/log (14 KiB)
    -------------------- -------------------------------------------
    PHP
    -------------------- -------------------------------------------
    Version 7.2.9
    Architecture 64 bits
    Intl locale fr_FR
    Timezone Europe/Zurich (2018-11-05T22:22:48+01:00)
    OPcache false
    APCu true
    Xdebug true
    -------------------- -------------------------------------------

    Do you see something that might help? See, Kernel env is still dev. I didn't "source .env" so you can see the issue... Thanks for your help!!!

  • 2018-11-05 weaverryan

    Hey Jérôme !

    Bah! I totally mis-read your "I'm not on a VM" message. LAME - sorry about that!

    Ok, I can tell for sure by what you're describing that, for some reason, Symfony is NOT reading your .env file. The "why" is the mystery :). Here are a few things to look into, if you see the error again:

    1) The logic for loading the .env file lives in bin/console and public/index.php. In there, you can literally put some debug code to make sure that the DotEnv class IS loading. I'm 98% sure that the problem is (for some reason) DotEnv not actually being called. The only reason that would happen is if (somehow) you have an APP_ENV env variable set... which is possible if you've tried Laravel before - and perhaps set it up somewhere. Or, I could be totally wrong ;).

    2) You can see what Symfony sees as your environment variables by running "php bin/console about" - it has a section where it shows the environment variables loaded from .env.

    Let me know if this helps you hunt anything down! Very interesting!

    Cheers!

  • 2018-11-05 Jérôme 

    Hello Ryan,

    I'm not using any VM... I just used composer to download the skeleton, just like you do in the tutorial. I just have a directory called the_spacebar. Diego and you are talking about VM, but I'm not using one. I just wanted to follow the tutorial as it is to learn the concepts with you.

    Maybe there's a problem when I composer require the symfony/skeleton or symfony/website-skeleton, because I tried to start another project and I also have this issue. I'm following the exact same process as you: go to .env, look for APP_ENV, it's value is "dev", I replace it by "prod" and then I just try to refresh the browser but it's not working. It's still the "dev" environment. For the changes to be taken into account, I have to go to my terminal (iTerm 2) and write "source .env"...

    In this tutorial, when you talk about creating a SLACK_WEBHOOK_ENDPOINT environment, I just followed the tutorial again, but it wasn't "seen" by the app... After a while, I just restarted the server, did a composer require dotenv and it was fixed. BUT! I'm not sure it was fixed by this or some other action... I don't know. And these problems are weird. I've been using SF since 3.3 and I never had these issues.

    If you have any suggestions or solutions to fix them, please don't hesitate to tell me... I'm still following the tutorials and practicing. Thank you again for the response!

  • 2018-11-05 weaverryan

    Hey Jérôme!

    I’m not sure why updating DotEnv would have helped, but I might know the root cause :). What VM are you using? If it’s homestead, I believe they set an APP_ENV env var (because Laravel also uses this). When Symfony sees this env var, it’s a signal that you are choosing to create real env vars, and so it skips reading .env. It sounds very similar to your issue. Let me know if I’m right!

    Cheers!

  • 2018-11-03 Jérôme 

    I updated dotenv and now it's working, I don't really understand why...

  • 2018-11-01 Jérôme 

    Nope. That's weird! I'm not on a vm... I'm as surprised as you are! :(

  • 2018-11-01 Diego Aguiar

    That's odd. If you are on Symfony4 and running on dev enviroment, then the container should be re-compiled after changing anything on ".env" file. It makes me think that probably you are working on a virtual machine and for any reason that file is not being auto-uploaded?

  • 2018-11-01 Jérôme 

    No, I already tried bin/console c:c and removing the var/cache entirely, it didn't solve the problem... And when I want to change the environment from dev to prod and vice versa, I have to do 'source .env' to make it work. Otherwise, the changes aren't taken into account... :( This is the first time it happens! I followed the tutorial precisely, I double checked the lines and everything is similar. Has something changed with SF4 that I'm not aware of? :/

    Thanks anyway!

  • 2018-11-01 Diego Aguiar

    Hey Jérôme 

    If you change something on your ".env" file, it does not get reflected? If that's correct, I believe you have a caching problem. Are you running in dev mode?
    Anyways, try removing everything inside "var/cache" and try again

    Cheers!

  • 2018-11-01 Jérôme 

    Hey,

    I did try everything, search everywhere but I didn't find a solution, so I figured that I should ask you directly. First of all, changes made in my .env file are not taken into account. I have to source .env with the command line so that the APP_ENV is updated... Is that normal? Also, I created the SLACK_WEBHOOK_ENDPOINT environment variable, but when I refresh I still see the error message saying that the environment variable is not found. Could you help me please?

    Thanks!

  • 2018-04-30 Victor Bocharsky

    Hey Petru,

    Good question! Well, phpinfo() is a debug function, you SHOULD totally avoid using it in production, as well as using dump(), var_dump(), etc. If you do not dump any sensitive information - I think it's ok, well, at least to read that env variables intruder would need to get access to your server. But hey, if he would have access to your server, he could read the parameters.yml file as well. So, probably, env vars are not more secure than old parameters.yml, but they are definitely more flexible.

    What about encryption, if you really worry about it and want to encrypt it - you can totally do it. What you need to do is just to encrypt all the values of env vars with some algorithm, for example, with base64 encode (yes, I know it's not too secure, because it's 2 ways algorithm which you can easily decode, but you probably will need to decode it anyway). So, when you will read the value of that encoded env var - you will need to do something like '%env(base64:DATABASE_PASSWORD)%', where base64 called operator, more about it here: https://symfony.com/blog/ne... . And you can even create your own operators if needed for such purposes. But as I said, probably unless you may pass encrypted variables directly to the service you use - you will need to decode this values which mean anyone may decode it if know the algorithm.

    I hope this helps.

    Cheers!

  • 2018-04-28 Petru Lebada

    Hey Ryan,

    I was wondering, is it really safe to store such sensitive information inside an env variable such as database connection(DATABASE_URL) without any kind of security(crypting or something else) ? I mean, they can be seen just through phpinfo page or is there another way to read them?