How to set .env values in laravel programmatically on the fly
PhpLaravelEnvironment VariablesPhp Problem Overview
I have a custom CMS that I am writing from scratch in Laravel and want to set env
values i.e. database details, mailer details, general configuration, etc from controller once the user sets up and want to give user the flexibility to change them on the go using the GUI that I am making.
So my question is how do I write the values received from user to the .env
file as an when I need from the controller.
And is it a good idea to build the .env
file on the go or is there any other way around it?
Thanks in advance.
Php Solutions
Solution 1 - Php
Since Laravel uses config files to access and store .env
data, you can set this data on the fly with config()
method:
config(['database.connections.mysql.host' => '127.0.0.1']);
To get this data use config()
:
config('database.connections.mysql.host')
>To set configuration values at runtime, pass an array to the config
helper
https://laravel.com/docs/5.3/configuration#accessing-configuration-values
Solution 2 - Php
Watch out! Not all variables in the laravel .env are stored in the config environment. To overwrite real .env content use simply:
putenv ("CUSTOM_VARIABLE=hero");
To read as usual, env('CUSTOM_VARIABLE') or env('CUSTOM_VARIABLE', 'devault')
NOTE: Depending on which part of your app uses the env setting, you may need to set the variable early by placing it into your index.php or bootstrap.php file. Setting it in your app service provider may be too late for some packages/uses of the env settings.
Solution 3 - Php
Based on josh's answer. I needed a way to replace the value of a key inside the .env
file.
But unlike josh's answer, I did not want to depend on knowing the current value or the current value being accessible in a config file at all.
Since my goal is to replace values that are used by Laravel Envoy which doesn't use a config file at all but instead uses the .env
file directly.
Here's my take on it:
public function setEnvironmentValue($envKey, $envValue)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
$oldValue = strtok($str, "{$envKey}=");
$str = str_replace("{$envKey}={$oldValue}", "{$envKey}={$envValue}\n", $str);
$fp = fopen($envFile, 'w');
fwrite($fp, $str);
fclose($fp);
}
Usage:
$this->setEnvironmentValue('DEPLOY_SERVER', '[email protected]');
Solution 4 - Php
More simplified:
public function putPermanentEnv($key, $value)
{
$path = app()->environmentFilePath();
$escaped = preg_quote('='.env($key), '/');
file_put_contents($path, preg_replace(
"/^{$key}{$escaped}/m",
"{$key}={$value}",
file_get_contents($path)
));
}
or as helper:
if ( ! function_exists('put_permanent_env'))
{
function put_permanent_env($key, $value)
{
$path = app()->environmentFilePath();
$escaped = preg_quote('='.env($key), '/');
file_put_contents($path, preg_replace(
"/^{$key}{$escaped}/m",
"{$key}={$value}",
file_get_contents($path)
));
}
}
Solution 5 - Php
Based on totymedli's answer.
Where it is required to change multiple enviroment variable values at once, you could pass an array (key->value). Any key not present previously will be added and a bool is returned so you can test for success.
public function setEnvironmentValue(array $values)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
if (count($values) > 0) {
foreach ($values as $envKey => $envValue) {
$str .= "\n"; // In case the searched variable is in the last line without \n
$keyPosition = strpos($str, "{$envKey}=");
$endOfLinePosition = strpos($str, "\n", $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
// If key does not exist, add it
if (!$keyPosition || !$endOfLinePosition || !$oldLine) {
$str .= "{$envKey}={$envValue}\n";
} else {
$str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
}
}
}
$str = substr($str, 0, -1);
if (!file_put_contents($envFile, $str)) return false;
return true;
}
Solution 6 - Php
#more simple way to cover .env you can do like this
$_ENV['key'] = 'value';
Solution 7 - Php
tl;dr
Based on vesperknight's answer I created a solution that doesn't use strtok
or env()
.
private function setEnvironmentValue($envKey, $envValue)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
$str .= "\n"; // In case the searched variable is in the last line without \n
$keyPosition = strpos($str, "{$envKey}=");
$endOfLinePosition = strpos($str, PHP_EOL, $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
$str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
$str = substr($str, 0, -1);
$fp = fopen($envFile, 'w');
fwrite($fp, $str);
fclose($fp);
}
Explanation
This doesn't use strtok
that might not work for some people, or env()
that won't work with double-quoted .env
variables which are evaluated and also interpolates embedded variables
KEY="Something with spaces or variables ${KEY2}"
Solution 8 - Php
In the event that you want these settings to be persisted to the environment file so they be loaded again later (even if the configuration is cached), you can use a function like this. I'll put the security caveat in there, that calls to a method like this should be gaurded tightly and user input should be sanitized properly.
private function setEnvironmentValue($environmentName, $configKey, $newValue) {
file_put_contents(App::environmentFilePath(), str_replace(
$environmentName . '=' . Config::get($configKey),
$environmentName . '=' . $newValue,
file_get_contents(App::environmentFilePath())
));
Config::set($configKey, $newValue);
// Reload the cached config
if (file_exists(App::getCachedConfigPath())) {
Artisan::call("config:cache");
}
}
An example of it's use would be;
$this->setEnvironmentValue('APP_LOG_LEVEL', 'app.log_level', 'debug');
$environmentName
is the key in the environment file (example.. APP_LOG_LEVEL)
$configKey
is the key used to access the configuration at runtime (example.. app.log_level (tinker config('app.log_level')
).
$newValue
is of course the new value you wish to persist.
Solution 9 - Php
If you don't need to save your changes to .env file, you could simply user this code :
\Illuminate\Support\Env::getRepository()->set('APP_NAME','New app name');
Solution 10 - Php
If you want to change it temporarily (e.g. for a test), this should work for Laravel 8:
<?php
namespace App\Helpers;
use Dotenv\Repository\Adapter\ImmutableWriter;
use Dotenv\Repository\AdapterRepository;
use Illuminate\Support\Env;
class DynamicEnvironment
{
public static function set(string $key, string $value)
{
$closure_adapter = \Closure::bind(function &(AdapterRepository $class) {
$closure_writer = \Closure::bind(function &(ImmutableWriter $class) {
return $class->writer;
}, null, ImmutableWriter::class);
return $closure_writer($class->writer);
}, null, AdapterRepository::class);
return $closure_adapter(Env::getRepository())->write($key, $value);
}
}
Usage:
App\Helpers\DynamicEnvironment::set("key_name", "value");
Solution 11 - Php
Based on totymedli's answer and Oluwafisayo's answer.
I set a little modification to change the .env file, It works too fine in Laravel 5.8, but when after I changed it the .env file was modificated I could see variables did not change after I restart with php artisan serve, so I tried to clear cache and others but I can not see a solution.
public function setEnvironmentValue(array $values)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
$str .= "\r\n";
if (count($values) > 0) {
foreach ($values as $envKey => $envValue) {
$keyPosition = strpos($str, "$envKey=");
$endOfLinePosition = strpos($str, "\n", $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
if (is_bool($keyPosition) && $keyPosition === false) {
// variable doesnot exist
$str .= "$envKey=$envValue";
$str .= "\r\n";
} else {
// variable exist
$str = str_replace($oldLine, "$envKey=$envValue", $str);
}
}
}
$str = substr($str, 0, -1);
if (!file_put_contents($envFile, $str)) {
return false;
}
app()->loadEnvironmentFrom($envFile);
return true;
}
So it rewrites correctly the .env file with the funtion setEnvironmentValue, but How can Laravel reload the new .env without to restart the system?
I was looking information about that and I found
Artisan::call('cache:clear');
but in local it does not work! for me, but when I uploaded the code and test in my serve it works to fine.
I tested it in Larave 5.8 and worked in my serve...
This could be a tip when you have a variable with more than one word and separetly with a space, the solution i did
public function update($variable, $value)
{
if ($variable == "APP_NAME" || $variable == "MAIL_FROM_NAME") {
$value = "\"$value\"";
}
$values = array(
$variable=>$value
);
$this->setEnvironmentValue($values);
Artisan::call('config:clear');
return true;
}
Solution 12 - Php
Tailor Otwell generate the laravel application key and set its value using this code (code modified for example purpose):
$escaped = preg_quote('='.config('broadcasting.default'), '/');
file_put_contents(app()->environmentFilePath(), preg_replace("/^BROADCAST_DRIVER{$escaped}/m", 'BROADCAST_DRIVER='.'pusher',
file_get_contents(app()->environmentFilePath())
));
You can find the code in the key generation class:
Illuminate\Foundation\Console\KeyGenerateCommand
Solution 13 - Php
you can use this package https://github.com/ImLiam/laravel-env-set-command
then use Artisan Facade to call artisan commands ex:
Artisan::call('php artisan env:set app_name Example')
Solution 14 - Php
PHP 8 solution
This solution is using env()
so should only be run when the configuration is NOT cached.
/**
* @param string $key
* @param string $value
*/
protected function setEnvValue(string $key, string $value)
{
$path = app()->environmentFilePath();
$env = file_get_contents($path);
$old_value = env($key);
if (!str_contains($env, $key.'=')) {
$env .= sprintf("%s=%s\n", $key, $value);
} else if ($old_value) {
$env = str_replace(sprintf('%s=%s', $key, $old_value), sprintf('%s=%s', $key, $value), $env);
} else {
$env = str_replace(sprintf('%s=', $key), sprintf('%s=%s',$key, $value), $env);
}
file_put_contents($path, $env);
}
Solution 15 - Php
this function update new value of existing key or add new key=value to end of file
function setEnv($envKey, $envValue) {
$path = app()->environmentFilePath();
$escaped = preg_quote('='.env($envKey), '/');
//update value of existing key
file_put_contents($path, preg_replace(
"/^{$envKey}{$escaped}/m",
"{$envKey}={$envValue}",
file_get_contents($path)
));
//if key not exist append key=value to end of file
$fp = fopen($path, "r");
$content = fread($fp, filesize($path));
fclose($fp);
if (strpos($content, $envKey .'=' . $envValue) == false && strpos($content, $envKey .'=' . '\"'.$envValue.'\"') == false){
file_put_contents($path, $content. "\n". $envKey .'=' . $envValue);
}
}
Solution 16 - Php
This solution builds upon the one provided by Elias Tutungi, it accepts multiple value changes and uses a Laravel Collection because foreach's are gross
function set_environment_value($values = [])
{
$path = app()->environmentFilePath();
collect($values)->map(function ($value, $key) use ($path) {
$escaped = preg_quote('='.env($key), '/');
file_put_contents($path, preg_replace(
"/^{$key}{$escaped}/m",
"{$key}={$value}",
file_get_contents($path)
));
});
return true;
}
Solution 17 - Php
You can use this custom method i located from internet ,
/**
* Calls the method
*/
public function something(){
// some code
$env_update = $this->changeEnv([
'DB_DATABASE' => 'new_db_name',
'DB_USERNAME' => 'new_db_user',
'DB_HOST' => 'new_db_host'
]);
if($env_update){
// Do something
} else {
// Do something else
}
// more code
}
protected function changeEnv($data = array()){
if(count($data) > 0){
// Read .env-file
$env = file_get_contents(base_path() . '/.env');
// Split string on every " " and write into array
$env = preg_split('/\s+/', $env);;
// Loop through given data
foreach((array)$data as $key => $value){
// Loop through .env-data
foreach($env as $env_key => $env_value){
// Turn the value into an array and stop after the first split
// So it's not possible to split e.g. the App-Key by accident
$entry = explode("=", $env_value, 2);
// Check, if new key fits the actual .env-key
if($entry[0] == $key){
// If yes, overwrite it with the new one
$env[$env_key] = $key . "=" . $value;
} else {
// If not, keep the old one
$env[$env_key] = $env_value;
}
}
}
// Turn the array back to an String
$env = implode("\n", $env);
// And overwrite the .env with the new data
file_put_contents(base_path() . '/.env', $env);
return true;
} else {
return false;
}
}
Solution 18 - Php
use the function below to change values in .env file laravel
public static function envUpdate($key, $value)
{
$path = base_path('.env');
if (file_exists($path)) {
file_put_contents($path, str_replace(
$key . '=' . env($key), $key . '=' . $value, file_get_contents($path)
));
}
}
Solution 19 - Php
Replace single value in .env file function:
/**
* @param string $key
* @param string $value
* @param null $env_path
*/
function set_env(string $key, string $value, $env_path = null)
{
$value = preg_replace('/\s+/', '', $value); //replace special ch
$key = strtoupper($key); //force upper for security
$env = file_get_contents(isset($env_path) ? $env_path : base_path('.env')); //fet .env file
$env = str_replace("$key=" . env($key), "$key=" . $value, $env); //replace value
/** Save file eith new content */
$env = file_put_contents(isset($env_path) ? $env_path : base_path('.env'), $env);
}
Example to local (laravel) use: set_env('APP_VERSION', 1.8)
.
Example to use custom path: set_env('APP_VERSION', 1.8, $envfilepath)
.