RFC: Adding support for `.env` files #224
Comments
How is a dotenv file better than other config file formats. Given we'd need to have/use a parser that supports basic bash syntax the adds complexity over something like an ini file. Could env files be integrated as config file reader for configure? |
If we are to add I also think that, similarly to what was done for
|
To outline the reasons I prefer it is conditional to being explicitly required by the end-user: 1 - if not, a lot of users will end up having it but not knowing what it does or not using it. |
Is there really much value to .env files if they are being parsed and handled by PHP? Seems like a fairly inefficient way to do configuration. I totally buy in on using environment variables for config, I just don't think that parsing effectively bash files in PHP is a good idea. Could we approach this problem in a different way by having conventions around ENV vars that a config reader could then convert into structured data? |
Well it works well for much of our config, just not stuff like the error handler. I don't think having some convention like For what it's worth, laravel basically calls env with smart defaults. |
IMO, env vars aren't for everyone. They've been working great for me and a lot of the core contributors I know and thus why I am biased on this RFC. Here's how I do it since our But I do remember having hacked my own for passing defaults as well. Basically, the idea was to allow configuration to be made either way (using // basics.php
function read($key, $default = null)
{
$result = env(strtoupper(str_replace('.', '_', $key));
if (is_null($result) && Configure::check($key)) {
$result = Configure::read($key);
}
if (is_null($result) && !is_null($default)) {
$result = $default;
}
return $result;
} Does that resemble what you are suggesting @markstory ? |
@jadb Not really, I was more wondering/proposing that env variables be sucked into Configure and renamed similar to |
Rails is doing a similar thing with secrets.yml |
@markstory The only problem with that is So I guess the two changes that can come about are:
I'm not sure how I feel about the second, but the first sounds like it could be generally useful and I'd be okay with that one. |
Wouldn't it be easier just to have default app.php config and override it with dev.php / production.php / backup.php / etc ? The name of a config file could be resolved from env. |
I'm not a fan of environment-specific config files. Having done so at scale, I can say that road is the road to failure. If you're going to resolve a filename based on env var, then why not just support the env vars themselves. |
I side with @josegonzalez. Again, having used both scenarios, environment variables all the way (added benefit: automatic support for heroku-style of hosting). |
Me neither. There is this environment, and environments that are irrelevant to the current install. |
Should we start listing the env names that we would use by conventions? |
@josegonzalez If the biggest blocker around env var inflection is the exception handler, couldn't we have that code read both the new and 'old' configuration names? |
I'd like that solution @markstory |
I added support to php-dotenv to parse <?php
// contents of a config class
$dotenv = new josegonzalez\Dotenv\Loader('path/to/.env');
$dotenv->setFilters(['josegonzalez\Dotenv\Filter\UnderscoreArrayFilter']);
$dotenv->parse();
$dotenv->filter();
return $dotenv->toArray(); A bit possible now I guess. Not sure anymore if we should add it? |
@josegonzalez We could include it for 3.1 as an alternative to using the current php config files. |
Would love that. |
|
1 similar comment
|
We'd need to have a patch for the aforementioned |
I was hoping to do the 3.1 RC this weekend. Adding configuration fallbacks sounds like the kind of thing we can squeeze in during an RC. |
Just did a simple test, and it looks like even with that, there would still be quite a few special cases we'd need to handle, so not worth doing in the core at all. I'll think on it a bit more. |
What are the other special cases @josegonzalez ? |
Anything with className. Also, the special cache configs with underscores, amongst others... |
Hrm. |
|
I would rather we not prefix with |
N00b question: How does this work with multiple apps on the same machine? I've never used the .env files but I'm teased to give it a try. Is the content of the .env file written to the global environment or just made available for env() within the Cake app? Because if I have two apps and both use the same names they'll get overriden? |
Your apache/nginx configuration would pass parameters to the fasctgi/fpm php process with these values. For background tasks, you would source in a |
@josegonzalez But I can understand this can be too much verbose, and I'm not against removing the 3 env variables we could add (with or without
|
The standard is sans prefix for most of these things. There isn't a |
@josegonzalez so no |
Note: One thing I've noticed is that for some keys in the A few keys that don't follow our "normal" naming schema:
For the most part, people don't need to modify the above via env vars, so thats nice. The real issue is just setting defaults for things that matter, like |
EmailProfile and EmailTransport would have been better in hindsight. What about those variables isn't normal? Is it that they have multi word keys with camelBacked names? |
Yeah, they end up getting multi-word keys if you do naive splitting. Here is what I have now: #!/usr/bin/env bash
# in config/.env
export APP_NAME=myapp
export DEBUG=true
export SECURITY_SALT="SOME_SALT1"
export DATABASE_URL="mysql://user:password@localhost/${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true"eIdentifiers=false&persistent=false"
export TEST_DATABASE_URL="mysql://user:password@localhost/test_${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true"eIdentifiers=false&persistent=false"
export CACHE_DURATION="2+ minutes"
export CACHE_DEFAULT_URL="file:///tmp/cache?prefix=${APP_NAME}_&duration=${CACHE_DURATION}"
export CACHE_CAKECORE_URL="file:///tmp/cache/persistent?prefix=${APP_NAME}_cake_core_&duration=${CACHE_DURATION}&serialize=true"
export CACHE_CAKEMODEL_URL="file:///tmp/cache/models?prefix=${APP_NAME}_cake_model_&duration=${CACHE_DURATION}&serialize=true"
export LOG_DEBUG_URL=file://logs?levels[]=notice&levels[]=info&levels[]=debug&file=debug
export LOG_ERROR_URL=file://logs?levels[]=warning&levels[]=error&levels[]=critical&levels[]=alert&levels[]=emergency&file=error
export EMAILTRANSPORT_DEFAULT_URL="mail://user:secret@localhost:25/?client=null&timeout=30&tls=null" <?php
// in config/env.php
use josegonzalez\Dotenv\Loader;
use Cake\Utility\Hash;
$config = [];
if (!env('APP_NAME')) {
$dotenv = new Loader([
__DIR__ . DS . '.env',
__DIR__ . DS . '.env.default',
]);
$dotenv->setFilters([
'josegonzalez\Dotenv\Filter\LowercaseKeyFilter',
'josegonzalez\Dotenv\Filter\UppercaseFirstKeyFilter',
'josegonzalez\Dotenv\Filter\UnderscoreArrayFilter',
function ($data) {
$keys = [
'Debug' => 'debug',
'Emailtransport' => 'EmailTransport',
'Database' => 'Datasources.default',
'Test.database' => 'Datasources.test',
'Test' => null,
'Cache.duration' => null,
'Cache.cakemodel' => 'Cache._cake_model_',
'Cache.cakecore' => 'Cache._cake_core_',
];
foreach ($keys as $key => $newKey) {
if ($newKey === null) {
$data = Hash::remove($data, $key);
continue;
}
$value = Hash::get($data, $key);
$data = Hash::remove($data, $key);
$data = Hash::insert($data, $newKey, $value);
}
return $data;
}
]);
$dotenv->parse();
$dotenv->filter();
$config = $dotenv->toArray();
}
return $config; // modified my config/bootstrap.php
try {
Configure::config('default', new PhpConfig());
Configure::load('app', 'default', false);
Configure::load('env', 'default', true);
} catch (\Exception $e) {
die($e->getMessage() . "\n");
} This will load up my env from a A few things I think we can do to ease this:
I'm happy to make a PR for Thoughts? |
I think return [
'debug' => env('CAKEPHP_DEBUG', true),
'Datasource' => [
'default' => [
'url' => env('CAKEPHP_DATASOURCE_DEFAULT')
]
]
]; |
@josegonzalez I'm Could we add other key configs in
|
I don't think we should promote changing the default timezone. Server/database time should always be in UTC to prevent errors in application configuration. I'd be happy to discuss this in depth, but I stand by that rule and so won't make any effort to do otherwise. Regarding default_locale, that makes sense to me. I'll add it as a new config under |
Also added App.default_locale. Refs #224
|
@josegonzalez By using your configurations i have the error:
And its because i commented export CACHE_CAKECORE_URL and export CACHE_CAKEMODEL_URL, can be this the issue and why? Thank you very much, very usefull thing the .env configuration! |
@arturmamedov |
@josegonzalez the env loader is still not part of cakephp, right? |
This is something that could help local developers configure their setups much quicker. With the added support for DSNs in the core, it is now quite easy to get setup.
Why
This isn't a new way to configure applications. Many of us are already taking advantage of this in production through process managers such as Circus, SupervisorD, and Upstart. As well, all popular webservers support the usage of environment variables (
SetEnv
in Apache, and various ways with Nginx). In production at least, environment variables allow the changing of configuration without a deploy, which can be a godsend if, for example, your database goes away and you need to swap to a backup.The other issue I'd like to tackle is different development environments for teams. It's annoying to have to change hardcoded config when moving from one database config to another (we don't all have
my_app:secret
setup), and having a.env
file can obviate the need for people stepping over each other while changing config.How
We can use josegonzalez/php-dotenv, a library I maintain, or vlucas/phpdotenv, which is used in Laravel. Both solve similar problems in different ways. The advantage with the former is that we have more control over features we want/need, though to be honest both would work adequately.
Using my library, we can add the following to our
bootstrap.php
file:Here is a sample
.env
file that I use forvagrant
based installations:I do a few replacements in my
app.php
file like so:I can likely make the library smarter and have it do it's replacements not only from
bash
but also from PHP constants or existing environment variables.Errata
We'd need to add documentation on how
.env
files work. I have quite a bit of it in the readme for my project, but we'd want to add this to the main docs as well.Note that this would maybe increase the complexity of setting up an app. We don't want to promote a
.env
file in production, but we do want to show users how to configure apps in production. Something to consider.Developers might think this is something we've stolen from other frameworks, though this sort of thing has been around for quite a while. Rails has had automatic support for DSNs in environment variables since at least 3.x, and the
friendsofcake/app-template
project has used them for at least a year. As well, most/all of the core team has used this sort of functionality in the past, though maybe not with a php library to support their dev cycle.Does adding this break BC? We've not yet made a release of the
app
composer project. Should we wait until a 3.1 CakePHP release before doing this? Or are we allowed to make changes like this without waiting for a major release.Finally, does everyone desire this change? I know this makes it easy for me to write my book, and probably makes tutorial writing slightly easier, but maybe this isn't right for our users. Thoughts?
The text was updated successfully, but these errors were encountered: