PHP-DI

Slim を久しぶりにさわったら、v4 にアップグレードした際に DI コンテナが PHP-DI に変わったようなので、使い方を確認してみた。

インストール

composer でパッケージをインストールする。

1
$ composer require php-di/php-di

コンテナを作成する

コンテナを作成するには、

  1. containerBuilder を作る
  2. containerBuilder から conatiner を作成する

という手順を取る。具体的には、こんな感じ。

1
2
3
4
5
6
7
8
<?php
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;

// Builderインスタンスの生成
$containerBuilder = new ContainerBuilder();
$container = $containerBuilder->build();
var_dump( get_class($container) );

コンテナに設定を保存する

containerBuilder の addDefinitions メソッドの引数の連想配列として設定を保存する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
require('vendor/autoload.php');

use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;

$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
'setting' => [
'message' => 'Hello, world',
],
]);

$container = $containerBuilder->build();
var_dump( $container->get('setting') );

実行結果は、以下のようになり設定値を取り出している。

1
2
3
4
array(1) {
'message' =>
string(12) "Hello, world"
}

コンテナにインスタンスを登録する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
require_once('vendor/autoload.php');

use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;

class SampleClass {
private $massage;
public function __construct(string $msg)
{
$this->message = $msg;
}
public function show()
{
echo $this->message, PHP_EOL;
}
}

$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
'setting' => [
'message' => 'Hello, world',
],
'test' => function(ContainerInterface $c) {
$obj = new SampleClass($c->get('setting')['message']);
return $obj;
}
]);

実行結果は、以下のようになる。

1
2
3
4
5
6
7
8
9
10
11
12
13
object(SampleClass)#23 (2) {
["massage":"SampleClass":private]=>
NULL
["message"]=>
string(12) "Hello, world"
}
object(SampleClass)#23 (2) {
["massage":"SampleClass":private]=>
NULL
["message"]=>
string(12) "Hello, world"
}
Hello, world

コンテナから2度インスタンスを取り出しているが、毎回同じ ID のインスタンスがであることから、毎回新しく生成しているのではなく、最初に生成したインスタンスを使いまわしていることがわかる。

DI

サンプルとして、インタフェース DeocoraterService を実装した AsteriskDecorater を使って修飾した文字列を表示する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
require_once(dirname(__FILE__).'/vendor/autoload.php');

use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;

interface DecoraterService {
public function decorate(string $message): string;
}

class AsteriskDecorater implements DecoraterService
{
public function decorate(string $message): string {
return "*{$message}*";
}
}

class ShowService {
private $decorater;
public function __construct(DecoraterService $decorater) {
$this->decorater = $decorater;
}
public function show(string $message) {
echo $this->decorater->decorate($message), PHP_EOL;
}
}

$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
'DecoraterService' => \DI\create('AsteriskDecorater'),
'ShowService' => function(DecoraterService $service) {
return new showService($service);
}
]);

$container = $containerBuilder->build();
$showService = $container->get('ShowService');
$showService->show('Hello, World');

重要なのはこの部分で、DecoraterService の実装として AsteriskDecorater を使用すると設定し、ShowService に DecoratorService を注入している。

1
2
3
4
5
6
$containerBuilder->addDefinitions([
'DecoraterService' => \DI\create('AsteriskDecorater'),
'ShowService' => function(DecoraterService $service) {
return new showService($service);
}
]);

修飾方法を変えるには、新しい実装を作成し、

1
2
3
4
5
6
class ExclamationDecorater implements DecoraterService
{
public function decorate(string $message): string {
return "!{$message}!";
}
}

注入の設定を変更する。

1
2
3
4
5
$containerBuilder->addDefinitions([
...
'DecoraterService' => \DI\create('ExclamationDecorater'),
...
]);

PHP 5.5 以降では、注入するクラス名は文字列と指定するほかに class キーワードをでクラス名を解決することができる。

1
2
3
4
5
$containerBuilder->addDefinitions([
...
DecoraterService::class => \DI\create('ExclamationDecorater'),
...
]);