Load external environment parameters in Symfony 3.

2019-06-27 12:19发布

问题:

I have a parameters file in an application using symfony 3.2 console, config and YAML component , and i try to set external Parameters from environment variable in the Service Container parameters.

I create container builder.

$container = new ContainerBuilder();

Am using file locator to locate resources :

$container = new ContainerBuilder();

Loader resolver to load resources

LoaderResolver();

And using load method :

$this->load('parameters.yml');

parameters.yml file:

parameters:
  database:
    driver: pdo_mysql
    host: 127.0.0.1
    dbname: dbname
    user: env(VAL1)
    password: env(VAL2)
  Local: us-en

After compiling the container and try to check get values from parameters bag :

$container->getParameterBag()->all()

its return me values like this :

env_VAL1_3ec776edc429c1734ed780a29a0af538 , env_VAL2_3ec776edc429c1734ed780a29a0af538

I think the container can't resolve those values from the environment .

Note : i set the environment variable using :

$ export VAL1='SOME TEXT'

Anyone has an idea why ?

回答1:

So I've spent some time investigating this 'issue' and here is what I found out...

The only way to get env(VAL1) thingy working is to generate the container to PHP file using PHPDumper and then use the generated container. There is no other way to make it working because it only resolves environment in that file.

In the normal Symfony project there is a generated var/cache/dev/appDevDebugProjectContainer.php file. There is method getDynamicParameter which looks like this

 private function getDynamicParameter($name)
    {
        switch ($name) {
            case 'kernel.root_dir': $value = ($this->targetDirs[3].'/app'); break;
            case 'kernel.logs_dir': $value = ($this->targetDirs[2].'/logs'); break;
            case 'user': $value = $this->getEnv('VAL1'); break;
            case 'session.save_path': $value = ($this->targetDirs[3].'/app/../var/sessions/dev'); break;
            case 'router.resource': $value = ($this->targetDirs[3].'/app/config/routing_dev.yml'); break;
            default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name));
        }
        $this->loadedDynamicParameters[$name] = true;

        return $this->dynamicParameters[$name] = $value;
    }

It is the only place where the env(VAL1) is evaluated.

So for your case the solution is the following.

I used simplified parameters.yml:

parameters:
    user: '%env(VAL1)%'

The export I did:

export VAL1=abc

PHP code:

$container = new ContainerBuilder();

$loader = new YamlFileLoader(
    $container,
    new FileLocator('.')
);

$loader->load('parameters.yml');

$container->compile();

$dumper = new PhpDumper($container);

$content = $dumper->dump(
    [
        'class' => 'DumpedContainer',
        'base_class' => 'Container',
        'file' => 'DumpedContainer.php',
        'debug' => true
    ]
);

// Use this code if you want to write file to the disk
$cache = new ConfigCache('DumpedContainer.php', true);
$cache->write($content, $container->getResources());
require_once $cache->getPath();

// ... otherwise use this code
//$content = str_replace('<?php', '', $content);
//eval($content);

$container = new DumpedContainer();

$user = $container->getParameter('user'); // $user = 'abc'

In the generated code you'll see the following method that do the magic

private function getDynamicParameter($name)
{
    switch ($name) {
        case 'user': $value = $this->getEnv('VAL1'); break;
        default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name));
    }
    $this->loadedDynamicParameters[$name] = true;

    return $this->dynamicParameters[$name] = $value;
}

This solution works fine but looking at it I wonder if this is really needed in your project? What's wrong with getenv()?



回答2:

Please try:

parameters:
database:
    driver: pdo_mysql
    host: 127.0.0.1
    dbname: dbname
    user: '%env(VAL1)%'
    password: '%env(VAL2)%'
log_path: /logs

I think that's the problem. See this documentation for reference: http://symfony.com/doc/current/configuration/external_parameters.html



回答3:

If you use Symfony\Bundle\FrameworkBundle\Test\WebTestCase instead of \PHPUnit_Framework_TestCase Symfony3 do the magic for you.

$client = static::createClient();
$this->container = $client->getContainer();
$user = $container->getParameter('user'); // $user = 'abc'

take a look on http://symfony.com/doc/current/testing.html#your-first-functional-test

With this method you will able to tweak also Symfony configurations using *_test.yml specific files.

I think this is the right way.