How to change the app environment at run time? - php

How can I change the app environment at run time?
I have some classes that only bind in the service provider in production. I'd like to assert with a unit test that they are properly bound. For other environment variables, I can set them with the config helper and then simply call resetApplication in tearDown but for some reason the variable set by APP_ENV doesn't change.
dump(app()->environment()); // "testing"
config(['app.env' => 'production']);
dump(app()->environment()); // "testing"
What can I do to get app()->environment() to return production at run time?

app()->environment() reads directly from the variables specified in your .env file rather than the configuration files.
You could take two approaches to solve your problem.
1. Read the environment variables from the config file rather than the .env file.
dump(config('app.env')); // "testing"
config(['app.env' => 'production']);
dump(config('app.env')); // "production"
2. Change the value of 'env' in the current app instance by changing the value of app()['env'].
dump(app()->environment()); // "testing"
app()['env'] = 'production';
dump(app()->environment()); // "production"

I've noticed that above answer can be a bit dangerous: You might be overwriting the 'env' key, without truly switching environments. Suddenly your app says that you are in testing, while your DB connection is still set to production.
Normally you just want to really stick to the Laravel best practice of defining ONE environment for each literal environment, but for my use case I needed to temporarily & programmatically switch between multiple environments within a single artisan script.
My solution (works in Laravel 5.8+ with DotEnv3) would be to really reboot the application:
<?php
$basepath = app()->basePath();
$env = app()->basePath('.env.alternative');
$boot = app()->basePath('bootstrap/app.php');
// Overwrite webserver env
(new Dotenv($basepath,'.env.alternative'))->overload();
// Reboot the application
(require $boot)
->loadEnvironmentFrom($env)
->make(Kernel::class)
->bootstrap();
// This returns 'mysql_alternative', as defined in .env.alternative
dd(DB::connection()->getName())
Disclaimer: I've only tested this to the extend of my own code.

From a testing point of view, then in the setUp() method:
$this->app['env'] = 'production';
But remember, only do this in a test class, specifically testing production runs. Otherwise, do it at the top of the test method, remembering to set it back to testing at the end.
If you adjust the setUp() method, remember your tearDown() method and change it to testing. By altering the $this->app['env'], you are directly changing the Applications environment, which is why it's so important to remember to set it back as it will produce side effects, also only do this in testing .

Related

Get All ENV variables in Laravel

Laravel has a .env file that contains various variables. Is there a way to get all the variables in one line of PHP code? I don't want to write
echo env('APP_DEBUG')
echo env('APP_URL')
etc...
I have tried
env("*")
env("*")
but none works.
This should work:
$_ENV; // gives all env variables.
To get a single env variable:
$_ENV['VARIABLE'];
The only safe way for reading all env() attributes is to use Dotenv directly :
$env = Dotenv\Dotenv::createArrayBacked(base_path())->load()
This method is usable on every environment : cli, dispatched jobs, without proper web server and even if php.ini is ignoring env.

Getting environement variables in Symfony 2.4

