Problem/Motivation
The symfony container allows to define service parameters from environment variables. (Symfony documentation on configuration and Environment Variable Processors)
However this does not work with Drupal.
Steps to reproduce
Add a service parameter like this:
parameters:
twig.config:
debug: '%env(bool:MY_TWIG_DEBUG_ENV_VARIABLE)%'
And observe the error:
In ParameterBag.php line 100:
The parameter "twig.config" has a dependency on a non-existent parameter "env(bool:MY_TWIG_DEBUG_ENV_VARIABLE)".
Proposed resolution
Use the EnvPlaceholderParameterBag and make sure environment variables are replaced correctly.
Remaining tasks
User interface changes
none
API changes
Support for environment variables in service parameters.
Data model changes
none
Release notes snippet
tbd
Comments
Comment #2
bircherAttached a simple patch to get things rolling.
We would still need a test for this, and most likely we would not want to resolve the environment variables when compiling the container but rather when using them, but that requires tracking down where to set that and this is enough for the use case I am having right now when environment variables are not changing until the container is compiled again.
Comment #4
andypostComment #7
jkdev commentedJust override the parameter from your own
ServiceProvider.Instead of using symfony magic and replace the way the container is being built, use the drupal/symfony way to alter services/parameters in the container:
https://www.drupal.org/docs/drupal-apis/services-and-dependency-injectio...
In essence what we did is to create file: my_module/src/MyModuleServiceProvider.php (the name matters) looks like this:
Make sure clear cache (so container will be compiled fresh) -
drush cr/drush cc containerComment #9
duadua commented@bircher
I added some basic test coverage for resolving the env variable at compile time.
Comment #10
duadua commentedI'm also playing around with the idea to support using environment variables during runtime, see the --runtime branch.
This is clearly a bit more involved, and I think belongs really into its own issue in the first place.
Comment #12
dieterholvoet commented@jkdev by doing it that way the values from environment variables are cached in the container, which means you need to rebuild caches anytime an environment variable is changed. I don't think we want that.
@duadua looks like you haven't pushed anything yet to the
issue-3249970--runtimeand3249970-support-setting-servicebranches. I'm not sure why, but it contains some commits from other issues. Onlyissue-3249970contains relevant commits.Comment #16
mr.baileysComment #17
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #19
spooky063 commentedThere is some problem with tests from `CKEditor5PluginManagerTest` class.
Functions `testInvalidPluginDefinitions` and `testDerivedPluginDefinitions` are not passed.
The error message from CI:
I don't know how to solve this issue.
Comment #20
godotislateHaven't completely found out why, but the DrupalKernel changes lead to the addition of Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass, and that makes it necessary to re-resolve the `%container.modules%` parameter to ModuleHandler in the test as well.
Comment #21
spooky063 commentedThanks a lot. It solved the problem. However, there are still 2 tests that haven't been passed relating to attributes and annotations on simple deriver.
See if someone can solve these problems.
Comment #22
spooky063 commentedIndeed, it's strange to have to reinject all the parameters. But given that the test recreates a ContainerBuilder with a FrozenParameterBag and, on the other side the Drupal container is compiled with an EnvPlaceholderParameterBag, I guess that's the reason.
I don't know if the test is just an edge case or if there's a real impact on Drupal's behavior.
Comment #23
spooky063 commentedComment #24
godotislateOne comment: should document the additional services/parameters that need to be re-resolved, accounting for the additional compiler pass.
Also, as tagged, this issue needs a change record documenting how to use the environment variable placeholders.
Comment #25
spooky063 commentedI don't think it's necessary to add an other comment for additional services that need to be re-resolved. In that case, only the comment on the test is needed because it's really connected to this behavior.
Concerning the documentation, I would agree to add a comment but I don't know where to put it. Does the
sites/default/default.services.ymlfile seem right to you (After that, the content of the documentation is the same as for Symfony)?I don't think we have access to the technical documentation repository to add a section.
Comment #26
godotislateSorry if this was unclear, but yes, this is the only comment that was needed. My previous post referred to my suggestion on the MR.
The ask isn't for more documentation in code. You can follow these instructions on how to add a change record to a Drupal issue. For an actual example, look at the change record documenting autowired services being added to Drupal.
Comment #27
spooky063 commentedOkay, I create a change record to Drupal issue.
Comment #28
nanakI'm getting a weird issue with an edge case. Reading the comments above, its seems to be related with the issue encountered in #20 #21 & #22
I have monolog installed, and in the parameters section of
monolog.services.yml, have these entries (shortened):With the patch installed, rebuilding caches fails with the following error:
You have requested a non-existent parameter "channel". Did you mean this: "monolog.channel_handlers"?Basically,
%%channel%%is interpreted as%channel%, so it tries to resolve the string as a service parameter namedchannel.If I then try to escape this sequence (so
%%%%channel%%%%), the registration succeeds, but the parameter is registered as%%channel%%, while it should be stored as%channel%A weird workaround I found to work is the following:
That way, registration succeeds, and the parameter is stored as expect, with a single
%.Comment #29
spooky063 commentedCan you tell us which version of the monolog module you're using (I can't find this parameter in the monolog module.)?
Comment #30
nanakIt's not related to monolog, it's just the mean through which I found the issue, it can be reproduced using core only (this project uses 10.3.14):
In a parameter_test_module.services.yml:
Upon clearing caches:
Without the patch, the parameter is properly registered in the container as
broken_parameter: '%%somevalue%%'Comment #31
spooky063 commentedI don't understand why you would use a parameter in this case. Why not pass the value directly.
After all, I don't know if your case is a problem.
Comment #32
nanakWell, the why do not really matter: it's a supported behavior according to symfony documentation, which works without the patch, so I'd say it is a problem, since it's a regression.
https://symfony.com/doc/current/configuration.html#configuration-parameters
I suspect some kind of double resolution of parameters within a compiler pass (or rather, two passes); but I can't say which one, nor why it kicks twice
Comment #33
spooky063 commentedI suppose the part for configuration parameters with %% needs to be, at least commented. Or better refactored.
Perhaps we could also modify the yaml schema for services. Visual Studio Code doesn't like the !php/[...] part.
Comment #34
godotislateI think this change in DrupalKernel might be the wrong approach:
Looking at the documentation for
\Symfony\Component\DependencyInjection\ContainerBuilder::compile(bool $resolveEnvPlaceholders = false):Generally the Drupal container does get dumped (and cached), outside of kernel tests. So
$resolveEnvPlaceholdersshould not be set to TRUE per that documentation. In addition, resolving the environment placeholders at compile time would mean that it does not work as Symfony documents:Instead, with the placeholders being resolved at compile time, any changes to env variables after the container is cached would not update the container parameter values without a cache rebuild. (If it's fine to support environment variables this way for Drupal, then that difference would need to be documented in the change record.)
And also, as seen in #20 and #28, the extra compile pass resolving the environment variable placeholders is having unexpected side effects.
I think the necessary change to support environment variables is a larger lift:
Drupal\Component\DependencyInjection\Containerwould need to reproduce the environment variable replacement functionality thatSymfony\Component\DependencyInjection\Containerhas, but since the Drupal container is quite different from Symfony's, this effort doesn't look straightforward to me.Separately, I think changing the Yaml parser to handle constants is out of scope for this issue. Work for that is being done in #2951046: Allow parsing and writing PHP class constants and enums in YAML files.
Comment #35
spooky063 commentedI guess you are right.
I also don't think that modifying the
Drupal\Component\DependencyInjection\Containerfile is a good idea. It's well specified in the comments the reasons for not instantiating methods likegetParameterBag(). Also, why uppercase parameters are not supported (e.g. env(KEY): 'value').Comment #36
spooky063 commentedMaybe @Jkdev approach #7 is the closest to Drupal way.
Here's a first idea (beta):
Comment #40
alexpottEnvironmental parameters need to be resolved at runtime - having them only take affect on cache rebuild would be a really odd behaviour divergence from Symfony. This means we have to change our approach on this issue and store enviroment parameters separately from the other parameters so we can resolve them in \Drupal\Component\DependencyInjection\Container::getParameter(). Shouldn't be too bad.
Comment #41
alexpottOk I've got this working, assisted by AI. This is all quite messy because we're not using Symfony's container dumping which handles environment based parameters much more elegantly - the gets from the environment are written to the dumped PHP. Therefore we perhaps should block this on #3583505: Use Symfony PhpDumper instead of a serialized array container structure which would mean we'd have to do less work.
Comment #42
godotislateBlocking on #3583505: Use Symfony PhpDumper instead of a serialized array container structure per #41.