如何讀取?如何儲存?這是重點
如何讀取
逐行正則匹配,一次不能多行。且只能定義
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-setenv.php
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目錄的載入。
我的個人部落格:http://worthly.cn/