laravel .env环境变量原理

介绍

对于应用程序运行的环境来说,不同的环境有不同的配置通常是很有用的。Laravel 利用 Vance Lucas 的 PHP 库 DotEnv 使得此项功能的实现变得非常简单。当应用程序收到请求时,.env 文件中列出的所有变量将被加载到 PHP 的超级全局变量 $_ENV 中。

使用

你可以使用 env 函数检索这些变量的值。事实上,如果你查看 Laravel 的配置文件,你就能注意到有数个选项已经使用了这个函数:

php 复制代码
'debug' => env('APP_DEBUG', false),

传递给 env 函数的第二个值是「默认值」。如果给定的键不存在环境变量,则会使用该值。

使用分析

我们可以先看一下助手函数 env

php 复制代码
if (! function_exists('env')) {
    /**
     * Gets the value of an environment variable.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    function env($key, $default = null)
    {
        return Env::get($key, $default);
    }
}

ENV::get 最终追踪到 EnvConstAdapter get

php 复制代码
    /**
     * Get an environment variable, if it exists.
     *
     * @param string $name
     *
     * @return \PhpOption\Option
     */
    public function get($name)
    {
        if (array_key_exists($name, $_ENV)) {
            return Some::create($_ENV[$name]);
        }

        return None::create();
    }

读取_ENV内容,_ENV是 通过环境提供给脚本的变量

php 复制代码
/**
 * @xglobal $_ENV array
 *
 * Variables provided to the script via the environment.
 * Analogous to the old $HTTP_ENV_VARS array (which is still available, but deprecated).
 *
 * <p><a href="https://secure.php.net/manual/en/reserved.variables.php">
 * https://secure.php.net/manual/en/reserved.variables.php</a>
 */
$_ENV = array();

写入$_ENV

bootstrap/app.php中

php 复制代码
    try {
        (new Laravel\Lumen\Bootstrap\LoadEnvironmentVariables(dirname(__DIR__)))->bootstrap();
    } catch (Dotenv\Exception\InvalidPathException $e) {
        //
    }

Laravel\Lumen\Bootstrap\LoadEnvironmentVariables 中

php 复制代码
<?php

namespace Laravel\Lumen\Bootstrap;

use Dotenv\Dotenv;
use Dotenv\Exception\InvalidFileException;
use Illuminate\Support\Env;
use Symfony\Component\Console\Output\ConsoleOutput;

class LoadEnvironmentVariables
{
    /**
     * The directory containing the environment file.
     *
     * @var string
     */
    protected $filePath;

    /**
     * The name of the environment file.
     *
     * @var string|null
     */
    protected $fileName;

    /**
     * Create a new loads environment variables instance.
     *
     * @param  string  $path
     * @param  string|null  $name
     * @return void
     */
    public function __construct($path, $name = null)
    {
        $this->filePath = $path;
        $this->fileName = $name;
    }

    /**
     * Setup the environment variables.
     *
     * If no environment file exists, we continue silently.
     *
     * @return void
     */
    public function bootstrap()
    {
        try {
            $this->createDotenv()->safeLoad();
        } catch (InvalidFileException $e) {
            $this->writeErrorAndDie([
                'The environment file is invalid!',
                $e->getMessage(),
            ]);
        }
    }

    /**
     * Create a Dotenv instance.
     *
     * @return \Dotenv\Dotenv
     */
    protected function createDotenv()
    {
        return Dotenv::create(
            $this->filePath,
            $this->fileName,
            Env::getFactory()
        );
    }

    /**
     * Write the error information to the screen and exit.
     *
     * @param  string[]  $errors
     * @return void
     */
    protected function writeErrorAndDie(array $errors)
    {
        $output = (new ConsoleOutput)->getErrorOutput();

        foreach ($errors as $error) {
            $output->writeln($error);
        }

        die(1);
    }
}

我们可以看到 bootstrap方法是创建Dotenv实例,并且调用Dotenv实例的safeload方法,Dotenv::create参数的含义

复制代码
   $this->filePath, #env所在目录
   $this->fileName, #env名称,默认 .env
   Env::getFactory() #laravel env相关的适配器工厂

由此我们可以自定义.env的位置和名称

safeload方法

php 复制代码
    /**
     * Load environment file in given directory.
     *
     * @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
     *
     * @return array<string|null>
     */
    public function load()
    {
        return $this->loadData();
    }

    /**
     * Load environment file in given directory, silently failing if it doesn't exist.
     *
     * @throws \Dotenv\Exception\InvalidFileException
     *
     * @return array<string|null>
     */
    public function safeLoad()
    {
        try {
            return $this->loadData();
        } catch (InvalidPathException $e) {
            // suppressing exception
            return [];
        }
    }

    /**
     * Load environment file in given directory.
     *
     * @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
     *
     * @return array<string|null>
     */
    public function overload()
    {
        return $this->loadData(true);
    }

    /**
     * Actually load the data.
     *
     * @param bool $overload
     *
     * @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
     *
     * @return array<string|null>
     */
    protected function loadData($overload = false)
    {
        return $this->loader->setImmutable(!$overload)->load();
    }

根据代码分析,safeLoad 中使用了 try {} catuch {} 捕获了异常,不会因为地址错误而报错,同时加载loadData,这里默认使用了loadData = true, 影响的Dotenv 创建的 DotenvVariables(array adapters, immutable)实例属性 $immutable值。我们通过追踪load方法,最终可以找到该方法:

php 复制代码
    /**
     * Set an environment variable.
     *
     * @param string      $name
     * @param string|null $value
     *
     * @throws \InvalidArgumentException
     *
     * @return void
     */
    public function set($name, $value = null)
    {
        if (!is_string($name)) {
            throw new InvalidArgumentException('Expected name to be a string.');
        }

        // Don't overwrite existing environment variables if we're immutable
        // Ruby's dotenv does this with `ENV[key] ||= value`.
        if ($this->isImmutable() && $this->get($name) !== null && $this->loaded->get($name)->isEmpty()) {
            return;
        }

        $this->setInternal($name, $value);
        $this->loaded->set($name, '');
    }

此时我们明白,如果$immutable = true的话,如果之前环境变量有了该值,后面的配置文件无法进行更改环境变量的值,如果没有该值进行添加

相关推荐
用户2986985301420 分钟前
.NET 文档自动化:Spire.Doc 设置奇偶页页眉/页脚的最佳实践
后端·c#·.net
序安InToo1 小时前
第6课|注释与代码风格
后端·操作系统·嵌入式
xyy1231 小时前
C#: Newtonsoft.Json 到 System.Text.Json 迁移避坑指南
后端
洋洋技术笔记1 小时前
Spring Boot Web MVC配置详解
spring boot·后端
JxWang051 小时前
VS Code 配置 Markdown 环境
后端
navms1 小时前
搞懂线程池,先把 Worker 机制啃明白
后端
JxWang051 小时前
离线数仓的优化及重构
后端
Nyarlathotep01131 小时前
gin01:初探gin的启动
后端·go
JxWang051 小时前
安卓手机配置通用多屏协同及自动化脚本
后端
JxWang051 小时前
Windows Terminal 配置 oh-my-posh
后端