I've been struggling to get the necessary env variables in a Symfony 2.4 application. The idea is to put the application in a docker container that will be managed by Amazon ECS.
I have tried the following :
1 - export a variable with :
export SYMFONY__DATABASE__HOST=blabla
then in parameters.yml.dist, get it with :
database_host : '%database.host%'
Didn't work. After composer install, en parameter.yml is created with that look like parameters.yml.dist. The values have not been translated to whatever is in the env variables.
I saw somewhere that I need to disable the incenteev bundle. Didn't help either.
2 - Add the variables in composer.json using incenteev's "env_map"
"env-map": {
"database_host": "DB_HOSTNAME"
}
DB_HOSTNAME being an env variable that I export in the same way
export DB_HOSTNAME=balbla.com
When I type php -i | grep DB, I do see those variables.
Then when I type composer install --no-interaction, in the first case I get this error message : You have requested a non-existent parameter "database.host".
In the second case, parameters.yml is created with whatever values are in parameters.yml.dist, and the env variable I added to composer.json is not used any where.
https://github.com/symfony/symfony/issues/7555
and
http://symfony.com/doc/current/configuration/external_parameters.html
did not help much.
Any Ideas guys? I really would like to get those env variables in the cleanest way possible.
I have run into the same trouble once. I found a way to get around it like Platform.sh do to host their Symfony applications. It's available on Github :
https://github.com/platformsh/platformsh-example-symfony
So basically, you create a parameters_prod.php config file wich you import in config.yml. Inside, you test to see if SYMOFNY_ENV=prod (which you need to set up when you deploy in production anyway). If this variable is set, you get what's in parameters_prod.php, otherwise you get what's in parameters.yml (for the dev environment).
And to get you environment variables in parameters_prod.php, you use the getenv() PHP method.
So basically, your parameters_prod.php will look something like this :
<?php
if (getenv('SYMFONY_ENV') == 'prod') {
$container->setParameter('database_driver', 'pdo_mysql');
$container->setParameter('database_port', 3306);
$container->setParameter('database_host', getenv('DATABASE_HOST'));
$container->setParameter('database_name', getenv('DATABASE_NAME'));
$container->setParameter('database_user', getenv('DATABASE_USER'));
$container->setParameter('database_password', getenv('DATABASE_PASSWORD'));
}
Docker will be able to get these environment variable without any thing else to do if you launch Apache in foreground mode as the entrypoint of your container (which you should anyway, otherwise the containers will not stay alive)

Where to Set Composer Environment Variables

