'Symfony 4: how to allow dynamic values for array nodes in bundle configuration?

In the Configuration of my bundle I have defined the following for example:

$treeBuilder = new TreeBuilder('foo');
$treeBuilder
    ->getRootNode()
    ->children()
        ->arrayNode('foobar')
            ->scalarPrototype()->end()
            ->defaultValue([])
        ->end()
    ->end()
;

i. e., the bundle expects an array of values in foo.foobar. Now, in the case of my bundle, these values are dependent on the environment the application is run in. Thus it would make sense to provide this configuration value through an environment variable. Since Symfony 3.4 values of environment variables can be processed. So this array could be stored JSON encoded in the environment variable and then be decoded via %env(json:FOO)%. So instead of having to write

foo:
    foobar:
        - Lorem
        - Ipsum
        - Dolor

one could instead use

foo:
    foobar: '%env(json:FOO)%'

where

FOO=["Lorem","Ipsum","Dolor"]

However, when trying to do that Symfony throws the following exception:

A dynamic value is not compatible with a "Symfony\Component\Config\Definition\PrototypedArrayNode" node type at path "foo.foobar".

What is the correct way of allowing such dynamic values for array nodes? Do I need to implement my own normalization in the configuration tree for example?



Solution 1:[1]

There is a big difference between static yaml config and '%env(json:FOO)%': content of env variable is not available at compile time. See docs:

You can reference environment variables using the special syntax %env(ENV_VAR_NAME)%. The values of these options are resolved at runtime (only once per request, to not impact performance).

In your BundleExtension (at compile time) your config value foobar will contain string like env_514deb50f3dcab34_json_FOO_ac251f2b9ef3c85f94a68df880763311 (instead of real array).

That means you can't conditionally load some configuration in your bundle, for example.
However, this node should be validated at compile time. In case of JSON it cannot be validated as scalarNode(...), because contain JSON env-processor. It cannot be validated as arrayNode(...), because ArrayNode does not support dynamic values. So you may skip the validation:

$treeBuilder
    ->getRootNode()
    ->children()
        ->variableNode('foobar')->end()
    ->end()
;

You may also add two different config items in bundle: for static configuration (as arrayNode) and for dynamic configuration (as variableNode) and then check in then extensions that only one of them is defined.

PS: It looks inconvenient. But these are quite natural limitations, if we remember that environment variables can be changed in runtime (i.e. after compiling the container).

Solution 2:[2]

you need to define the JSON in the .env file as string

this is correct: FOO='["Lorem","Ipsum","Dolor"]'

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 IStranger
Solution 2 StFroyd