繁体   English   中英

如何以编程方式动态地在 laravel 中设置 env 值

[英]How to set .env values in laravel programmatically on the fly

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

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

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

由于 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

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

putenv ("CUSTOM_VARIABLE=hero");

像往常一样阅读 env('CUSTOM_VARIABLE') 或 env('CUSTOM_VARIABLE', 'devault')

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

基于乔希的回答。 我需要一种方法来替换.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');

基于 totymedli 的回答。

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

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;

}

更简化:

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

tl;博士

根据vesperknight 的回答,我创建了一个不使用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);
}

解释

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

KEY="Something with spaces or variables ${KEY2}"

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

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

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

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是环境文件中的关键(例如.. APP_LOG_LEVEL)

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

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

基于 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;
    }

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

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 文件,您可以简单地使用以下代码:

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

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

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

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

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);
  }
}
$envFile = app()->environmentFilePath();
$_ENV['APP_NAME'] = "New Project Name";
$txt= "";
foreach ($_ENV as $key => $value) {
    $txt .= $key."=".$value."\n"."\n";
}
file_put_contents($envFile,$txt);

该解决方案可以创建、替换和保存 .env 文件中给定的值。

我创建它是为了在命令package:install中使用,以自动将 .env 键值对添加到 .env 文件。

/**
 * Set .env key-value pair
 *
 * @param array $keyPairs Desired key name
 * @return void
 */
public function saveArrayToEnv(array $keyPairs)
{
    $envFile = app()->environmentFilePath();
    $newEnv = file_get_contents($envFile);

    $newlyInserted = false;

    foreach ($keyPairs as $key => $value) {
        // Make sure key is uppercase (can be left out)
        $key = Str::upper($key);

        if (str_contains($newEnv, "$key=")) {
            // If key exists, replace value
            $newEnv = preg_replace("/$key=(.*)\n/", "$key=$value\n", $newEnv);
        } else {
            // Check if spacing is correct
            if (!str_ends_with($newEnv, "\n\n") && !$newlyInserted) {
                $newEnv .= str_ends_with($newEnv, "\n") ? "\n" : "\n\n";
                $newlyInserted = true;
            }
            // Append new
            $newEnv .= "$key=$value\n";
        }
    }

    $fp = fopen($envFile, 'w');
    fwrite($fp, $newEnv);
    fclose($fp);
}

你传递给它一个关联数组:

$keyPairs = [
    'KEY' => 'VALUE',
    'API_KEY' => 'XXXXXX-XXXXXX-XXXXX'
]

它将添加它们或用给定值替换给定键的值。 当然还有改进的余地,比如检查数组是否实际上是一个关联数组。 但这适用于 Laravel 命令类中的硬编码内容。

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

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

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

此解决方案建立在 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;
    }

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

  /**
 * 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 文件函数中的单个值

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM