💡

Laravelのサービスコンテナをもっと使いこなしてみる! bindメソッドのクロージャに引数を指定してみた

2024/05/10に公開

はじめに

Laravelのサービスコンテナにあるbindメソッドのクロージャに何か引数を指定できないか調べてみたところ、指定できたので、その結果を書きます。

環境

  • PHP 8.3.6
  • Laravel 11.7.0

サンプルコード

今回の調査につかった適当なサンプルです。

namespace App;

interface InterfaceXX
{
    public function get(): int;
}

class ClassA implements InterfaceXX
{
    public function get(): int
    {
        return 10;
    }
}

class ClassB implements InterfaceXX
{
    public function get(): int
    {
        return 100;
    }
}

本題

いきなり結果ですが、サンプルコードを使って次のようなコードが書けることがわかりました。

app/Providers/AppServiceProvider.php

use App\ClassA;
use App\ClassB;
use App\InterfaceXX;

    public function register(): void
    {
        $this->app->bind(InterfaceXX::class, function ($app, $parameter) {
            if ($parameter['flag'] === true) {
                return new ClassA();
            } else {
                return new ClassB();
            }
        });
    }
% php artisan tinker
Psy Shell v0.12.3 (PHP 8.3.6 — cli) by Justin Hileman
> use App\InterfaceXX;
> $class = app()->make(InterfaceXX::class, ['flag' => true]);
= App\ClassA {#5060}

> $class->get();
= 10

> $class = app()->make(InterfaceXX::class, ['flag' => false]);
= App\ClassB {#5105}

> $class->get();
= 100

なぜできるのか

色々省略しちゃいますが、makeメソッドを実行すると、resolveメソッドを呼びます。

https://github.com/laravel/framework/blob/v11.7.0/src/Illuminate/Container/Container.php#L721-L724

resolveメソッドの処理の中でbuildメソッドが呼ばれ、

https://github.com/laravel/framework/blob/v11.7.0/src/Illuminate/Container/Container.php#L786-L788

buildメソッドの中でクロージャの第2引数に何か渡していることがわかります。

https://github.com/laravel/framework/blob/v11.7.0/src/Illuminate/Container/Container.php#L894-L902

第2引数に指定している、getLastParameterOverrideメソッドをみると配列を返すことがわかります。

https://github.com/laravel/framework/blob/v11.7.0/src/Illuminate/Container/Container.php#L1015-L1018

$this->withというものが、resolveメソッドの引数の$parametersを設定している。

https://github.com/laravel/framework/blob/v11.7.0/src/Illuminate/Container/Container.php#L777

つまり、makeメソッドの第2引数の配列が設定されるので、 サービスコンテナのbindメソッドの引数に指定したクロージャの引数になるわけです。

makeメソッドの第2引数はなぜあるのか?

つぎのようなbindメソッドにクロージャではなくクラスを指定した場合、コンストラクタの引数を任意の値を指定することができます。

namespace App;

class ClassC implements InterfaceXX
{
    public function __construct(private int $num)
    {
    }

    public function get(): int
    {
        return $this->num;
    }
}
app/Providers/AppServiceProvider.php

use App\ClassC;
use App\InterfaceXX;

    public function register(): void
    {
        $this->app->bind(InterfaceXX::class, ClassC::class);
    }
% php artisan tinker
Psy Shell v0.12.3 (PHP 8.3.6 — cli) by Justin Hileman
> use App\InterfaceXX;
> $class = app()->make(InterfaceXX::class, ['num' => 1000]);
= App\ClassC {#5103}

> $class->get();
= 1000

まとめ

小ネタでした。
Laravelに詳しくはなれたけど活用する機会は少ないかもしれないです。

Discussion