====== DIコンテナー - League/Container ====== Version 3.3 ([[https://github.com/thephpleague/container/blob/master/LICENSE.md|MIT License]]) --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-04-18// [[basic-library:top|定番ライブラリー に戻る]] 関連記事 * [[basic-library:project|プロジェクトの作成 - Apricot (α版)]] * [[basic-library:phpdotenv:4.1|環境変数 - phpdotenv]] * [[basic-library:monolog:2.0|ログ出力 - monolog]] * [[basic-library:idiorm:1.5|ORマッパー - Idiorm]] * [[basic-library:bladeone:3.37|テンプレートエンジン - BladeOne]] * [[basic-library:fast-route:1.3|リクエストルーター - FastRoute]] * DIコンテナー - League/Container * [[basic-library:valitron:1.4|バリデーター - Valitron]] * [[basic-library:whoops:2.7|エラーハンドラー - Whoops]] * [[basic-library:php-debugbar:1.16|デバッグ出力 - php-debugbar]] リンク * https://github.com/thephpleague/container --- League/Containerの本家 * https://container.thephpleague.com/ --- League/Containerのマニュアル(英語) * https://thephpleague.com/ --- The League of Extraordinary Packages テストプログラムの所在 {Project Folder}\test\league-container\ ---- ===== League/Containerについて ===== DIコンテナの主な目的はコントローラへの依存性の注入(DI)にあると思います。それはビジネスロジックであるサービスと、コントローラやビューとの結合性を如何に疎にするかによって開発効率、保守性やテスト容易性が決まるからに他なりません。しかし「ちょっとした機能のプログラムをPHPでサクサクと実装したい」のが目的のApricotにDIコンテナが果たして必要なのでしょうか。迷いましたが結果的には、ORMやリクエストルーターと同じくシンプルで軽量なものを選定して追加することにしました。現在ではDIコンテナはもはやソフトウェア開発にとって当たり前の部品なのかもしれません。 DIコンテナにはいくつかの候補があがりました。シンプルで軽量という時点で多機能で秀作なDIコンテナである[[http://php-di.org/|PHP-DI]]は除外されましたが、場合によっては選択してもよかったと思っています。そして次の2つが候補に残りました: * [[https://pimple.symfony.com/|Pimple]] * [[https://container.thephpleague.com/|Container]] Pimpleは[[https://www.slimframework.com/|Slim]]でも採用されいる軽量DIコンテナです。どちらにするか迷いましたが、最終的には、PHP-DIにも搭載されている ''Auto Wiring'' をサポートしているContainerの方を採用することにしました。''Auto Wiring'' とはコンストラクター引数の型ヒントを調べることにより、オブジェクトとそのすべての依存関係を再帰的に自動的に解決する機能の事です。''Auto Wiring'' については手動での簡易実装を考えていたので、その手間のかからないContainerを選びました。 尚、Containerについては、(Containerではあまりに一般的な呼称なので)誤解のないように、プロバイダ名を冠して「League/Container」と呼ぶ事にします。League(リーグ)の正式名称は [[https://thephpleague.com/|The League of Extraordinary Packages]] で、この組織は最新のコーディング標準を使用して、しっかりとテストされたPHPパッケージを構築するために結束した開発者のグループのことです。 ===== インストール ===== composer require league/container Using version ^3.3 for league/container ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 2 installs, 0 updates, 0 removals - Installing psr/container (1.0.0): Loading from cache - Installing league/container (3.3.0): Loading from cache Writing lock file Generating autoload files 1 package you are using is looking for funding. Use the `composer fund` command to find out more! >Note: >Eclipse起動中にパッケージを取得した場合は、プロジェクト・エクスプローラー内の[プロジェクト(apricote)]を右クリックして[リフレッシュ]を選択して下さい。また、新しく取得したパッケージのインテリセンスが有効にならない場合は、プロジェクトのビルトまたはクリーン&ビルドを行ってビルドリストの更新を行って下さい。 パッケージの取得が終わると ''composer.json'' の ''require'' に以下が追加されます。 { "require": { "league/container": "^3.3" } } \\ ===== テストプログラム ===== 以下の例題は、コンストラクター・インジェクションを3つの方法で行ったものです。最初はDIコンテナーを使用しない場合、2つ目はDIコンテナーを使用した場合、最後にAuto Wiringを使ったものです。テスト用のコードはテストフォルダ(test\league-container\)に、作成します。この例は League/Containerのマニュアル に記載されている [[https://container.thephpleague.com/3.x/auto-wiring|Auto Wiringの例題]] を簡素化したものです。 * Foo.php --- 2つのサービス(BarとBza)を持つFooコントローラ * index1.php --- 手動によるコンストラクター・インジェクションの例 * index2.php --- DIコンテナ― によるコンストラクター・インジェクションの例 * index3.php --- Auto Wiring によるコンストラクター・インジェクションの例 * index4.php --- Auto Wiring だがコンテナーをキャッシュしている例 bar = $bar; $this->baz = $baz; } } class Bar { // .. } class Baz { // .. } \\ ==== 【テスト1】DIコンテナを使用しない場合 ==== DIコンテナーを使わずに、サービスをコントローラに手動で注入している例です。尚、例題ではComposerによるAutoloadを使用していないので、spl_autoload_register()で代替しています。 bar instanceof Acme\Bar); // true var_dump($foo->baz instanceof Acme\Baz); // true === 結果 === D:\usr\ws2019\apricot\test\league-container\index1.php:17:boolean true D:\usr\ws2019\apricot\test\league-container\index1.php:18:boolean true D:\usr\ws2019\apricot\test\league-container\index1.php:19:boolean true \\ ==== 【テスト2】DIコンテナを使用する場合 ==== DIコンテナ― によるコンストラクター・インジェクションの例です。依存関係をクラスコンストラクターに渡すことは、依存性注入の最も簡単な方法です。League/Container ではコンストラクター・インジェクションの他に、セッター・インジェクションとクラスファクトリーによる依存性注入の方法もサポートしています。 add(Acme\Foo::class) ->addArgument(Acme\Bar::class) ->addArgument(Acme\Baz::class) ; $container->add(Acme\Bar::class); $container->add(Acme\Baz::class); $foo = $container->get(Acme\Foo::class); var_dump($foo instanceof Acme\Foo); // true var_dump($foo->bar instanceof Acme\Bar); // true var_dump($foo->baz instanceof Acme\Baz); // true === 結果 === D:\usr\ws2019\apricot\test\league-container\index2.php:25:boolean true D:\usr\ws2019\apricot\test\league-container\index2.php:26:boolean true D:\usr\ws2019\apricot\test\league-container\index2.php:27:boolean true \\ ==== 【テスト3】Auto Wiringを使用する場合 ==== League/Containerは **Auto Wiring** 機能をサポートします。これは、コンストラクター引数の型ヒントを調べることにより、オブジェクトとそのすべての依存関係を再帰的に自動的に解決する機能です。但し、注入できるのはオブジェクト型の変数だけです。 >Auto Wiring はデフォルトで無効になっているので、有効にするにはコンテナデリゲートとして ''ReflectionContainer'' を登録しなけらばなりません。 delegate( new League\Container\ReflectionContainer ); $foo = $container->get(Acme\Foo::class); var_dump($foo instanceof Acme\Foo); // true var_dump($foo->bar instanceof Acme\Bar); // true var_dump($foo->baz instanceof Acme\Baz); // true === 結果 === D:\usr\ws2019\apricot\test\league-container\index3.php:23:boolean true D:\usr\ws2019\apricot\test\league-container\index3.php:24:boolean true D:\usr\ws2019\apricot\test\league-container\index3.php:25:boolean true \\ ==== 【テスト4】Auto Wiringを使用する場合(キャッシュ有効) ==== デフォルトでは ReflectionContainer は、要求の度にそれを解決しようとします。ReflectionContainer でキャッシュ機能を有効にするには以下のようにcacheResolutions()を使用します。 delegate( (new League\Container\ReflectionContainer)->cacheResolutions() ); $fooOne = $container->get(Acme\Foo::class); $fooTwo = $container->get(Acme\Foo::class); var_dump($fooOne === $fooTwo); // true === 結果 === D:\usr\ws2019\apricot\test\league-container\index4.php:23:boolean true \\