内部挙動を押さえときたかったので。一部日本語が変なのはご愛嬌
目的
- コンテナ名前解決の仕組みをたどる
- コンストラクタ自動注入の仕組みをたどる
サービスコンテナ
オブジェクトのかご。オブジェクトに名前つけて突っ込んで後から取り出す。都合のいいグローバル変数みたいな(使い方をしてはイケない)。
突っ込みたい人向け
インスタンスを突っ込む
オブジェクトを作ってから突っ込む。
$fooBar = new FooBar(new SomethingElse);
$app->instance('FooBar', $fooBar);
オブジェクトの作り方を突っ込む
オブジェクトの作り方だけ。取り出す人が作ればええやんスタイル。
$app->bind('FooBar', function($app) {
return new FooBar($app['SomethingElse']);
});
オブジェクトの作り方を突っ込む(節約指向)
オブジェクトの作り方だけ。一番最初に取り出した人が作って、後の人は最初の人が作ったやつをもらう。
$app->singleton('FooBar', function($app) {
return new FooBar($app['SomethingElse']);
});
欲しい人向け
決めた名前で取り出す。
$fooBar = $app->make('FooBar');
ここまでがざっくり使い方。
コンテナへの登録:お名前と本体
サービスコンテナには 登録時の名前 $abstract
と、それに紐づく処理$concrete
とがある。
bind
とかsingleton
でいうところの第一引数が$abstract
で第二引数が$concrete
。ちなみにsingleton
はbind
の糖衣構文でしか無く処理はほぼ同じ(sharedフラグを立てるだけ)。
/**
* Register a shared binding in the container.
*
* @param string $abstract
* @param \Closure|string|null $concrete
* @return void
*/
public function singleton($abstract, $concrete = null)
{
$this->bind($abstract, $concrete, true);
}
bind
に渡された$concrete
は、どのような値であっても内部的に全てクロージャに変換される。
makeによる取り出しで何がおこるか
$fooBar = $app->make($abstract);
は、内部的には
$fooBar = $app->build($concrete);
に変換される。
(かなりざっくり端折った。実際にはsingletonキャッシュとかその辺が色いろある。)
bindにより紐付けられた$abstract
によるコールmake($abstract)
は、紐づけられた$concrete
によるコールbuild($concrete)
となる。
この場合、bind
による$concrete
はクロージャに変換される、のルールによりbind
の引数は必ずクロージャである。
紐付のない$abstract
によるコールmake($abstract)
は、build($abstract)
に変換され、これもまたbind
メソドへと渡される。
このケースの引数はクロージャとは限らない。
build の実装
build
はクロージャを受け取って、それを実行する。引数としてコンテナ自信を渡すため、クロージャでは第一引数に$app
が取れる。
これだけであれば非常にシンプルなのだが、ややこしいのがクロージャでないケース。
$concrete
はbind
時に自動的にクロージャに変換される。この変換されるクロージャは概ねこんな感じである。
function($app) use ($concrete)
{
return $app->build($concrete);
}
クロージャと言えどbuild
をラップしているだけの構成。
自動でクロージャに変換された非クロージャの$concrete
は、一旦は自動生成されたクロージャとしてbuild
に解釈されても、次のステップで元の文字列としてbuild($concrete)
される。
build
の引数に文字列(クラス名)を与えた場合、当該クラスのインスタンスを生成する。
これはリフレクションを用いたlaravelっぽいコンストラクタ注入自動解決のあれでインスタンスを生成するマンで、タイプヒントに基づくコンテナからのクラス取り出しを連鎖的に自動で行ってくれるもの。
このような理由でコンテナに突っ込んだクラス名は、自動的にコンストラクタ注入が適用されるというわけ。
つまりコンテナ外でもbuild
メソドさえコールしてやれば、勝手にコンストラクタ注入してくれる。あまりオススメはしないけど。