🌿

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行に答えがありました。

https://github.com/laravel/framework/blob/v13.29.0/src/Illuminate/Foundation/Http/Kernel.php#L164-L176

$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リクエストではないので/が取れる
GitHubで編集を提案

Discussion