-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DI] Allow injecting ENV parameters at runtime using %env(MY_ENV_VAR)% #19681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Love it! Ship it! @stof can you take a look at this too please? In general, was the issue not that this approach would be problematic with bundles that used the config before the dumping or something? I seem to remember a discussion in this direction. It would be possible to have dynamic variable lookups before dumping, or from the loaded dumped cache, but not both? |
07caeea
to
f5cd82f
Compare
This is an interesting approach, but it is not as backwards compatible as one would think. There are bundles out there today, which as a part of their extension actually manipulate parameters. SncRedisBundle e.g. splits DSNs in the extension. Passing one of these parameters there will at best cause some kind of syntax error in the bundle, or at worst cause old environment values to be cached. |
if (isset($this->envCache[$name]) || array_key_exists($name, $this->envCache)) { | ||
return $this->envCache[$name]; | ||
} | ||
if (false !== $env = getenv($name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to define "providers" instead of calling getenv
directly ?
This way, we could use key/value store (like consul, etcd, ...) or even files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really good idea! I suggest to focus on env vars in this PR and open for more env-like providers next.
I don't think bc is the right concept for what you're saying, at least there is no bc break here, unless one already has a parameter named But you're right that this strategy is not compatible with any parameter transformations done by DI extensions. |
The problem isn't so much for bundle authors, as you say, they can just fix it, but for users. I'm just worried this will give users weird and possibly subtile errors. |
That's not to say I'm necessarily -1 though, this sort of approach (where you only have to send PRs to the few bundles that manipulate parameters) will get the entire ecosystem environment variable compatible a lot sooner. |
$defaultValue = parent::get($name); | ||
|
||
if (!is_scalar($defaultValue)) { | ||
throw new RuntimeException(sprintf('The default value of an env() parameter must be string, but "%s" given to "%s".', gettype($defaultValue), $name)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it says it should be string, but the statement checks for a scalar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something that can be concatenated is fine, but the error message is easier to act on this way to me.
Thank you for this ! Much needed |
64d613a
to
5d1cc63
Compare
From @iamluc:
The |
5d1cc63
to
465f5ab
Compare
@iamluc I would avoid calling consul during the container's boot. Instead I would use consul-template. |
Would it be a good idea to do one of the following: A: Add a B: Add a |
465f5ab
to
f7e0a54
Compare
Talking with @lyrixx , I reverted the split of the
|
@magnusnordlander I'd say no need, bundle authors don't necessarily need to know about that, and shouldn't be allowed to block what a user wants to do if it works for them IMHO. But let's see what others think of your proposal. |
I see no reason to make dynamic parameters configurable like Magnus suggests. It shouldn't matter parameters come from the environment, and it should be totally up to the end user. I'd only worry if the exception user gets is descriptive enough to see what's wrong. |
@nicolas-grekas: Bundle authors do need to know, because it means that they basically cannot write extensions or compiler passes that inspects the values of configuration (or, basically anything in the DIC). I don't imagine that bundle authors will block the user out of spite, but rather if the implementation of the extension or a compiler pass doesn't work with dynamic parameters. This isn't a hypothetical scenario, there are bundles out there today that do not work with this, bundles that by Symfony 3.1 standards do nothing wrong, and I think it's better that the user finds out due to an exception than subtle bugs. |
Tell if I'm wrong, but such extensions are already kind of broken, by not allowing unresolved parameters, isn't it? If yes, then nothing new here to me. |
@nicolas-grekas: To my knowledge, using unresolved parameters has never been possible? Parameters are resolved in It's slightly unusual (probably less than 10% of extensions, but not an insignificant number) to do a lot of processing of config values in the extension, but by no means broken according to Symfony 3.1 standards. |
Do we actually need the env placeholder system ? If the DI Configuration class accepts any string, the If it performs validation on the value (boolean field, number field, enum field, string with specific format...), the placeholder will also fail. |
In fact, resolving |
@mmucklo: I use https://github.com/vlucas/phpdotenv, and then add the following method to my AppKernel:
Works like a charm. |
…ect exception class. (paradajozsef) This PR was squashed before being merged into the 3.2-dev branch (closes #19994). Discussion ---------- [DependencyInjection] Env parameterbag fix: use the correct exception class. | Q | A | ------------- | --- | Branch? | "master" | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Small fix after #19681 No description needed imo. :) Commits ------- b016c60 [DependencyInjection] Env parameterbag fix: use the correct exception class.
@magnusnordlander Does this allow you to add parameters into container via dotenv? What I've done is made .env tracked just like parameters.yml (so that a change to a .env file will cause a re-compile of the cache) however I had to extend buildContainer and use a custom version of EnvParametersResource to do it. I'm wondering if with this change there's perhaps now a better way? (i.e. to track .env and to also make any parameters in .env, such as SYMFONY__SOMETHING__OR__OTHER to appear as parameters in the container, such as something.or.other)... |
@mmucklo: No, it allows me to add environment variables via dotenv. Those environment variables can then (now that this PR is merged) be referenced in your config using the |
Can/will this change be back-ported to Symfony2? |
@denormalizer No, new features are always only added to the current development version (i.e. the master branch). |
…kadoo) This PR was squashed before being merged into the 3.2-dev branch (closes #20199). Discussion ---------- [DependencyInjection] Fix duplication of placeholders | Q | A | ------------- | --- | Branch? | master | Bug fix? | yes | New feature? |no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #20198 | License | MIT | Doc PR | reference to the documentation PR, if any Fixes performance issues related to merging parameter bags when using the `env()` parameters introduced in #19681 Commits ------- 124f30d [DependencyInjection] Fix duplication of placeholders
@denormalizer,
ecentria/dynamic-parameters-bundle provides Implementation is forward compatible with Symfony 3.2 |
* | ||
* @throws EnvNotFoundException When the environment variable is not found and has no default value | ||
*/ | ||
protected function getEnv($name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicolas-grekas dumb question but why not having the default parameter value as a second argument here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for what purpose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks more elegant and natural than doing something like:
parameters:
env(FOO): bar
foo: "%env(FOO)%"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry but I don't understand. If you're suggesting to use %env(FOO, default)%
, then again, this is a parameter name, not a DSL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm suggesting to be able to do:
parameters:
foo: "%env(FOO, 'bar')%"
instead of the workaround above, although the gain might be limited by the difficulty of parsing the default value. I guess I'm just being a bit to envious on Laravel way of doing things there:
return [
'foo' => env('FOO', 'bar'),
];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, fully agree. It's really nice that parameters.yml(.dist)
can declare defaults. See e.g. symfony/demo@21fcb92
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my current project I dropped parameters.yml completely. I think its just unnecessary when using environment variables. For development I am using dotenv to bring the default values…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah actually I want to get rid of app_dev.php
as well so using dotenv, although doable it's a bit annoying (dotenv should not be used in production).
But the answers that have been given are quite helpful, thanks for it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but you shouldn't have unset environment variables in production either.
I think .env for development is a pretty good solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. The .env
file will only be loaded if we are in debug mode and environment is not prod. So in prod, I specify every variable in my container configuration e.g. docker-compose.yml
. For running unit tests or a composer install
while building the container I do an export on the .env.example
file which is similar to the previously parameters.yml.dist
…eters (nicolas-grekas) This PR was merged into the 3.2 branch. Discussion ---------- [DependencyInjection] Document %env(...)% dynamic parameters Ref. symfony/symfony#19681 Commits ------- 083da46 [DI] Document %env()% dynamic parameters
Hmm.. I might be a bit late to join this discussion, but since I am running into this issue............ Just for the sake of clearity (and to save myself headaches) we have a number of env variables in the config.yml and for those that are not critical in our application we have defined default values. I appreciate errors to point out problems, but in our case this is what we want. We have a set of defaults, and when needed we can overwrite the the value. And yes, some variables might not be loaded in every environment. But in this particular application and these specific variables we decided that this is OK and this is what we prefer. Partially to keep the config straight forward, prevent deployment issues over insignificant settings and to help new developers get up to speed quickly and that sorts of things. But as I understand, Symfony is now creating the bug for us to point out that there might be a bug? I feel that this should be optional. There are cases in which you don't want total protection. Other factors might outweigh the gain. And what about refactoring? I might not want to remove parameters from which I know I will need them very soon. I should be able to keep them without breaking the application. Also I should be able to work on a ticket to create or refactor configurations and I should be able to release that (assuming its valid of course) and continue to work on the implementation later if that suits our schedule better. Not every application is the same. I don't think this is a good change. Please correct me if I got the wrong idea. Kind regards, |
This is an alternative approach to #18155 for injecting env vars into container configurations.
With this PR, anywhere parameters are allowed, one can use
%env(ENV_VAR)%
to inject a dynamic env var. Additionally, if one sets a value to such parameters in e.g. theparameter.yml
file (env(ENV_VAR): foo
), this value will be used as a default value when the env var is not defined. If no default value is specified, anEnvVarNotFoundException
will be thrown at runtime.Unlike previous attempts that also used parameters (#16403), the implementation is compatible with DI extensions: before being dumped, env vars are resolved to uniquely identifiable string placeholders that can get through DI extensions manipulations. When dumped, these unique placeholders are replaced by dynamic calls to a getEnv method.
ping @magnusnordlander @dzuelke @fabpot