Laravelの`request()`はいつContainerに登録されるのか
はじめに
ServiceProviderで次のような実装があった。
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
$path = request()->path();
}
public function register(): void
{
$path = request()->path();
}
}
この場合、それぞれ次のような動きになると思っていた。
-
boot()-> URLのpathは取得できる -
register()-> URLのpathは取得できない
ただ、実際は違って、boot()とregister()のどちらでもURLのpathを取得できた。
なぜ、register()でrequest()は使えるのか少し調べてみた。
環境
- PHP 8.5.10
- Laravel 13.29.0
ServiceProviderの動きを確認する
ざっくりした流れはこんな感じでした。
public/index.php
↓
$app->handleRequest($request)
↓
Http\Kernel::handle($request)
↓
sendRequestThroughRouter($request)
↓
bootstrap()
↓
ServiceProvider::register() // サービスコンテナに登録を行う
ServiceProvider::boot()
↓
Middleware
↓
Router
bootstrap()の中でServiceProviderのregister()とboot()が呼ばれる。
なので、register()の時点ではまだ依存関係をコンテナに登録は行なっていないので使えない認識でした。
しかし、その1つ前にsendRequestThroughRouter($request)が呼ばれています。
実行している内容を確認したところ、最初の1行に答えがありました。
$this->app->instance('request', $request);
つまり、ServiceProviderの処理が動く前にRequestクラスはコンテナに登録済みのため。register()の処理の中でrequest()->path()を実行してもpathは取得できたわけです。
Artisanコマンドの場合はどうなる?
さきほど確認した内容はHTTPリクエストの場合です。
LaravelにはArtisanコマンドというCLIツールが用意されており、こちらも実行時は最初にServiceProviderの処理が動きます。
ただし、HTTPリクエストではないのでURLのpathは取得できないです。
それは、ServiceProviderが動くまでの流れがそもそも違うからです。
artisan
↓
$app->handleCommand(new ArgvInput)
↓
Console\Kernel::handle($request)
↓
bootstrap()
↓
ServiceProvider::register() // サービスコンテナに登録を行う
ServiceProvider::boot()
↓
run
HTTPの方で実行していた$this->app->instance('request', $request);が動かないので取得できない。(request()自体は実行できるため、request()->path()を実行すると/が返る。)
まとめ
- HTTPリクエストはServiceProviderが動く前にRequestクラスのコンテナ登録がされる
- そのため、ServiceProviderの
register()とboot()でもrequestのpathが取れる - Artisanコマンドでは、HTTPリクエストではないので
/が取れる
Discussion