Php 如何以编程方式动态设置laravel中的.env值

Php 如何以编程方式动态设置laravel中的.env值,php,laravel,environment-variables,Php,Laravel,Environment Variables,我有一个自定义CMS,我正在用Laravel从头开始编写,并希望在用户设置后从controller设置env值,即数据库详细信息、邮件详细信息、常规配置等,并希望给用户灵活性,以便使用我正在制作的GUI在移动中更改它们 所以我的问题是,当我需要控制器时,如何将从用户接收到的值写入.env文件 在运行中构建.env文件是一个好主意,还是有其他方法 提前感谢。由于Laravel使用配置文件访问和存储.env数据,您可以使用config()方法动态设置此数据: config(['database.co

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

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

在运行中构建
.env
文件是一个好主意,还是有其他方法


提前感谢。

由于Laravel使用配置文件访问和存储
.env
数据,您可以使用
config()
方法动态设置此数据:

config(['database.connections.mysql.host' => '127.0.0.1']);
要获取此数据,请使用
config()

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


如果希望将这些设置持久化到环境文件中,以便以后再次加载(即使配置已缓存),则可以使用如下函数。我将把安全警告放在那里,对这样一个方法的调用应该严格控制,用户输入应该被适当地消毒

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
是环境文件中的键(例如..应用程序日志级别)

$configKey
是用于在运行时访问配置的密钥(例如..app.log\u级别(tinker
config('app.log\u级别')


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

基于josh的回答。我需要一种方法来替换
.env
文件中键的值

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

因为我的目标是替换Laravel特使使用的值,它根本不使用配置文件,而是直接使用
.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');

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

putenv(“自定义变量=英雄”);

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

注意:根据应用程序的哪个部分使用env设置,您可能需要将变量提前设置到index.php或bootstrap.php文件中。在应用程序服务提供商中设置该变量对于某些包/使用env设置来说可能太晚了。

tl;dr 基于此,我创建了一个不使用或的解决方案

解释 这不使用可能对某些人不起作用的
strtok
,或不使用双引号
.env
变量的
env()
,这些变量被计算并插入嵌入变量

KEY="Something with spaces or variables ${KEY2}"
更简化:

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

替换.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\u env('APP\u VERSION',1.8)


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

基于totymedli的答案

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

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;

}
你可以使用这个软件包

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

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

此解决方案建立在Elias Tutungi提供的解决方案的基础上,它接受多个值更改并使用Laravel集合,因为foreach的值太大

函数集\u环境\u值($values=[])
{
$path=app()->environmentFilePath();
收集($values)->map(函数($value,$key)使用($path){
$escape=preg_quote('='.env($key),'/');
文件内容($path,preg\u replace)(
“/^{$key}{$escaped}/m”,
“{$key}={$value}”,
文件获取内容($path)
));
});
返回true;
}

基于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;
}
因此,它使用Function 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;
    }

您可以使用我在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;
        }
    }
#覆盖.env的更简单方法可以这样做

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

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

您可以在密钥生成类中找到代码:
illumb\Foundation\Console\KeyGenerateCommand
PHP8解决方案

此解决方案使用的是
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文件,只需使用以下代码:

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

config(['something'=>'data']);return env('something');
这是我在controller中设置它的方式,但它不返回任何内容。您不需要使用
env()
。只需使用
config()
设置数据,然后使用
config('something')
获取数据。我设置了数据库凭据
$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())
));
/**
 * @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);
}
\Illuminate\Support\Env::getRepository()->set('APP_NAME','New app name');