symfony: Environment Variables lack type - break built-in Symfony configuration

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? no
Symfony version 3.2

Related to #20434, if you try to pull data such as CACHE from an environment variable and pass it to Twig, Twig’s Bundle reads the type literally, as “false” or “true” - as it is passed from getenv.

How to Replicate

Add this environment variable:

CACHE false

In config.yml:

twig:
    cache: %env(CACHE)%

This results in a cache being created under web/false/, which is certainly not the intended result.

Note that this has other unintended consequences throughout Symfony, where a boolean is expected but not validated.

Solutions?

A simple solution could be to use the env() library created by Laravel as it provides for type coercion out of the box.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 5
  • Comments: 30 (19 by maintainers)

Commits related to this issue

Most upvoted comments

In case types are finally adopted for env vars, could we use Python’s dictionaries syntax instead of the proposed syntax?

# Current Proposal
Strings: %env(...)%
Booleans: %env:bool(...)%
Integers: %env:int(...)%
Floats: %env:float(...)%

# Proposal based on Python's syntax
Strings: %env(...)s%  (%env(...)% works too)
Booleans: %env(...)b%
Integers: %env(...)i%
Floats: %env(...)f%

We should move toward a solution because right now we can’t use dynamic config based on runtime for simple configuration (boolean)

Ex:

swiftmailer:
    disable_delivery: "%env(DISABLE_EMAIL_DELIVERY)%"
parameters:
    env(DISABLE_EMAIL_DELIVERY): true

Invalid type for path 'swiftmailer.mailers.default.disable_delivery'. Expected boolean, but got string

To me, it’s unfortunate that 3.3 doesn’t ship type hiting & conf validation 😦

I am sharing my hack here. If you really want to get your expected type from env, here is what I do:

parameters:
    env(MY_BOOL): "b:1;"

any_service:
    trigger: !php/object env(MY_BOOL)

but I agree with @fabpot after I read his design, I updated my code and did not need this use-case anymore.

Hi, environment Variables don’t work only in case

mailer_auth_mode mailer_encryption

I’m getting an error

Invalid configuration for path “swiftmailer.mailers.default.auth_mode”: The “%env(MAILER_AUTH_MODE)%” authentication mode is not supported

Symfony version 3.3.2 Replicate bug. 1.Run containers(Set env. variables)

docker-compose.yml

version: ‘3’

services: symfony: image: chorss/docker-extension-php:latest volumes: - “/path/to/your/folder/:/var/www/html” links: - db:mysql ports: - 80:80 environment: - DATABASE_DRIVER=pdo_mysql - DATABASE_HOST=mysql - DATABASE_NAME=symfony - DATABASE_USER=root - DATABASE_PASSWORD=root - DATABASE_CHARSET=UTF8 - DATABASE_PORT=3306 - MAILER_TRANSPORT=smtp - MAILER_HOST=127.0.0.1 - MAILER_USER=null - MAILER_PASSWORD=null - MAILER_PORT=null - SECRET_KEY=ThisTokenIsNotSoSecretChangeIt - MAILER_AUTH_MODE=login - MAILER_ENCRYPTION=ssl db: container_name: mysql image: mysql:5.7 ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: root

parameters.yml

parameters: database_driver: ‘%env(DATABASE_DRIVER)%’ database_charset: ‘%env(DATABASE_CHARSET)%’ 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(MAILER_TRANSPORT)%’ mailer_auth_mode: ‘%env(MAILER_AUTH_MODE)%’ mailer_host: ‘%env(MAILER_HOST)%’ mailer_user: ‘%env(MAILER_USER)%’ mailer_password: ‘%env(MAILER_PASSWORD)%’ mailer_encryption: %env(MAILER_ENCRYPTION)%’ mailer_port: ‘%env(MAILER_PORT)%’ secret: ‘%env(SECRET_KEY)%’

  1. Run containers
  2. Enter

localhost/web/app_dev.php

And #23888 😃

The workaround is to not use env params, but regular params for now. Or adjust your config constraints.

@smcjones

Keep in mind that using env vars does not replace parameters.

This is key; %foo% and %env(HOME)% are different things. For clarification;

parameters:
    foo: %env(HOME)%

services:
    foobar:
        class: stdClass
        properties:
            a: %foo%
            b: %env(HOME)%

Produces;

<parameter key="foo">%env(HOME)%</parameter>

<service id="foobar" class="stdClass">
  <property name="a">%env(HOME)%</property>
  <property name="b">%env(HOME)%</property>
</service>
$this->services['foobar'] = $instance = new \stdClass();

$instance->a = $this->getEnv('HOME');
$instance->b = $this->getEnv('HOME');

Parameters are resolved at compile time, env parameters at runtime. This is good!

https://symfony.com/blog/new-in-symfony-3-2-runtime-environment-variables also states that

Their main advantages are that they can be changed between deploys without changing any code

This is what’s happening here.

However, i do understand the confusion; as it highly looks like normal parameters. But your solution of moving things to config_prod.yml is the way to go here.

Not sure SF should do anything here, other then improving docs. Maybe it’s needs a different syntax to make it less confusing with parameters.

An environment variables is a string by design.