基本的なphp-diの使い方
マニュアル: http://php-di.org/doc/getting-started.html
インストール
現時点ではversion6がインストールされる
composer require php-di/php-di
DI対象のクラスの例
HogeクラスはFugaクラスを引数に取るように定義
class Fuga { public function hello() { echo "FUGA\n"; } } class Hoge { protected $fuga; public function __construct(Fuga $fuga) { $this->fuga = $fuga; } public function hello() { echo "HOGE\n"; $this->fuga->hello(); } }
PHPによるコンテナ定義
ドキュメント: http://php-di.org/doc/php-definitions.html
コンテナ定義の基本形
// test.php require_once __DIR__.'/vendor/autoload.php'; use Psr\Container\ContainerInterface; $container = new DI\Container(); $builder = new DI\ContainerBuilder(); $builder->addDefinitions([ Hoge::class => function (ContainerInterface $c) { return new Hoge($c->get(Fuga::class)); }, Fuga::class => function (ContainerInterface $c) { return new Fuga(); }, ]); $container = $builder->build(); $container->get(Hoge::class)->hello();
実行すると
% php test.php HOGE FUGA %
コンテナ値の定義(addDefinitions)は様々な書き方が可能
// クロージャの引数をコンテナにしない事も可能 Hoge::class => function (Fuga $fuga) { return new Hoge($fuga); }, // DIクラスのメソッド記法 Hoge::class => DI\create(Hoge::class)->constructor(DI\get(Fuga::class)), // createメソッドは引数と対象のクラスが同じなら省略も可能 Fuga::class => DI\create(), // createの代わりにautowireを使うと自動でconstructorの依存関係も解釈してインジェクションを行う Hoge::class => DI\autowire(),
build後もcontainerにsetする事も可能(が、build後に設定する事になるのでキャッシュ機能を使うとエラーになる)
$builder->addDefinitions([ Hoge::class => DI\autowire(), ]); $container = $builder->build(); $container->set(Fuga::class, DI\create());
パラメータ名とクラス名が一致してる場合、addDefinitionsによる定義を省略しても自動で読み込まれる(Autowiring機能)
$builder = new ContainerBuilder(); $container = $builder->build(); $container->get(Fuga::class)->hello();
アノテーションによるコンテナ定義
ドキュメント: http://php-di.org/doc/annotations.html
doctrine/annotationsが必要となるのでインストール
composer require doctrine/annotations
アノテーションはbuildする前に明示的に「$containerBuilder->useAnnotations(true);」する必要がある
$container = new DI\Container(); $builder = new DI\ContainerBuilder(); $builder->useAnnotations(true); $container = $builder->build(); $container->get(Hoge::class)->hello();
そしてアノテーションでクラスにインジェクションが行えるようになる
class Hoge { /** * @Inject * @var Fuga */ private $fuga1; private $fuga2; /** * @Inject * @param Fuga $fuga */ public function __construct($fuga) { $this->fuga2 = $fuga; } public function hello() { echo "HOGE\n"; $this->fuga1->hello(); $this->fuga2->hello(); } }
実行すると
% php test.php HOGE FUGA FUGA %
キャッシュの設定
キャッシュを指定する事も可能なので本番環境では設定すると良い
$builder = new \DI\ContainerBuilder(); $builder->enableCompilation(__DIR__ . '/tmp'); $builder->writeProxiesToFile(true, __DIR__ . '/tmp/proxies'); $container = $builder->build();
pimpleコンテナとの並行利用
元々pimpleのコンテナを利用しており、いきなりphp-diに切り替えるのはちょっと怖いなという場合はpimpleコンテナとphp-diコンテナを1つのコンテナにまとめることが可能
まずはライブラリをインストール
composer require pimple/pimple composer require acclimate/container
複数コンテナを一つのコンテナとして扱えるようにできる。このようにすると内部的に徐々にコンテナ定義を移行できる
// test.php require_once __DIR__.'/vendor/autoload.php'; use Pimple\Container as PimpleContainer; use DI\ContainerBuilder; use Acclimate\Container\ContainerAcclimator; use Acclimate\Container\CompositeContainer; # pimpleで定義 $pimpleContainer = new PimpleContainer(); $pimpleContainer['bar'] = function ($c) { return new Bar(); }; # PHP-DIで定義 $builder = new ContainerBuilder(); $phpdiContainer = $builder->build(); $acclimator = new ContainerAcclimator; $pimpleContainer = $acclimator->acclimate($pimpleContainer); $phpdiContainer = $acclimator->acclimate($phpdiContainer); $container = new CompositeContainer([$pimpleContainer, $phpdiContainer]); $container->get(Hoge::class)->hello(); $container->get('bar')->hello(); class Fuga { public function hello() { echo "FUGA\n"; } } class Hoge { protected $fuga; public function __construct(Fuga $fuga) { $this->fuga = $fuga; } public function hello() { echo "HOGE\n"; $this->fuga->hello(); } } class Bar { public function hello() { echo "BAR\n"; } }
実行
% php test.php HOGE FUGA BAR %