如何在 laravel 中以编程方式即时设置 .env 值

Posted

技术标签:

【中文标题】如何在 laravel 中以编程方式即时设置 .env 值【英文标题】:How to set .env values in laravel programmatically on the fly 【发布时间】:2017-03-19 21:50:34 【问题描述】:

我有一个自定义 CMS,我在 Laravel 中从头开始编写,并希望在用户设置后从控制器设置 env 值,即数据库详细信息、邮件详细信息、常规配置等,并希望用户可以灵活地使用我正在制作的 GUI 随时更改它们。

所以我的问题是,当我需要控制器时,如何将用户收到的值写入.env 文件。

在旅途中构建.env 文件是个好主意还是有其他解决方法?

提前致谢。

【问题讨论】:

根据我的观点,不应修改 env 变量,因为 env 变量不是为修改而进行的,而是应该以稳健的值指导应用程序。你应该尝试另一个技巧/黑客来实现你的目标。 我同意@SaumyaRastogi...环境变量不应该被修改 @SaumyaRastogi 我需要这个来通过复制随机生成的应用程序令牌来更轻松地安装 repo,我需要将它存储在.env 中。所以有合法用途。 @totymedli 我不认为这是一个合法的用途,而是使用像 php artisan some_command 这样的 laravel 命令。 Laravel 还使用php artisan key:generate 为应用程序生成随机密钥。也许这对你有帮助! @SaumyaRastogi 我用那个。我从那里设置了.env 文件。密钥生成到数据库。如果不将其复制到 .env 文件中,我将不得不在每个请求中获取它。 【参考方案1】:

由于 Laravel 使用配置文件来访问和存储 .env 数据,您可以使用 config() 方法即时设置这些数据:

config(['database.connections.mysql.host' => '127.0.0.1']);

要获取此数据,请使用config():

config('database.connections.mysql.host')

要在运行时设置配置值,请将数组传递给 config 助手

https://laravel.com/docs/5.3/configuration#accessing-configuration-values

【讨论】:

config(['something'=> 'data']); return env('something'); 这是我在控制器中设置的方式,但它没有返回任何内容。 您不需要使用env()。只需使用config() 设置数据,然后使用config('something') 获取数据。 我使用此方法在运行时设置了我的数据库凭据,并且还从 .env 文件中删除了它,但它采用默认值,即。 forge@localhost 所以这个方法行不通,我不只是想进行配置,我什至想设置环境变量,如数据库详细信息、邮件详细信息等 这意味着您在尝试设置数据时使用了错误的路径。请举例说明您在做什么,然后我会为您提供帮助。 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(); 这是我的路线文件【参考方案2】:

小心!并非 laravel .env 中的所有变量都存储在配置环境中。 要覆盖真实的 .env 内容,只需使用:

putenv ("CUSTOM_VARIABLE=hero");

如常读取,env('CUSTOM_VARIABLE') 或 env('CUSTOM_VARIABLE', 'devault')

注意:根据应用程序的哪个部分使用 env 设置,您可能需要提前设置变量,方法是将其放入 index.php 或 bootstrap.php 文件中。对于环境设置的某些包/使用而言,在您的应用服务提供商中设置它可能为时已晚。

【讨论】:

如果您/真​​的/必须在运行时更改 env 值,这似乎比破解文件内容更好。 这对于代理工作的单元测试非常有用,您将再次使用模板并希望设置由于 phpunit.xml 静态而无法解决的奇怪环境 请注意,这不会在 laravel 中编辑实际的 .env 文件,并且可以用实际 .env 文件中的值替换为下一个请求 这是最好的答案。我正在使用它来更改 APP_URL,因为我有域名别名,并且 APP_URL 需要设置为 $_SERVER['HTTP_HOST']【参考方案3】:

根据乔希的回答。我需要一种方法来替换 .env 文件中的键值。

但与 josh 的回答不同,我根本不想依赖于知道当前值或配置文件中可访问的当前值。

因为我的目标是替换 Laravel Envoy 使用的值,它根本不使用配置文件,而是直接使用 .env 文件。

这是我的看法:

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

用法:

$this->setEnvironmentValue('DEPLOY_SERVER', 'forge@122.11.244.10');

【讨论】:

strtok() 部分对我不起作用。我将其替换为:$oldValue = env($envKey); @PiotrSuchanek 这不适用于使用双引号的变量,例如:KEY="Something with spaces or variables $KEY2"。见better solution in my answer。【参考方案4】:

基于 totymedli 的回答。

如果需要一次更改多个环境变量值,您可以传递一个数组(键->值)。将添加以前不存在的任何键并返回一个布尔值,以便您测试是否成功。

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;


【讨论】:

嗯.. 我正在尝试使用它,但 .env 文件永远不会更新为我作为值传递的内容。没有错误或任何东西,真的很奇怪。在这个线程中尝试了其他一些解决方案,但没有一个有效。知道是什么原因造成的吗?【参考方案5】:

更简化:

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

或作为助手:

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

【讨论】:

【参考方案6】:

#更简单的覆盖.env的方法你可以这样做

