成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Laravel核心解讀--ENV的加載和讀取

Anleb / 2356人閱讀

摘要:在啟動時會加載項目中的文件。我們來看一下的源碼來分析下是怎么加載中的配置的。我們來看看函數(shù)的源碼它直接通過內(nèi)建函數(shù)讀取環(huán)境變量。我們看到了在加載配置和讀取配置的時候,使用了和兩個函數(shù)。

Laravel在啟動時會加載項目中的.env文件。對于應(yīng)用程序運行的環(huán)境來說,不同的環(huán)境有不同的配置通常是很有用的。 例如,你可能希望在本地使用測試的Mysql數(shù)據(jù)庫而在上線后希望項目能夠自動切換到生產(chǎn)Mysql數(shù)據(jù)庫。本文將會詳細(xì)介紹 env 文件的使用與源碼的分析。

Env文件的使用 多環(huán)境env的設(shè)置

項目中env文件的數(shù)量往往是跟項目的環(huán)境數(shù)量相同,假如一個項目有開發(fā)、測試、生產(chǎn)三套環(huán)境那么在項目中應(yīng)該有三個.env.dev、.env.test.env.prod三個環(huán)境配置文件與環(huán)境相對應(yīng)。三個文件中的配置項應(yīng)該完全一樣,而具體配置的值應(yīng)該根據(jù)每個環(huán)境的需要來設(shè)置。

接下來就是讓項目能夠根據(jù)環(huán)境加載不同的env文件了。具體有三種方法,可以按照使用習(xí)慣來選擇使用:

在環(huán)境的nginx配置文件里設(shè)置APP_ENV環(huán)境變量fastcgi_param APP_ENV dev;

設(shè)置服務(wù)器上運行PHP的用戶的環(huán)境變量,比如在www用戶的/home/www/.bashrc中添加export APP_ENV dev

在部署項目的持續(xù)集成任務(wù)或者部署腳本里執(zhí)行cp .env.dev .env

針對前兩種方法,Laravel會根據(jù)env("APP_ENV")加載到的變量值去加載對應(yīng)的文件.env.dev、.env.test這些。 具體在后面源碼里會說,第三種比較好理解就是在部署項目時將環(huán)境的配置文件覆蓋到.env文件里這樣就不需要在環(huán)境的系統(tǒng)和nginx里做額外的設(shè)置了。

自定義env文件的路徑與文件名

env文件默認(rèn)放在項目的根目錄中,laravel 為用戶提供了自定義 ENV 文件路徑或文件名的函數(shù),

例如,若想要自定義 env 路徑,可以在 bootstrap 文件夾中 app.php 中使用Application實例的useEnvironmentPath方法:

$app = new IlluminateFoundationApplication(
    realpath(__DIR__."/../")
);

$app->useEnvironmentPath("/customer/path")

若想要自定義 env 文件名稱,就可以在 bootstrap 文件夾中 app.php 中使用Application實例的loadEnvironmentFrom方法:

$app = new IlluminateFoundationApplication(
    realpath(__DIR__."/../")
);

$app->loadEnvironmentFrom("customer.env")
Laravel 加載ENV配置

Laravel加載ENV的是在框架處理請求之前,bootstrap過程中的LoadEnvironmentVariables階段中完成的。

我們來看一下IlluminateFoundationBootstrapLoadEnvironmentVariables的源碼來分析下Laravel是怎么加載env中的配置的。

configurationIsCached()) {
            return;
        }

        $this->checkForSpecificEnvironmentFile($app);

        try {
            (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
        } catch (InvalidPathException $e) {
            //
        }
    }

    /**
     * Detect if a custom environment file matching the APP_ENV exists.
     *
     * @param  IlluminateContractsFoundationApplication  $app
     * @return void
     */
    protected function checkForSpecificEnvironmentFile($app)
    {
        if ($app->runningInConsole() && ($input = new ArgvInput)->hasParameterOption("--env")) {
            if ($this->setEnvironmentFilePath(
                $app, $app->environmentFile().".".$input->getParameterOption("--env")
            )) {
                return;
            }
        }

        if (! env("APP_ENV")) {
            return;
        }

        $this->setEnvironmentFilePath(
            $app, $app->environmentFile().".".env("APP_ENV")
        );
    }

    /**
     * Load a custom environment file.
     *
     * @param  IlluminateContractsFoundationApplication  $app
     * @param  string  $file
     * @return bool
     */
    protected function setEnvironmentFilePath($app, $file)
    {
        if (file_exists($app->environmentPath()."/".$file)) {
            $app->loadEnvironmentFrom($file);

            return true;
        }

        return false;
    }
}

