分析 bootstrap 流程--Lumen 篇

與 Laravel 一樣,從進入點 index.php 開始看起。

$app = require __DIR__.'/../bootstrap/app.php';

$app->run();

Lumen 顯得非常簡單,只有拿 Application 後,直接 run 即可。

而 bootstrap/app.php 如下:

require_once __DIR__.'/../vendor/autoload.php';

// 載 .env
try {
(new Dotenv\Dotenv(__DIR__.'/../'))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
//
}

// 建構 Lumen Application,並帶入主要目錄
$app = new Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);

// 設定 container
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);

$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);

// 註冊 route
$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});

return $app;

回顧之前分析 bootstrap,這裡多了載 .env 與註冊 route 兩件事。這意味著 Lumen Application 並沒有處理這兩件事。

載 .env 是 Bootstrapper LoadEnvironmentVariables 所初始化的,Router 則是在 App\Providers\RouteServiceProvider::boot() 的時候註冊的。

不僅如此,從註解的說明可以知道,Lumen 預設很多事都沒有做,如 ServiceProvider 需要從頭來;Middleware 也要重頭來;Facade 需要另外設定,等。

來看 Lumen Application 的建構子有多精簡:

public function __construct($basePath = null)
{
if (! empty(env('APP_TIMEZONE'))) {
date_default_timezone_set(env('APP_TIMEZONE', 'UTC'));
}

$this->basePath = $basePath;

// 初始化 Container,註冊基本必要的 instance 與 alias
$this->bootstrapContainer();

// 註冊錯誤處理,是 Laravel Bootstrap\HandleExceptions 的任務
$this->registerErrorHandling();

// 初始化 Router,其實裡面就是 new Router
$this->bootstrapRouter();
}

大致上,原本 Laravel 會跑的 Bootstrap 任務,全都精簡化放在這裡了。

最後就是執行 run() 方法,它寫在 RoutesRequests 這個 trait 裡:

public function run($request = null)
{
// Dispatcher 由 Lumen Application 擔任,處理完取得 Response
$response = $this->dispatch($request);

// 將 response 送出給瀏覽器
if ($response instanceof SymfonyResponse) {
$response->send();
} else {
echo (string) $response;
}

// 執行 terminable middleware
if (count($this->middleware) > 0) {
$this->callTerminableMiddleware($response);
}
}

從 Request 可以傳 null 可得知,這同時也是提供了方便測試的接口。

Slim 也可以這麼做。

明天再繼續看 dispatch() 方法裡面做了什麼。