$_ENV['key'] = 'value';

【讨论】:

感谢@bty_cj 的回答,我相信很多人会觉得这很有用。【参考方案7】:

tl;博士

基于vesperknight's answer,我创建了一个不使用strtokenv() 的解决方案。

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

说明

这不使用可能对某些人不起作用的strtok,或不适用于双引号.env 变量的env(),这些变量被评估并插入嵌入变量

KEY="Something with spaces or variables $KEY2"

【讨论】:

这将修改#CACHE_DRIVER=...(如果已注释)而不是未注释的(真实的)【参考方案8】:

如果您希望将这些设置保存到环境文件中以便稍后再次加载它们(即使配置被缓存),您可以使用这样的函数。我将把安全警告放在那里,对这样的方法的调用应该被严格保护,并且用户输入应该被正确地清理。

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");
    

它的使用示例是;

$this->setEnvironmentValue('APP_LOG_LEVEL', 'app.log_level', 'debug');

$environmentName是环境文件中的key(例如..APP_LOG_LEVEL)

$configKey 是用于在运行时访问配置的密钥(例如 ..app.log_level (tinker config('app.log_level'))。

$newValue 当然是您希望保留的新值。

【讨论】:

你能告诉我我应该把这段代码添加到哪个文件吗? 它说找不到配置 确保文件顶部有use Config;【参考方案9】:

基于 totymedli 的回答和 Oluwafisayo 的回答。

我设置了一些修改来更改 .env 文件,它在 Laravel 5.8 中工作得很好,但是当我更改它之后,.env 文件被修改了,我可以看到在我用 php artisan serve 重新启动后变量没有改变,所以我试图清除缓存和其他,但我看不到解决方案。

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;

所以它使用函数 setEnvironmentValue 正确地重写了 .env 文件,但是 Laravel 如何在不重新启动系统的情况下重新加载新的 .env 文件?

我正在寻找这方面的信息,我发现了

    Artisan::call('cache:clear');

但在本地它不起作用!对我来说,但是当我上传代码并在我的服务中进行测试时,它可以正常工作。

我在 Larave 5.8 中对其进行了测试并在我的服务中工作...

当您有一个包含多个单词且单独带有空格的变量时,这可能是一个提示,我所做的解决方案

    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;
    

【讨论】:

【参考方案10】:

Tailor Otwell 使用此代码生成 laravel 应用程序密钥并设置其值(代码已修改为示例目的):

$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())
));

您可以在密钥生成类中找到代码: Illuminate\Foundation\Console\KeyGenerateCommand

【讨论】:

【参考方案11】:

如果您不需要将更改保存到 .env 文件,您可以简单地使用以下代码:

\Illuminate\Support\Env::getRepository()->set('APP_NAME','New app name');

【讨论】:

似乎对我不起作用;在返回视图的控制器方法中使用上面的代码,在视图中调用echo env("APP_NAME"),清除缓存,没有变化。 它在控制器方法中适用于我,然后在 Laravel 版本 8.42.0 的视图中回显【参考方案12】:

如果你想临时改变它(例如为了测试),这应该适用于 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);
    


用法:

App\Helpers\DynamicEnvironment::set("key_name", "value");

【讨论】:

【参考方案13】:

你可以使用这个包 https://github.com/ImLiam/laravel-env-set-command

然后使用 Artisan Facade 调用 artisan 命令,例如:

Artisan::call('php artisan env:set app_name Example')

【讨论】:

我真的不喜欢这个解决方案,因为它更新了 .env 文件。但如果这就是你要找的东西,那就太棒了。【参考方案14】:

PHP 8 解决方案

此解决方案使用env(),因此仅应在未缓存配置时运行。

/**
 * @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);

【讨论】:

如何改进写入.env 文件的其他半打答案?【参考方案15】:

使用下面的函数更改 .env 文件 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)
        ));
    

【讨论】:

【参考方案16】:

此函数更新现有键的新值或将新键=值添加到文件末尾

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

【讨论】:

【参考方案17】:

此解决方案基于 Elias Tutungi 提供的解决方案,它接受多个值更改并使用 Laravel 集合,因为 foreach 很糟糕

    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;
    

【讨论】:

【参考方案18】:

您可以使用我从互联网上找到的这种自定义方法,

  /**
 * 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;
        
    

【讨论】:

【参考方案19】:

替换 .env 文件函数中的单个值

/**
 * @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);

本地(laravel)使用示例:set_env('APP_VERSION', 1.8)

使用自定义路径的示例:set_env('APP_VERSION', 1.8, $envfilepath)

【讨论】:

以上是关于如何在 laravel 中以编程方式即时设置 .env 值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MAC 中以编程方式设置 *** 连接?

如何在 Objective C 中以编程方式设置集合视图?

如何在 Android 中以编程方式设置按钮的边距?

如何在 Swift 中以编程方式在水平线上设置约束? [关闭]

如何在android中以编程方式设置ImageView的高度宽度?

iOS 如何在 AppDelegate 中以编程方式设置侧边栏菜单?