摘要:在啟動時會加載項目中的文件。我們來看一下的源碼來分析下是怎么加載中的配置的。我們來看看函數的源碼它直接通過內建函數讀取環境變量。我們看到了在加載配置和讀取配置的時候,使用了和兩個函數。
Laravel在啟動時會加載項目中的.env文件。對于應用程序運行的環境來說,不同的環境有不同的配置通常是很有用的。 例如,你可能希望在本地使用測試的Mysql數據庫而在上線后希望項目能夠自動切換到生產Mysql數據庫。本文將會詳細介紹 env 文件的使用與源碼的分析。
Env文件的使用 多環境env的設置項目中env文件的數量往往是跟項目的環境數量相同,假如一個項目有開發、測試、生產三套環境那么在項目中應該有三個.env.dev、.env.test、.env.prod三個環境配置文件與環境相對應。三個文件中的配置項應該完全一樣,而具體配置的值應該根據每個環境的需要來設置。
接下來就是讓項目能夠根據環境加載不同的env文件了。具體有三種方法,可以按照使用習慣來選擇使用:
在環境的nginx配置文件里設置APP_ENV環境變量fastcgi_param APP_ENV dev;
設置服務器上運行PHP的用戶的環境變量,比如在www用戶的/home/www/.bashrc中添加export APP_ENV dev
在部署項目的持續集成任務或者部署腳本里執行cp .env.dev .env
針對前兩種方法,Laravel會根據env("APP_ENV")加載到的變量值去加載對應的文件.env.dev、.env.test這些。 具體在后面源碼里會說,第三種比較好理解就是在部署項目時將環境的配置文件覆蓋到.env文件里這樣就不需要在環境的系統和nginx里做額外的設置了。
自定義env文件的路徑與文件名env文件默認放在項目的根目錄中,laravel 為用戶提供了自定義 ENV 文件路徑或文件名的函數,
例如,若想要自定義 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會檢查配置是否緩存過以及判斷應該應用那個env文件,針對上面說的根據環境加載配置文件的三種方法中的頭兩種,因為系統或者nginx環境變量中設置了APP_ENV,所以Laravel會在checkForSpecificEnvironmentFile方法里根據 APP_ENV的值設置正確的配置文件的具體路徑, 比如.env.dev或者.env.test,而針對第三中情況則是默認的.env, 具體可以參看下面的checkForSpecificEnvironmentFile還有相關的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來加載數據:
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函數會用file函數將配置從文件中一行行地讀取到數組中去,然后排除以#開頭的注釋,針對內容中包含=的行去調用setEnvironmentVariable方法去把文件行中的環境變量配置到項目中去:
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屬性設置成了false,Loader設置變量的時候如果通過getEnvironmentVariable方法讀取到了變量值,那么就會跳過該環境變量的設置。所以Dotenv默認情況下不會覆蓋已經存在的環境變量,這個很關鍵,比如說在docker的容器編排文件里,我們會給PHP應用容器設置關于Mysql容器的兩個環境變量
environment: - "DB_PORT=3306" - "DB_HOST=database"
這樣在容器里設置好環境變量后,即使env文件里的DB_HOST為homestead用env函數讀取出來的也還是容器里之前設置的DB_HOST環境變量的值database(docker中容器鏈接默認使用服務名稱,在編排文件中我把mysql容器的服務名稱設置成了database, 所以php容器要通過database這個host來連接mysql容器)。因為用我們在持續集成中做自動化測試的時候通常都是在容器里進行測試,所以Dotenv不會覆蓋已存在環境變量這個行為就相當重要這樣我就可以只設置容器里環境變量的值完成測試而不用更改項目里的env文件,等到測試完成后直接去將項目部署到環境上就可以了。
如果檢查環境變量不存在那么接著Dotenv就會把環境變量通過PHP內建函數putenv設置到環境中去,同時也會存儲到$_ENV和$_SERVER這兩個全局變量中。
在項目中讀取env配置在Laravel應用程序中可以使用env()函數去讀取環境變量的值,比如獲取數據庫的HOST:
env("DB_HOST`, "localhost");
傳遞給 env 函數的第二個值是「默認值」。如果給定的鍵不存在環境變量,則會使用該值。
我們來看看env函數的源碼:
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內建函數getenv讀取環境變量。
我們看到了在加載配置和讀取配置的時候,使用了putenv和getenv兩個函數。putenv設置的環境變量只在請求期間存活,請求結束后會恢復環境之前的設置。因為如果php.ini中的variables_order配置項成了 GPCS不包含E的話,那么php程序中是無法通過$_ENV讀取環境變量的,所以使用putenv動態地設置環境變量讓開發人員不用去關注服務器上的配置。而且在服務器上給運行用戶配置的環境變量會共享給用戶啟動的所有進程,這就不能很好的保護比如DB_PASSWORD、API_KEY這種私密的環境變量,所以這種配置用putenv設置能更好的保護這些配置信息,getenv方法能獲取到系統的環境變量和putenv動態設置的環境變量。
本文已經收錄在系列文章Laravel源碼學習里,歡迎訪問閱讀。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/29552.html
摘要:過去一年時間寫了多篇文章來探討了我認為的框架最核心部分的設計思路代碼實現。為了大家閱讀方便,我把這些源碼學習的文章匯總到這里。數據庫算法和數據結構這些都是編程的內功,只有內功深厚了才能解決遇到的復雜問題。 過去一年時間寫了20多篇文章來探討了我認為的Larave框架最核心部分的設計思路、代碼實現。通過更新文章自己在軟件設計、文字表達方面都有所提高,在剛開始決定寫Laravel源碼分析地...
摘要:總結本文主要學習了啟動時做的七步準備工作環境檢測配置加載日志配置異常處理注冊注冊啟動。 說明:Laravel在把Request通過管道Pipeline送入中間件Middleware和路由Router之前,還做了程序的啟動Bootstrap工作,本文主要學習相關源碼,看看Laravel啟動程序做了哪些具體工作,并將個人的研究心得分享出來,希望對別人有所幫助。Laravel在入口index...
摘要:終止程序終止中間件內核的方法會調用中間件的方法,調用完成后從請求進來到返回響應整個應用程序的生命周期就結束了。 Http Kernel Http Kernel是Laravel中用來串聯框架的各個核心組件來網絡請求的,簡單的說只要是通過public/index.php來啟動框架的都會用到Http Kernel,而另外的類似通過artisan命令、計劃任務、隊列啟動框架進行處理的都會用到C...
摘要:調用了的可以看出,所有服務提供器都在配置文件文件的數組中。啟動的啟動由類負責引導應用的屬性中記錄的所有服務提供器,就是依次調用這些服務提供器的方法,引導完成后就代表應用正式啟動了,可以開始處理請求了。 服務提供器是所有 Laravel 應用程序引導中心。你的應用程序自定義的服務、第三方資源包提供的服務以及 Laravel 的所有核心服務都是通過服務提供器進行注冊(register)和引...
摘要:然而,本文的討論重點,還是背后的源碼,是怎么做到這一步的。從哪開始看源碼位于你還是可以使用編輯器搜,就可以看到源碼了。第三步序列化所有路由注冊映射關系,還是在的方法中上面的方法位于中的中。所以到這里,的源碼解讀就完成了。 學 Laravel 和 Vuejs 你真應該來 codecasts.com ! Laravel ?route:cache 可以直接緩存路由文件,這樣其實可以在一定程度...
閱讀 1147·2021-11-25 09:43
閱讀 2965·2019-08-30 15:54
閱讀 3349·2019-08-30 15:54
閱讀 2991·2019-08-30 15:44
閱讀 1623·2019-08-26 12:18
閱讀 2255·2019-08-26 11:42
閱讀 875·2019-08-26 11:35
閱讀 3295·2019-08-23 18:22