PHPのマイクロフレームワークであるSlim3 Frameworkでは、PimpleパッケージベースにしたDIコンテナが用意されています。
今回はSlim3で、DIコンテナからインスタンス取得時に、呼び元からコンストラクタ引数を渡す方法を説明します。
通常のDIコンテナ使用パターン
本題に入る前に、まずは、パラメータを渡さない通常のDIコンテナの使用法を確認します。
最初に、DIコンテナで管理されるクラスを用意します。こちらはtest()メソッドがあるだけの普通のクラスです。
<?php
class MyService
{
public function test($name="")
{
echo "hello {$name}." . PHP_EOL;
}
}
次に、先ほどのMyServiceクラスをDIコンテナに登録しています。
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../vendor/autoload.php';
$app = new \Slim\App;
// インスタンスを生成して返す
$container = $app->getContainer();
$container['myService'] = function ($container) {
$myService = new MyService();
return $myService;
};
DIコンテナのインスタンスは\Slim\App
クラスのgetContainer()メソッドで取得できます。
取得したコンテナオブジェクトに対して、配列のようなイメージで関数をセットしてあげます。セットした関数では、DIコンテナで管理したいオブジェクトを準備してreturnする必要があります。
DIコンテナにオブジェクト(厳密にはオブジェクト生成する関数)を登録したら、次は利用側です。
$app->get('/test/{name}', function (Request $request, Response $response) {
$name = $request->getAttribute('name');
// DIコンテナから、オブジェクトを取得
$myService = $this->myService;
$myService->test($name);
});
$app->run();
$app->get()などのコールバックハンドラ内では$this->myServiceのような形式でDIコンテナからオブジェクトを取得できるようになっています。これは$this->myServiceの行が実行されたタイミングで、先ほどDIコンテナへ登録した関数が走り、結果としてオブジェクトが取得できるという仕組みになっています。
これが、コンストラクタ引数なしで、DIコンテナからオブジェクトを生成する方法です。
コンストラクタ引数を渡すパターン
次は、コンストラクタ引数を渡してインスタンス生成するパターンです。
こちらも、まずはDIコンテナで管理されるクラスの定義を行います。先ほどと異なりコンストラクタ関数__construct()が定義されていますが、こちらも何の変哲もない普通のクラス定義です。
<?php
class MyService2
{
private $prefix;
private $suffix;
public function __construct($prefix = null, $suffix = null) {
$this->prefix = $prefix ?? "Good morning";
$this->suffix = $suffix ?? "have a nice day.";
}
public function test($name="")
{
echo "{$this->prefix} {$name}. {$this->suffix}" . PHP_EOL;
}
}
次はDIコンテナへの登録です。ここは、最初の例とは違っているので注意が必要です。
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../vendor/autoload.php';
$app = new \Slim\App;
// インスタンスを生成する関数を返す
$container = $app->getContainer();
$container['myService2'] = function ($container) {
return function($prefix = null, $suffix = null) {
$myService2 = new MyService2($prefix, $suffix);
return $myService2;
};
};
先ほどはreturnでインスタンスを返していましたが、今回は関数を返しています。
関数内ではインスタンス生成しているので、ここで返す関数は"インスタンス生成する関数"をリターンしていることになります。この、"インスタンス生成する関数"には省略可能な引数が2つあり、受け取った引数をMyService2クラスのコンストラクタ引数に渡しています。
最後にDIコンテナに登録されたオブジェクトの利用です。
$app->get('/test2/{name}', function (Request $request, Response $response) {
$name = $request->getAttribute('name');
// パラメータを指定してインスタンスを取得
$myService2 = ($this->myService2)("Good Evening", "bye.");
$myService2->test($name);
});
$app->run();
最初の例では、$this->myServiceでインスタンス自体が取得できていましたが、今回の$this->myService2では、インスタンス"を生成する関数"が取得できます。少しややこしいですが$this->myService2を関数だと思ってみるとわかりやすいです。$this->myService2は関数なので、さらにその後に("Good Evening", "bye.")をつけることで、関数呼び出しが行えます。
呼び出された関数の戻り値はMyService2クラスのインスタンスなので、後は$myService2->test()のような形で、利用することができます。
また、先ほど作成した無名関数は引数を省略可能にしていたので、($this->myService2)()
のように、引数無しでインスタンス生成を要求することもできます。
``` language-php
$app->get('/test2/{name}', function (Request $request, Response $response) {
$name = $request->getAttribute('name');
// パラメータなしでインスタンスを取得
$myService3 = ($this->myService2)();
$myService3->test($name);
});