深刻理解 Laravel 中.env 文件讀取

如何讀取?如何保存?這是重點php

如何讀取

逐行正則匹配,一次不能多行。且只能定義bool/string類型。laravel

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文件效率比較低。apache

順着getEnvironmentVariable往下:bootstrap

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_getenvapache_setenvputenv 對於apche_getenv : http://php.net/manual/zh/function.apache-setenv.php緩存

putenv: 添加 setting 到服務器環境變量。 環境變量僅存活於當前請求期間。 在請求結束時環境會恢復到初始狀態。服務器

優勢:一、優雅 二、穩定性 先看他的優雅寫法:app

list($name, $value) = array_map('trim', explode('=', $name, 2));

對於穩定性: 上面設置了$_ENV$_SERVERputenv這些都是爲了跨平臺的穩定性,若是其中一個變量不可用,就從另外一箇中獲取。ide

解析過程函數

/**
      *規範化給定的環境變量。
     *
      *取得開發人員傳入的值和:
      * - 確保咱們處理單獨的名稱和值,若是須要,將名稱字符串分開,引號
      * - 解析變量值,
      * - 解析帶有雙引號的變量名,
      * - 解析嵌套變量。
     *
      * @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的內核分爲consolehttp兩種。咱們固然看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的方法。

總結:

一、.env文件的變量有註釋功能 二、保存在請求期間的環境變量中 三、重點:在config:cache以後不會加載env,所以不能再config目錄之外的文件使用env函數,不然將會致使程序以超乎想象的方式運行。我相信你的程序多少會出現這個問題。 四、非引號包含的值,不能包含空格 五、值會去除井號#後面部分保留前面部分,這就可能有問題了。好比:

# 數據庫密碼包含井號,那咋辦?
# 錯誤
db_password=12345#%snq
# 正確 使用引號
db_password="12345#%snq"

下一節就是閱讀LoadConfiguration源碼了-config目錄的加載。

原文:https://laravel-china.org/articles/18022/edit

相關文章
相關標籤/搜索