iT邦幫忙

2019 iT 邦幫忙鐵人賽

1

不同公司所制定的系統環境規範都有所不同。比方說環境變數的設定,敝公司的規範如下:

  1. 不設定系統環境變數 /etc/environment,而是使用檔案載入
  2. 系統人員將會把檔案放在 /path/to/.env
  3. 部分的環境設定系統人員不清楚,會由開發人員設定。換句話說,還有另一個 env 是跟著專案跑的。

上述會有幾個要點要注意:

  1. Laravel 預設只有載入 /path/to/project/.env,而上面的規範,還會多載 /path/to/.env
  2. 系統人員設定的 .env 開發人員不能隨意覆蓋

很直接的 Workaround

最一開始,筆者使用最蠢的 workaround,直接修改 bootstrap/app.php

// 在 return Application 前,載入該載的 env 即可
if (file_exists('/path/to/.env')) {
    (new Dotenv\Dotenv('/path/to'))->load();
}

return $app;

簡單,也很有效。只是有點不明顯,在重建環境時,很容易忘了這件事。

自定義新的 Bootstrapper

分析 bootstrap 流程有提到原本的 .env 如何載入,我們想辦法來客製化這個流程,讓載 env 也成為 Laravel Bootstrap 的流程之一。

一個直接做法是,自己寫一個 bootstrap:

<?php
// app/Bootstrap/CustomizeLoadEnvironmentVariables.php
namespace App\Bootstrap;

use Dotenv\Dotenv;
use Illuminate\Contracts\Foundation\Application;

class CustomizeLoadEnvironmentVariables
{
    public function bootstrap(Application $app)
    {
        if (file_exists('/path/to/.env')) {
            (new Dotenv('/path/to'))->load();
        }
    }
}

然後在 Http Kernel 覆寫 bootstrappers 屬性即可:

protected $bootstrappers = [
    App\Bootstrap\CustomizeLoadEnvironmentVariables::class,
    \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,
];

覆寫 Bootstrapper

自定義新的 Bootstrapper 的缺點是,會需要覆寫一個修改內容不多的 bootstrappers,某種程度這也算是 copy & paste 的產物,這是不符合 DRY 原則的。

這裡還有另一個做法則是拿原有的來覆寫:

<?php

namespace App\Bootstrap;

use Dotenv\Dotenv;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables as BaseLoadEnvironmentVariables;

class LoadEnvironmentVariables extends BaseLoadEnvironmentVariables
{
    public function bootstrap(Application $app)
    {
        if (file_exists('/path/to/.env')) {
            (new Dotenv('/path/to'))->load();
        }

        parent::bootstrap($app);
    }
}

接著在 bootstrap/app.php 綁定這個實作即可:

// ======== Customize Binding ========

$app->singleton(
    Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
    App\Bootstrap\LoadEnvironmentVariables::class
);

可以參考分析 bootstrap 流程提到的如何產生 bootstrapper 實例,以及 Containerbind() 如何實作,即可了解為何這個做法是可行的。

而 Http Kernel 就不需要覆寫了。這也是筆者目前的做法。


上一篇
如何正確地在 Response 加 Header(2)
下一篇
array_get()、data_get() 與 object_get() 的差異
系列文
Laravel 原始碼分析46

尚未有邦友留言

立即登入留言