深入理解 Laravel 中.env 文件读取
如何读取?如何保存?这是重点
如何读取
逐行正则匹配,一次不能多行。且只能定义
bool
/string
类型。
getenv
:
laravel的env是通过该函数获取env环境变量
那么存储呢?使用了Dotenv包
加载如下:Dotenv\Loader类下:
/**
* Load `.env` file in given directory.
*
* @return array
*/
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文件效率比较低。
顺着getEnvironmentVariable
往下:
public function setEnvironmentVariable($name, $value = null)
{
// 这里就是解析了
list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
// Don't overwrite existing environment variables if we're immutable
// Ruby's dotenv does this with `ENV[key] ||= value`.
if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
return;
}
// If PHP is running as an Apache module and an existing
// Apache environment variable exists, overwrite it
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;
}
上面的代码会注意到:apache_getenv
和apache_setenv
和putenv
对于apche_getenv
: http://php.net/manual/zh/function.apache-s...
putenv
:
添加 setting 到服务器环境变量。 环境变量仅存活于当前请求期间。 在请求结束时环境会恢复到初始状态。
优点:1、优雅 2、稳定性
先看他的优雅写法:
list($name, $value) = array_map('trim', explode('=', $name, 2));
对于稳定性:
上面设置了$_ENV
、$_SERVER
、putenv
这些都是为了跨平台的稳定性,如果其中一个变量不可用,就从另一个中获取。
解析过程
/**
*规范化给定的环境变量。
*
*取得开发人员传入的值和:
* - 确保我们处理单独的名称和值,如果需要,将名称字符串分开,引号
* - 解析变量值,
* - 解析带有双引号的变量名,
* - 解析嵌套变量。
*
* @param string $ name
* @param string $ value
*
* @return array
*/
protected function normaliseEnvironmentVariable($name, $value)
{
// 就是简单的 array_map('trim', explode('=', $name, 2)); 根据等号分开并且去除两边空格
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);
}
protected function sanitiseVariableName($name, $value)
{
// 支持 export方式定义变量,把单引号和双引号去掉
$name = trim(str_replace(array('export ', '\'', '"'), '', $name));
return array($name, $value);
}
//
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));
}
// 是否包含单引号或者双引号
protected function beginsWithAQuote($value)
{
return strpbrk($value[0], '"\'') !== false;
}
问题1:
使用php artisan config:cache
之后env
函数不再有用,就是从getenv
无法获取数据?原因?
因为cache
了之后,laravel
就会将所有config
生成缓存文件,并且判断如果缓存文件生成,就不会加载.env
文件,因此env函数就不会获取导数据啦,那么是哪里判断了?\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables
类在作怪!!
public function bootstrap(Application $app)
{
// 是否缓存config目录的配置了,缓存了就不会去加载.env
if ($app->configurationIsCached()) {
return;
}
// 检查env应该加载哪一个文件
$this->checkForSpecificEnvironmentFile($app);
try {
// 这个时候才加载 .env
(new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
} catch (InvalidPathException $e) {
//
}
}
问题2:
这个加载环境变量是什么时候加载的?
看Kernel
内核代码,laravel
的内核分为console
和http
两种。我们当然看http
的。\Illuminate\Foundation\Http\Kernel
类
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
可以看到,加载env变量是启动的第一步,然后就是加载config目录的文件。这些类都有一个叫bootstrap的方法。
总结:
1、.env
文件的变量有注释功能
2、保存在请求期间的环境变量中
3、重点:在config:cache
之后不会加载env
,因此不能再config
目录以外的文件使用env
函数,否则将会导致程序以超乎想象的方式运行。我相信你的程序多少会出现这个问题。
4、非引号包含的值,不能包含空格
5、配置值会去除井号#
后面部分,保留前面部分,这就可能有问题了。比如:
# 数据库密码包含井号,那咋办?
# 错误,因为会认为井号#后面的是注释
db_password=12345#%snq
#相当于:
db_password=12345
# 正确 使用引号
db_password="12345#%snq"
下一节就是阅读LoadConfiguration
源码了-config目录的加载。
本作品采用《CC 协议》,转载必须注明作者和本文链接
很有用,我自己也看了。看的有点懵
昨天我也遇到
php artisan config:cache
后.env
无法读取的问题,查询资料后我是直接用config里面的文件读取.env
的数据,然后再读取config里面的文件来间接读取.env
的值。不过很感谢楼主的文章,阐述了所以然,棒棒哒。@susucool 还有个config函数
@lovecn 是的,这是下一节的内容,请见下回分解
感谢楼主,刚刚就遇到了参数值里面包含#号,读取的时候#号后面直接没了的问题。看到你这儿才发现原来是可以加双引号的
正解,问题以解决
感谢,在弄docker镜像的时候,看到
Dockerfile
中的注释,说不能再config
之外使用env
,于是搜到这篇内容,发现自己在config
之外写了很多。。。。还好看到了