• 5
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

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.

      • 2
    • According to my opinion the env variables shouldn't be modified as env variables are not made for modifications, instead they should guide the app wih robust values. You should try another trick/hack to achieve your goal.
    • @SaumyaRastogi I needed this to make repo installing easier by copying a randomly generated app token that I need to store in .env. So there are legit uses.
    • @totymedli I don’t think so that it’s a legit use, instead make use of laravel commands like php artisan some_command. Laravel also uses php artisan key:generate to generate random key for the app. Maybe this helps you!
    • @SaumyaRastogi I use that. I set the .env file from there. The key is generated to the db. Without copying it to the .env file, I would have to fetch it in every request.

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

  • 43
Reply Report
    • It means you're using wrong path when trying to set data. Please give me example of what are you doing and then I'll be able to help you with it.
    • config(['DB_HOST'=> '127.0.0.1']); config(['DB_PORT'=> '3306']); config(['DB_DATABASE'=> 'qbi_cms']); config(['DB_USERNAME'=> 'root']); config(['DB_PASSWORD'=> '']); Auth::routes(); This is from my routes file
    • config(['something'=> 'data']); return env('something'); This is how I set it in controller but it is not returning anything.
    • You don't need to use env(). Just set data with config() and then get data with config('something').
      • 1
    • I setup my database credentials on runtime using this method and also have removed it from the .env file but it takes the default values ie. forge@localhost so this method is not working, I dont just want to put configuration, I even want to setup env variables like database details, mailing details, etc

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.

  • 29
Reply Report
      • 1
    • If you /really/ have to change env values at runtime, this seems a much better way to go than hacking the file's contents.
    • This is really good for unit testing with agency work where you will use the template again and want to set up weird environments that arent solved with phpunit.xml since its static
    • please mention that this does not edit the actual .env file in laravel and could be replaced by the next request with values in the actual .env file

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;

}
  • 14
Reply Report
      • 2
    • Hmm.. I'm trying to use this but the .env file never updates to what I pass as values. No errors or anything either, really weird. Tried a few other solutions in this threads but none works. Got any idea what could be causing this?

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', 'forge@122.11.244.10');
  • 14
Reply Report

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)
        ));
    }
}
  • 9
Reply Report

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.

  • 4
Reply Report

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}"
  • 4
Reply Report

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;
    }
  • 1
Reply Report

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;
    }
  • 0
Reply Report

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;
        }
    }
  • -1
Reply Report

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).

  • -1
Reply Report

Warm tip !!!

This article is reproduced from Stack Exchange / Stack Overflow, please click

Trending Tags

Related Questions