I don't want to disable Xdebug, I don't care that it's slower, so I see there is an environment variable COMPOSER_DISABLE_XDEBUG_WARN and the docs mention setting it in the config.
So, I opened my global /Users/username/.composer/config.json and add:
{
"config": {
"COMPOSER_DISABLE_XDEBUG_WARN": true
}
}
No change.
Where or how do I set this environment variable to get rid of this damn message?
The PHP CLI inherits the environment variables from your shell. You can add the following line to your .bash_profile (or whatever you use for your shell).
export COMPOSER_DISABLE_XDEBUG_WARN=1
Make sure to close the terminal, and reopen it to load the new variable.
Source: https://stackoverflow.com/a/18098263/58795
Use COMPOSER_DISABLE_XDEBUG_WARN as ENVVAR https://github.com/composer/composer/issues/4622#issuecomment-158678115
As per Composer documentation for this flag, ...
"COMPOSER_DISABLE_XDEBUG_WARN": 1
set it to 1, not true to disable the warning. (Ya, I know, if I was coding this part of Composer I would've accepted any truthy value but what can you do, ask for your money back?)

Laravel - Multiple Production Environments

We have a Laravel 4.2 site based in the US that we're looking to whitelabel in Canada then the UK. I have a local and test environment for both (staging vs canada_staging, etc), and the problem I have with production is we have a lot of if conditions that checks if the environment is production (if (App::environment() === 'production') for instance). I could use production for both, but each site has its own specific configuration and language files (for instance they have provinces instead of states). Is there an easy way to overcome this situation?
Yes, you can create file app/bootstrap/environment.php on each server and define env.:
<?php
return 'production-us;
In app/bootstrap/start.php in detect env. section add this:
$env = $app->detectEnvironment(function ()
{
return require __DIR__.'/environment.php';
});
You will have unique env. on each server in the easiest way.
Related post here.

Environment detection in Laravel 4.1+

Laravel 4.1 removed the feature to use the domain for detecting what environment the app is running in. Reading the docs they now suggest using host names. However, to me that seems cumbersome if you are working in a team. Should everyone change the bootstrap/start.php file and add their own host name to be able to run the app in a dev environment? Also, what if you want to have two different environments on the same machine?
How to best detect the environment if you are working in a team in Laravel 4.1+?
Here is my settings from bootstrap/start.php file:
$env = $app->detectEnvironment(function() use($app) {
return getenv('ENV') ?: ($app->runningInConsole() ? 'local' : 'production');
});
Instead of default array, this method in my case is returning closure with ternary. That way I got more flexibility in choosing desired environment. You can use switch statement also. Laravel will read return value and configure itself.
With getenv native function, I am just listening for a given environment. If my app is on the server, then it will "pick" server configurations. If locally, then it will pick local (or development)
And don't forget to create custom directories for you environemnts in app/config
There is also testing env, which is choosen automatically, every time you are unit testing app.
Laravel makes working with environments really fun.
UPDATE:
With environments we are mostly cencerned about db credentials.
For production I use Fortrabbit, so when configuring new app on server, fortrabbit is generating those values for me. I just need to declare them. e.g. DB of just database or db... Or DB_HOST or HOST ...
Locally, those values are the one you use for your localhost/mysql settings.
Update:
In Laravel 5.0 environment detection is no longer needed in the same way. In the .env file you can simply have a variable for which environment the app should run in.
Old answer for Laravel <= 4.2
What I ended up doing is very close to what carousel suggested. Anyway, thought I would share it. Here is the relevant part of our bootstrap/start.php file:
$env = $app->detectEnvironment(function ()
{
if($app->runningInConsole())
return "development";
$validEnvironments = array("development", "staging", "production");
if (in_array(getenv('APP_ENV'), $validEnvironments)) {
return getenv('APP_ENV');
}
throw new Exception("Environment variable not set or is not valid. See developer manual for further information.");
});
This way all team members have to declare an environment variable somewhere. I haven't really decided if throwing an exception if the environment variable is not set or just default to production is the best thing. However, with the above, it's easy to change.
For me, I just use 'dev' => '*.local' and it works. I haven't 100% tested in a team situation but I think it'd work (big assumption alert:) assuming you're on OSX and get the default Alexs-iMac.local-like hostname.
As for faking an environment, I'm not sure it's really supported. It'll be doable, but in general the whole point of environments is that dev has entirely different needs to production and the two are mutually exclusive. Having the ability to switch on one physical environment seems counter to that goal.
Laravel 4.1 and 4.2 detects the environments through the machine names specified in the "bootstrap/start.php" file.
For example, in my case the config becomes:
$env = $app->detectEnvironment(array(
'local' => array('Victor.local', 'Victor-PC'),
));
This means that Laravel will use the 'local' environment settings for both machines: 'Victor.local' (a Mac) and 'Victor-PC' (Windows).
This way you can regsiter several machines to work as local environment. Other environments can be registered too.
In order to know the current machine name, you can use the following PHP code:
<?php echo gethostname(); ?>
Hope it helps!
You can use something like this:
$env = $app->detectEnvironment(function(){
if($_SERVER['HTTP_HOST'] == 'youdomain_local')
{
return 'local';
}elseif($_SERVER['HTTP_HOST'] == 'youdomain_team')
{
return 'team';
}else{
return 'production';
}
});
what i did is, make dir app/config/local and use code
$env = $app->detectEnvironment(function(){
return $_SERVER['HTTP_HOST']=="localhost"?"local":"production";
});
For localhost and online.
I didn't like that production was default, so I made it anything other than live server will go to local configs:
in bootstrap/start.php :
$env = $app->detectEnvironment(function(){
if (gethostname() !== 'live_server_hostname'){
return 'local';
} else {
return 'production';
}
});
In bootstrap/start.php define this:
$env = $app->detectEnvironment(function() use($app) {
$enviromentsHosts = [
'localhost',
'local',
];
if ($app->runningInConsole() || in_array($app['request']->getHost(), $enviromentsHosts)) {
return 'local';
} else {
return 'production';
}
});
I believe it is better to use only the resources of Laravel 4

Categories