距離上一次分享差不多一週了,這篇文章,就分享下利用 Laravel 的 Bootstrapping
達到網站後臺設定 laravel 配置。
需求場景
首先,ThinkSNS+ 作為一個使用者可以使用的「社交系統」和開源網站程式一樣擁有後臺,那有一些配置,Laravel 是要求寫在 /config/*.php
的配置檔案中的,例如 app.name
、app.debug
等資訊的配置,以及 Jobs 的驅動配置,廣播系統的配置等,我們都搬到了網站後臺,使用者安裝後可以不用修改配置檔案的情況下映象配置。
如何覆蓋配置
我們首先開啟 Illuminate\Foundation\Application::bootstrapWith
方法,程式碼如下:
/**
* Run the given array of bootstrap classes.
*
* @param array $bootstrappers
* @return void
*/
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
$this->make($bootstrapper)->bootstrap($this);
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}
重點程式碼在 $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
和 $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
上,很明顯是載入並執行 bootstrapper 的前置和後置事件。
所以,我們看還有一個方法叫做 beforeBootstrapping
和 afterBootstrapping
然後怎麼做呢?我們看
// Illuminate\Foundation\Http::$bootstrappers
/**
* The bootstrap classes for the application.
*
* @var array
*/
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,
];
沒錯,這裡是固定了順序的,我錯誤的載入順序,會造成laravel的失敗,所以,我們選擇在 之前繼承 Illuminate\Foundation\Application
的 應用基礎上增加一個事件,程式碼如下:
public function __construct($basePath = null)
{
parent::__construct($basePath);
// Load configuration after.
$this->afterBootstrapping(\Illuminate\Foundation\Bootstrap\LoadConfiguration::class, function ($app) {
$app->make(\Zhiyi\Plus\Bootstrap\LoadConfiguration::class)
->handle();
});
}
哪裡新增的事件?
因為 ThinkSNS+ 是繼承了 Illuminate\Foundation\Application
實現了新的 Application 類,所以我們直接在構造方法裡面增加了程式碼。
這樣,當 Laravel 啟動,但是還沒有載入 bootstrapper 的時候,已經把 載入配置的後置事件注入進去了。當載入配置執行完成後就會執行我注入的後置事件。
後置事件的實現
我們在建立了 \Zhiyi\Plus\Bootstrap\LoadConfiguration
這樣一個類,註冊為後置事件,路徑為: /app/Bootstrap/LoadConfiguration.php
,然後實現程式碼如下:
<?php
namespace Zhiyi\Plus\Bootstrap;
use Zhiyi\Plus\Support\Configuration;
use Illuminate\Contracts\Foundation\Application;
class LoadConfiguration
{
protected $app;
protected $configuration;
/**
* 載入配置構造方法.
*
* @author Seven Du <shiweidu@outlook.com>
*/
public function __construct(Application $app, Configuration $configuration)
{
$this->app = $app;
$this->configuration = $configuration;
}
/**
* Run handler.
*
* @return void
* @author Seven Du <shiweidu@outlook.com>
*/
public function handle()
{
static $loaded = false;
if ($loaded) {
return;
}
$this->app->config->set(
$this->configuration->getConfigurationBase()
);
}
}
很簡單,因為 app('config')
是一個 Illuminate\Contracts\Config\Repository
介面的例項,所以直接呼叫 set 方法進行配置覆蓋。
而 Zhiyi\Plus\Support\Configuration
類是封裝的自定義配置載入類,載入的配置檔案存放在一個 YAML 檔案中,這個類實現了 自定義配置檔案的載入和儲存。這樣,我們從後臺呼叫 API 然後 constroller 呼叫這個類的 save 方法進行持久化。
Zhiyi\Plus\Support\Configuration::getConfigurationBase
為什麼要特殊說一下這個方法?因為這個方法的特殊性,也是 depth merge 實現的重要函式,在 Repository 中支援 app.name = value
這樣的形式進行深曾鍵值賦值,利用這一個特性,這個函式將 多維陣列轉換為一維。
效果:
// 多維陣列
$arr = [
'app' => [
'name' => 'xxx',
'env' = 'local'
]
];
// 轉換後
$arr = [
'app.name' => 'xxx',
'app.env' => 'local',
];
然後呼叫 app('config')->set($arr)
就對 Laravel 的 config 進行了 depth merge。
最後,持久化儲存的 YAML 內容如下:
app:
name: ThinkSNS+
queue:
default: database
cache:
default: memcached
stores:
memcached:
servers:
host: 127.0.0.1
port: 11211
所以,基於 depth merge 在 .plus.yml
配置中,只需要儲存部分配置,即可不想配置結構的完整性的情況下對 Laravel 映象配置合併。
ThinkSNS+ 倉庫:https://github.com/zhiyicx/thinksns-plus
感謝大家的閱讀,開源不易,請為我們點一個 Star ?
Seven 的程式碼太渣,歡迎關注我的新擴充包 medz/cors 解決 PHP 專案程式設定跨域需求。