在他的啟動方法bootstrap中,Laravel會檢查配置是否緩存過以及判斷應(yīng)該應(yīng)用那個env文件,針對上面說的根據(jù)環(huán)境加載配置文件的三種方法中的頭兩種,因為系統(tǒng)或者nginx環(huán)境變量中設(shè)置了APP_ENV,所以Laravel會在checkForSpecificEnvironmentFile方法里根據(jù) APP_ENV的值設(shè)置正確的配置文件的具體路徑, 比如.env.dev或者.env.test,而針對第三中情況則是默認(rèn)的.env, 具體可以參看下面的checkForSpecificEnvironmentFile還有相關(guān)的Application里的兩個方法的源碼:

protected function checkForSpecificEnvironmentFile($app)
{
    if ($app->runningInConsole() && ($input = new ArgvInput)->hasParameterOption("--env")) {
        if ($this->setEnvironmentFilePath(
            $app, $app->environmentFile().".".$input->getParameterOption("--env")
        )) {
            return;
        }
    }

    if (! env("APP_ENV")) {
        return;
    }

    $this->setEnvironmentFilePath(
        $app, $app->environmentFile().".".env("APP_ENV")
    );
}

namespace IlluminateFoundation;
class Application ....
{

    public function environmentPath()
    {
        return $this->environmentPath ?: $this->basePath;
    }
    
    public function environmentFile()
    {
        return $this->environmentFile ?: ".env";
    }
}

判斷好后要讀取的配置文件的路徑后,接下來就是加載env里的配置了。

(new Dotenv($app->environmentPath(), $app->environmentFile()))->load();

Laravel使用的是Dotenv的PHP版本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();
    }
}

它依賴/Dotenv/Loader來加載數(shù)據(jù):

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;
    }
}

Loader讀取配置時readLinesFromFile函數(shù)會用file函數(shù)將配置從文件中一行行地讀取到數(shù)組中去,然后排除以#開頭的注釋,針對內(nèi)容中包含=的行去調(diào)用setEnvironmentVariable方法去把文件行中的環(huán)境變量配置到項目中去:

namespace Dotenv;
class Loader
{
    public function setEnvironmentVariable($name, $value = null)
    {
        list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);

        $this->variableNames[] = $name;

        // 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;
    }
    
    public function getEnvironmentVariable($name)
    {
        switch (true) {
            case array_key_exists($name, $_ENV):
                return $_ENV[$name];
            case array_key_exists($name, $_SERVER):
                return $_SERVER[$name];
            default:
                $value = getenv($name);
                return $value === false ? null : $value; // switch getenv default to null
        }
    }
}

Dotenv實例化Loader的時候把Loader對象的$immutable屬性設(shè)置成了false,Loader設(shè)置變量的時候如果通過getEnvironmentVariable方法讀取到了變量值,那么就會跳過該環(huán)境變量的設(shè)置。所以Dotenv默認(rèn)情況下不會覆蓋已經(jīng)存在的環(huán)境變量,這個很關(guān)鍵,比如說在docker的容器編排文件里,我們會給PHP應(yīng)用容器設(shè)置關(guān)于Mysql容器的兩個環(huán)境變量

    environment:
      - "DB_PORT=3306"
      - "DB_HOST=database"

這樣在容器里設(shè)置好環(huán)境變量后,即使env文件里的DB_HOSThomesteadenv函數(shù)讀取出來的也還是容器里之前設(shè)置的DB_HOST環(huán)境變量的值database(docker中容器鏈接默認(rèn)使用服務(wù)名稱,在編排文件中我把mysql容器的服務(wù)名稱設(shè)置成了database, 所以php容器要通過database這個host來連接mysql容器)。因為用我們在持續(xù)集成中做自動化測試的時候通常都是在容器里進(jìn)行測試,所以Dotenv不會覆蓋已存在環(huán)境變量這個行為就相當(dāng)重要這樣我就可以只設(shè)置容器里環(huán)境變量的值完成測試而不用更改項目里的env文件,等到測試完成后直接去將項目部署到環(huán)境上就可以了。

如果檢查環(huán)境變量不存在那么接著Dotenv就會把環(huán)境變量通過PHP內(nèi)建函數(shù)putenv設(shè)置到環(huán)境中去,同時也會存儲到$_ENV$_SERVER這兩個全局變量中。

在項目中讀取env配置

在Laravel應(yīng)用程序中可以使用env()函數(shù)去讀取環(huán)境變量的值,比如獲取數(shù)據(jù)庫的HOST:

