基本的な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
%