本記事は CakePHP Advent Calendar 2019 の19日目のエントリーになります。
はじめに
先日、待ちに待ったCakePHP4.0.0が 正式リリース されましたね
そして、気が早いのですが4.0系がリリースされたということは、次の大きなアップデートは4.1系のリリースということになります。(本当に気が早くてごめんなさい)
そんなこともあり早速 4.1のロードマップ をチラ見してみたのですが Application の欄に以下のような記述があり、何やら面白そうな変化が起きそうだなと感じました。
- Experimental support for PSR11 compliant dependency injection container. Potential libraries that we could use are:
- phpleague/container - https://container.thephpleague.com/3.x
日本語に訳すと「PSR11準拠のDIコンテナを試験導入するかも。phpleague/container
というライブラリに可能性を感じてるやで。」といった感じでしょうか。
というわけで、本記事ではCake4.1に導入されるかもしれないDIコンテナライブラリである phpleague/container を試してみたいと思います。
免責事項
- DIやDIコンテナそのものの説明は行いません。
- インターネットや書籍に素晴らしい記事や文献があるのでそちらをご参照ください。
インストール
composerでサクッとインストール出来ます。ちなみに、2019年12月時点では3.3.0がインストールされます。
$ composer require league/container
クライアントとサービスクラスの作成
まずはクライアントクラス(オブジェクトが注入されるクラス)とサービスクラス(注入対象となるオブジェクトのクラス)を作成します。よくあるConstructor Injection
です。
<?php declare(strict_types=1);
namespace Basic;
class Client
{
/**
* @var Service
*/
public $service;
/**
* Constructor Injection
*
* @param Service $service
*/
public function __construct(Service $service)
{
$this->service = $service;
}
}
class Service
{
}
DIコンテナの作成
準備が整ったのでphpleague/container
を利用してDIコンテナを作成します。
// DI Container
require 'vendor/autoload.php';
use League\Container\Container;
$container = new Container;
$container->add(Service::class);
$container->add('client_alias', Client::class)->addArgument(Service::class);
$client1 = $container->get('client_alias');
$client2 = $container->get('client_alias');
var_dump($client1 instanceof Client); // true
var_dump($client1->service instanceof Service); // true
var_dump($client1 === $client2); //false
add()
メソッドを利用して登録を行い、get()
メソッドを利用してインスタンスを取得します。エイリアス名の設定なども簡単に出来ます。
インターフェイスの利用
実際にDIコンテナを利用する時は、インターフェイスを利用して具象クラスを隠蔽することが多いと思いますので、そのパターンも簡単に書いてみたいと思います。
<?php declare(strict_types=1);
namespace Advance;
class Client
{
/**
* @var ServiceInterface
*/
public $service;
/**
* Constructor Injection
*
* @param ServiceInterface $service
*/
public function __construct(ServiceInterface $service)
{
$this->service = $service;
}
}
interface ServiceInterface
{
}
class CakeService implements ServiceInterface
{
}
class LaravelService implements ServiceInterface
{
}
// DI Container
require 'vendor/autoload.php';
use League\Container\Container;
$container = new Container;
$container->add(ServiceInterface::class, CakeService::class);
$container->add(Client::class)->addArgument(ServiceInterface::class);
$client = $container->get(Client::class);
var_dump($client instanceof Client); // true
var_dump($client->service instanceof ServiceInterface); // true
var_dump($client->service instanceof CakeService); // true
var_dump($client->service instanceof LaravelService); // false
インターフェイスを利用した場合でも同じようにサクッとDIコンテナを作成することが出来ます。
まとめ
数行の記述で簡単に1DIコンテナが実現出来ました。今回は本当に試してみたレベルですが、もう少し深堀りして、CakePHPでこのライブラリがどのように使われるのか想像するのも面白そうです。2ちなみに ドキュメンテーションサイト がかっこよくてテンションが上りました
というわけで、2020年もCakePHPの動向から目が離せません!