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的话,如果之前环境变量有了该值,后面的配置文件无法进行更改环境变量的值,如果没有该值进行添加

相关推荐
除了菜一无所有!3 分钟前
基于SpringBoot技术的教务管理
java·spring boot·后端
DEARM LINER2 小时前
mysql 巧妙的索引
数据库·spring boot·后端·mysql
开心工作室_kaic5 小时前
ssm010基于ssm的新能源汽车在线租赁管理系统(论文+源码)_kaic
java·前端·spring boot·后端·汽车
代码吐槽菌5 小时前
基于SSM的汽车客运站管理系统【附源码】
java·开发语言·数据库·spring boot·后端·汽车
Ellie陈7 小时前
Java已死,大模型才是未来?
java·开发语言·前端·后端·python
wclass-zhengge8 小时前
SpringBoot篇(运维实用篇 - 临时属性)
运维·spring boot·后端
2401_857600958 小时前
商场应急管理:SpringBoot技术解决方案
java·spring boot·后端
半夏之沫8 小时前
✨最新金九银十✨大厂后端面经✨
java·后端·面试