引言
為了把配置引數獨立出來,用以區分開發環境,線上環境等功能, 或者手動切換快取的驅動,佇列的驅動,郵件伺服器地址,等等等等, 這些可以方便地標記。所以laravel使用 .env 檔案包裹這些配置資料,也就是鍵值對。
學習時間一般情況下,我們不允許修改env檔案的內容,除非手動處理。可是在程式設計中難免遇到非修改不可的情況, 那麼又該如何動態地操作env檔案內的鍵值對呢?
假設對於系統使用 key:generate 生成的 APP_KEY 不安全,在做自動化部署,批量部署時有動態修改 APP_KEY 這個鍵的需求。該怎麼來實現呢?
其實,env檔案不過是一個文字檔案,遵循 key=value 這樣的標準格式進行書寫,全程使用字串匹配, 單行直到換行符停止。
那麼修改 env 檔案內容,無非就是找到相關的鍵,然後將值替換掉,如此而已。
下面給出第一個版本,也就是簡單粗暴的 file_put_contents,先獲取env檔案的路徑:
$path = base_path('.env');
需要判斷檔案是否存在:
if (file_exists($path)){ // 檔案存在}
檔案存在則先讀出檔案的所有內容到一個字串變數內:
$origin = file_get_contents($path);
假設我們的新 APP_KEY 存在變數 $new_key 內,首先獲取原始的 APP_KEY的值:
$old_key = env('APP_KEY');
字串操作當然要使用字串替換函式直接匹配,我們使用 str_replace,env檔案的資料量畢竟不大, 這麼也也沒有太大效能的問題。
$result = str_replace('APP_KEY=' . $old_key, $new_key, $origin);
這樣$result記憶體儲的就是最新的env檔案的值,接下來寫入env檔案就行了:
file_put_contents($result);
預設是覆寫,所以執行完程式,env檔案就是最新的動態修改的資料了。
深入一步上面的程式碼還是有瑕疵的,因為對於錯誤故障處理基本上沒有,這很容易造成錯誤。 另外對於env這麼重要的檔案操作,直接使用字串替換,整個檔案的讀和覆寫, 本身的風險就非常高。
如何改造我們的操作方式,使其更為安全呢?我們需要相容性更好的程式碼。本節我們嘗試使用正則匹配的方式, 來解析env檔案,並逐行讀取,逐行操作,逐行判斷, 對於存在的鍵值,進行覆蓋;對於不存在的,則進行建立。 這樣就可以相容新建和更新兩種功能,且支援的鍵值更為靈活。
封裝為助手函式,假設傳入的引數為陣列,且是關聯陣列。宣告函式如下:
function updateEnv($data = array()){}
函式體內書寫邏輯,首先非空判斷:
if (! count($data)) {return;}
如果不是關聯陣列,也同樣不接受,因為env檔案必須明確指定鍵和值。 關聯陣列只用判斷陣列的鍵與自動序列化的鍵不同即可:
if (array_keys($data) === range(0, count($data) - 1)) {return;}
準備匹配模式:
$pattern = '/([^\\=]*)\\=[^\\n]*/';
這就是env檔案書寫的格式。上一節我們已經介紹過了。我們把舊的env檔案讀入一個數組,並宣告新的陣列,儲存最新的配置檔案資料:
$envFile = base_path() . '/.env';$lines = file($envFile);$newLines = [];
然後遍歷舊的檔案資料,逐行解析:
foreach ($lines as $line) { preg_match($pattern, $line, $matches); if (!count($matches)) { $newLines[] = $line; continue; } if (!key_exists(trim($matches[1]), $data)) { $newLines[] = $line; continue; } $line = trim($matches[1]) . "={$data[trim($matches[1])]}\\n"; $newLines[] = $line;}
上面只是一個大致的處理流程,這個解析過程,你可以獨立為自定義函式,或者其他解析引擎,具有通用性。
最後把解析完的新資料,完整寫入env檔案內:
$newContent = implode('', $newLines);file_put_contents($envFile, $newContent);
至此,env檔案的更新操作就完成了。
寫在最後本文通過兩種方式實現了在程式內動態建立和更新env全域性配置檔案檔案資料的功能, 第二種方法容錯性更好,具有通用性,擴充套件性強,所以我們推薦。 第一種做法沒有錯誤處理,生產環境下幾乎不能用。大家知道思路就好了。
Happy coding :-)