内容简介:Laravel ENV—— 环境变量的加载与源码解析
前言
本文 GitBook 地址: https://www.gitbook.com/book/leoyang90/laravel-source-analysis
laravel
在启动时,会加载项目的 env
文件,本文将会详细介绍 env
文件的使用与源码的分析。
ENV 文件的使用
多环境 ENV 文件的设置
laravel
支持在不同的环境下加载不同的 env
文件,若想要实现多环境 env
文件,需要做两件事:
一、在项目写多个 ENV
文件,例如三个 env
文件:
.env.development
、.env.staging
、.env.production
,
这三个文件中分别针对不同环境为某些变量配置了不同的值,
二、配置 APP_ENV
环境变量值
配置环境变量的方法有很多,其中一个方法是在 nginx
的配置文件中写下这句代码:
fastcgi_param APP_ENV production;
那么 laravel
会通过 env('APP_ENV')
根据环境变量 APP_ENV
来判断当前具体的环境,假如环境变量 APP_ENV
为 production
,那么 laravel
将会自动加载 .env.production
文件。
自定义 ENV 文件的路径与文件名
laravel
为用户提供了自定义 ENV
文件路径或文件名的函数,
例如,若想要自定义 env
路径,就可以在 bootstrap
文件夹中 app.php
文件:
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
$app->useEnvironmentPath('/customer/path')
若想要自定义 env
文件名称,就可以在 bootstrap
文件夹中 app.php
文件:
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
$app->loadEnvironmentFrom('customer.env')
ENV 文件变量设置
- 在
env
文件中,我们可以为变量赋予具体值:
CFOO=bar
值得注意的是,这种具体值不允许赋予多个,例如:
CFOO=bar baz
- 可以为变量赋予字符串引用
CQUOTES="a value with a # character"
值得注意的是,这种引用不允许字符串中存在符号 \
,只能使用转义字符 \\
而且也不允许内嵌符号 ""
,只能使用转移字符 \"
,否则取值会意外结束:
CQUOTESWITHQUOTE="a value with a # character & a quote \" character inside quotes" # " this is a comment
$this->assertEquals('a value with a # character & a quote " character inside quotes', getenv('CQUOTESWITHQUOTE'));
- 可以在
env
文件中添加注释,方法是以#
开始:
CQUOTES="a value with a # character" # this is a comment
- 可以使用
export
来为变量赋值:
export EFOO="bar"
- 可以在
env
文件中使用变量为变量赋值:
NVAR1="Hello"
NVAR2="World!"
NVAR3="{$NVAR1} {$NVAR2}"
NVAR4="${NVAR1} ${NVAR2}"
NVAR5="$NVAR1 {NVAR2}"
$this->assertEquals('{$NVAR1} {$NVAR2}', $_ENV['NVAR3']); // not resolved
$this->assertEquals('Hello World!', $_ENV['NVAR4']);
$this->assertEquals('$NVAR1 {NVAR2}', $_ENV['NVAR5']); // not resolved
ENV 加载源码分析
laravel 加载 ENV
ENV
的加载功能由类 \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class
完成,它的启动函数为:
public function bootstrap(Application $app)
{
if ($app->configurationIsCached()) {
return;
}
$this->checkForSpecificEnvironmentFile($app);
try {
(new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
} catch (InvalidPathException $e) {
//
}
}
如果我们在环境变量中设置了 APP_ENV
变量,那么就会调用函数 checkForSpecificEnvironmentFile
来根据环境加载不同的 env
文件:
protected function checkForSpecificEnvironmentFile($app)
{
if (php_sapi_name() == 'cli' && with($input = new ArgvInput)->hasParameterOption('--env')) {
$this->setEnvironmentFilePath(
$app, $app->environmentFile().'.'.$input->getParameterOption('--env')
);
}
if (! env('APP_ENV')) {
return;
}
$this->setEnvironmentFilePath(
$app, $app->environmentFile().'.'.env('APP_ENV')
);
}
protected function setEnvironmentFilePath($app, $file)
{
if (file_exists($app->environmentPath().'/'.$file)) {
$app->loadEnvironmentFrom($file);
}
}
vlucas/phpdotenv 源码解读
laravel
中对 env
文件的读取是采用 vlucas/phpdotenv
的开源项目:
class Dotenv
{
public function __construct($path, $file = '.env')
{
$this->filePath = $this->getFilePath($path, $file);
$this->loader = new Loader($this->filePath, true);
}
public function load()
{
return $this->loadData();
}
protected function loadData($overload = false)
{
$this->loader = new Loader($this->filePath, !$overload);
return $this->loader->load();
}
}
env
文件变量的读取依赖类 /Dotenv/Loader
:
class Loader
{
public function load()
{
$this->ensureFileIsReadable();
$filePath = $this->filePath;
$lines = $this->readLinesFromFile($filePath);
foreach ($lines as $line) {
if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
$this->setEnvironmentVariable($line);
}
}
return $lines;
}
}
我们可以看到,env
文件的读取的流程:
- 判断
env
文件是否可读 - 读取整个
env
文件,并将文件按行存储 - 循环读取每一行,略过注释
- 进行环境变量赋值
protected function ensureFileIsReadable()
{
if (!is_readable($this->filePath) || !is_file($this->filePath)) {
throw new InvalidPathException(sprintf('Unable to read the environment file at %s.', $this->filePath));
}
}
protected function readLinesFromFile($filePath)
{
// Read file into an array of lines with auto-detected line endings
$autodetect = ini_get('auto_detect_line_endings');
ini_set('auto_detect_line_endings', '1');
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
ini_set('auto_detect_line_endings', $autodetect);
return $lines;
}
protected function isComment($line)
{
return strpos(ltrim($line), '#') === 0;
}
protected function looksLikeSetter($line)
{
return strpos($line, '=') !== false;
}
环境变量赋值是 env
文件加载的核心,主要由 setEnvironmentVariable
函数:
public function setEnvironmentVariable($name, $value = null)
{
list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
return;
}
if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {
apache_setenv($name, $value);
}
if (function_exists('putenv')) {
putenv("$name=$value");
}
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
}
normaliseEnvironmentVariable
函数用来加载各种类型的环境变量:
protected function normaliseEnvironmentVariable($name, $value)
{
list($name, $value) = $this->splitCompoundStringIntoParts($name, $value);
list($name, $value) = $this->sanitiseVariableName($name, $value);
list($name, $value) = $this->sanitiseVariableValue($name, $value);
$value = $this->resolveNestedVariables($value);
return array($name, $value);
}
splitCompoundStringIntoParts
用于将赋值语句转化为环境变量名 name
和环境变量值 value
。
protected function splitCompoundStringIntoParts($name, $value)
{
if (strpos($name, '=') !== false) {
list($name, $value) = array_map('trim', explode('=', $name, 2));
}
return array($name, $value);
}
sanitiseVariableName
用于格式化环境变量名:
protected function sanitiseVariableName($name, $value)
{
$name = trim(str_replace(array('export ', '\'', '"'), '', $name));
return array($name, $value);
}
sanitiseVariableValue
用于格式化环境变量值:
protected function sanitiseVariableValue($name, $value)
{
$value = trim($value);
if (!$value) {
return array($name, $value);
}
if ($this->beginsWithAQuote($value)) { // value starts with a quote
$quote = $value[0];
$regexPattern = sprintf(
'/^
%1$s # match a quote at the start of the value
( # capturing sub-pattern used
(?: # we do not need to capture this
[^%1$s\\\\] # any character other than a quote or backslash
|\\\\\\\\ # or two backslashes together
|\\\\%1$s # or an escaped quote e.g \"
)* # as many characters that match the previous rules
) # end of the capturing sub-pattern
%1$s # and the closing quote
.*$ # and discard any string after the closing quote
/mx',
$quote
);
$value = preg_replace($regexPattern, '$1', $value);
$value = str_replace("\\$quote", $quote, $value);
$value = str_replace('\\\\', '\\', $value);
} else {
$parts = explode(' #', $value, 2);
$value = trim($parts[0]);
// Unquoted values cannot contain whitespace
if (preg_match('/\s+/', $value) > 0) {
throw new InvalidFileException('Dotenv values containing spaces must be surrounded by quotes.');
}
}
return array($name, trim($value));
}
这段代码是加载 env
文件最复杂的部分,我们详细来说:
-
若环境变量值是具体值,那么仅仅需要分割注释
#
部分,并判断是否存在空格符即可。 - 若环境变量值由引用构成,那么就需要进行正则匹配,具体的正则表达式为:
/^"((?:[^"\\]|\\\\|\\"))".*$/mx
这个正则表达式的意思是:
- 提取
“”
双引号内部的字符串,抛弃双引号之后的字符串 - 若双引号内部还有双引号,那么以最前面的双引号为提取内容,例如 "dfd("dfd")fdf",我们只能提取出来最前面的部分 "dfd("
- 对于内嵌的引用可以使用
\"
,例如 "dfd\"dfd\"fdf",我们就可以提取出来 "dfd\"dfd\"fdf"。 - 不允许引用中含有
\
,但可以使用转义字符\\
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 从源码分析vue-cli@3.0环境变量配置
- PHP 对输入变量名的自动转换的问题与源码分析
- PHP 源码探秘 - 在解析外部变量时的一个不想修复的 bug ?
- [数据可视化]d3js源码,selection(四)添加事件,控制流,局部变量
- 全局变量,静态全局变量,局部变量,静态局部变量
- python变量与变量作用域
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Game Programming Patterns
Robert Nystrom / Genever Benning / 2014-11-2 / USD 39.95
The biggest challenge facing many game programmers is completing their game. Most game projects fizzle out, overwhelmed by the complexity of their own code. Game Programming Patterns tackles that exac......一起来看看 《Game Programming Patterns》 这本书的介绍吧!
图片转BASE64编码
在线图片转Base64编码工具
SHA 加密
SHA 加密工具