env("DB_HOST`, "localhost");

傳遞給 env 函數(shù)的第二個值是「默認(rèn)值」。如果給定的鍵不存在環(huán)境變量,則會使用該值。

我們來看看env函數(shù)的源碼:

function env($key, $default = null)
{
    $value = getenv($key);

    if ($value === false) {
        return value($default);
    }

    switch (strtolower($value)) {
        case "true":
        case "(true)":
            return true;
        case "false":
        case "(false)":
            return false;
        case "empty":
        case "(empty)":
            return "";
        case "null":
        case "(null)":
            return;
    }

    if (strlen($value) > 1 && Str::startsWith($value, """) && Str::endsWith($value, """)) {
        return substr($value, 1, -1);
    }

    return $value;
}

它直接通過PHP內(nèi)建函數(shù)getenv讀取環(huán)境變量。

我們看到了在加載配置和讀取配置的時候,使用了putenvgetenv兩個函數(shù)。putenv設(shè)置的環(huán)境變量只在請求期間存活,請求結(jié)束后會恢復(fù)環(huán)境之前的設(shè)置。因為如果php.ini中的variables_order配置項成了 GPCS不包含E的話,那么php程序中是無法通過$_ENV讀取環(huán)境變量的,所以使用putenv動態(tài)地設(shè)置環(huán)境變量讓開發(fā)人員不用去關(guān)注服務(wù)器上的配置。而且在服務(wù)器上給運行用戶配置的環(huán)境變量會共享給用戶啟動的所有進(jìn)程,這就不能很好的保護(hù)比如DB_PASSWORDAPI_KEY這種私密的環(huán)境變量,所以這種配置用putenv設(shè)置能更好的保護(hù)這些配置信息,getenv方法能獲取到系統(tǒng)的環(huán)境變量和putenv動態(tài)設(shè)置的環(huán)境變量。

本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里,歡迎訪問閱讀。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/29552.html

相關(guān)文章

  • Laravel核心解讀--完結(jié)篇

    摘要:過去一年時間寫了多篇文章來探討了我認(rèn)為的框架最核心部分的設(shè)計思路代碼實現(xiàn)。為了大家閱讀方便,我把這些源碼學(xué)習(xí)的文章匯總到這里。數(shù)據(jù)庫算法和數(shù)據(jù)結(jié)構(gòu)這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復(fù)雜問題。 過去一年時間寫了20多篇文章來探討了我認(rèn)為的Larave框架最核心部分的設(shè)計思路、代碼實現(xiàn)。通過更新文章自己在軟件設(shè)計、文字表達(dá)方面都有所提高,在剛開始決定寫Laravel源碼分析地...

    laoLiueizo 評論0 收藏0
  • Laravel學(xué)習(xí)筆記之bootstrap源碼解析

    摘要:總結(jié)本文主要學(xué)習(xí)了啟動時做的七步準(zhǔn)備工作環(huán)境檢測配置加載日志配置異常處理注冊注冊啟動。 說明:Laravel在把Request通過管道Pipeline送入中間件Middleware和路由Router之前,還做了程序的啟動Bootstrap工作,本文主要學(xué)習(xí)相關(guān)源碼,看看Laravel啟動程序做了哪些具體工作,并將個人的研究心得分享出來,希望對別人有所幫助。Laravel在入口index...

    xiaoxiaozi 評論0 收藏0
  • Laravel核心解讀--HTTP內(nèi)核

    摘要:終止程序終止中間件內(nèi)核的方法會調(diào)用中間件的方法,調(diào)用完成后從請求進(jìn)來到返回響應(yīng)整個應(yīng)用程序的生命周期就結(jié)束了。 Http Kernel Http Kernel是Laravel中用來串聯(lián)框架的各個核心組件來網(wǎng)絡(luò)請求的,簡單的說只要是通過public/index.php來啟動框架的都會用到Http Kernel,而另外的類似通過artisan命令、計劃任務(wù)、隊列啟動框架進(jìn)行處理的都會用到C...

    chenjiang3 評論0 收藏0
  • Laravel核心解讀--服務(wù)提供器(ServiceProvider)

    摘要:調(diào)用了的可以看出,所有服務(wù)提供器都在配置文件文件的數(shù)組中。啟動的啟動由類負(fù)責(zé)引導(dǎo)應(yīng)用的屬性中記錄的所有服務(wù)提供器,就是依次調(diào)用這些服務(wù)提供器的方法,引導(dǎo)完成后就代表應(yīng)用正式啟動了,可以開始處理請求了。 服務(wù)提供器是所有 Laravel 應(yīng)用程序引導(dǎo)中心。你的應(yīng)用程序自定義的服務(wù)、第三方資源包提供的服務(wù)以及 Laravel 的所有核心服務(wù)都是通過服務(wù)提供器進(jìn)行注冊(register)和引...

    Richard_Gao 評論0 收藏0
  • 源碼解讀Laravel php artisan route:cache

    摘要:然而,本文的討論重點,還是背后的源碼,是怎么做到這一步的。從哪開始看源碼位于你還是可以使用編輯器搜,就可以看到源碼了。第三步序列化所有路由注冊映射關(guān)系,還是在的方法中上面的方法位于中的中。所以到這里,的源碼解讀就完成了。 學(xué) Laravel 和 Vuejs 你真應(yīng)該來 codecasts.com ! Laravel ?route:cache 可以直接緩存路由文件,這樣其實可以在一定程度...

    wangzy2019 評論0 收藏0

發(fā)表評論

0條評論

Anleb

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<