今日も適当ダイアリー

PHP や Javascript や Symfony、BEAR.Sunday などのWeb周りのことを中心に。それ以外のことも気まぐれに投稿します。

PHP5.4+フレームワーク BEAR.Sundayを理解するためにRay.Diを触ってみるの巻 其の弐

こんにちは。夏はやっぱり生ビール、@madapajaです。
PHP5.4+フレームワーク BEAR.Sundayを理解するためにRay.Diを触ってみるの巻 其の壱 : 今日も適当ダイアリー の続きです。

PHP5.4+フレームワーク BEAR.Sundayを理解するためにRay.Diを触ってみるの巻シリーズ

前回は、Ray を Composer 経由でインストールして User クラスを作りました。

Ray を使わずに書いてみる

Ray を使ってみる前に、普通に PHP を書いて User クラスを実行してみたいと思います。今回は Madapaja\Ray\Di\Sample01\App クラスを下記のように書きました。

src/Madapaja/Ray/Di/Sample01/App.php


namespace Madapaja\Ray\Di\Sample01;
use Madapaja\Ray\Di\Sample01\Model\User;

class App
{
public function run()
{
$pdo = new \PDO('sqlite::memory:', null, null);
$user = new User($pdo);

$user->init();

$user->createUser('Koriym', mt_rand(18,35));
$user->createUser('Bear', mt_rand(18,35));
$user->createUser('Yoshi', mt_rand(18,35));

$users = $user->readUsers();
print_r($users);
}
}

そして上の App クラスを実行するために下記のファイルを作ります。内容はオートローダを読み込んで App::run() を実行するだけです。

main.php


require __DIR__.'/vendor/autoload.php';

use Madapaja\Ray\Di\Sample01\App;
(new App())->run();

ここまで出来たら php main.php として実行してみます。

Array
(
[0] => Array
(
[Name] => Koriym
[Age] => 24
)

[1] => Array
(
[Name] => Bear
[Age] => 18
)

[2] => Array
(
[Name] => Yoshi
[Age] => 30
)

)

のように、結果が表示されたでしょうか?簡単ですね。というか、普通のPHPなので当たり前ですね。

Ray.Di でインジェクトしてみよう

やっと、Rayの出番です。Ray.Diを使って依存性を注入してみます。今回の場合は、 User クラスへ PDO を注入します。

Ray では DI や AOP の設定をモジュールという単位(クラス)で行います。モジュールはアプリケーションやフレームワークを構成する際に使われる知識を持っており、その知識をもとにインジェクターが依存を注入することで、適切なオブジェクトグラフを生成されることになります。
とりあえず、モジュールで DI と AOP の設定をするよ、ということが分かればOKです。

アノテーションの追加

モジュールを作る前に、User クラスのコンストラクタへインジェクトを行うことを示すために、アノテーションを追加します。

src/Madapaja/Ray/Di/Sample01/Model/User.php


namespace Madapaja\Ray\Di\Sample01\Model;

use Ray\Di\Di\Inject;
use Ray\Di\Di\Named;

class User
{
private $db;

/**
* @Inject
* @Named("pdo=pdo_user")
*/
public function __construct(\PDO $pdo)
{
$this->db = $pdo;
}

// ...

13行目で @Inject アノテーションによってインジェクトポイントだということを示し、14行目で @Named アノテーションで pdo_user という名前で(pdo を)バインドすることを示します。
なお、5~6行目は @Inject@Named アノテーションの名前空間を解決させるために use しています。

UserModule を作る

ということで、User クラスの構成設定を行う UserModule を作ります。 モジュールは Ray\Di\AbstractModule を継承し configure() メソッド内で構成の設定を行います。

src/Madapaja/Ray/Di/Sample01/Module/UserModule.php


namespace Madapaja\Ray\Di\Sample01\Module;

use Ray\Di\AbstractModule;

class UserModule extends AbstractModule
{
protected function configure()
{
// bind dependency @Inject @Named("pdo_user")
$pdo = new \PDO('sqlite::memory:', null, null);
$this->bind('PDO')->annotatedWith('pdo_user')->toInstance($pdo);
}
}

DI の設定は 13 行目で行っており、PDO クラスの pdo_user という名前の箇所に PDO インスタンス($pdo)をバインドしています。 (本来はインターフェースではない PDO クラスや、その実体(インスタンス)を使用しない方がいいのですが、ここではコードを簡単にするために許してもらいます。)

インジェクタ経由でインスタンスを取得する

これで準備が整いました。 App クラスを書き変えてインジェクタを利用してみます。

src/Madapaja/Ray/Di/Sample01/App.php


namespace Madapaja\Ray\Di\Sample01;

use Ray\Di\Injector;
use Madapaja\Ray\Di\Sample01\Module\UserModule;

class App
{
public function run()
{
$di = Injector::create([new UserModule]);
$user = $di->getInstance('Madapaja\Ray\Di\Sample01\Model\User');

$user->init();

// ...

12行目で、今作った UserModule を指定してインジェクタを生成し、13行目で実際に User クラスのインスタンスを取得しています。 これで、依存がよしなに解決されるようになりました。

アノテーションのオートロードを設定

さっそく実行してみたいところですが、アノテーションのオートロードのためにもうひとつ処理を追加する必要があります。 これは BEAR.Sunday ではフレームワーク側でやってくれますので、今回のように Ray をスタンドアロンで利用する場合にのみ必要な設定です。

main.php でオートローダーを取得して、アノテーションレジストリにローダーを登録します。 実際のソースは下記のようになります。

main.php


use Doctrine\Common\Annotations\AnnotationRegistry;
use Madapaja\Ray\Di\Sample01\App;

$loader = require __DIR__.'/vendor/autoload.php';
AnnotationRegistry::registerLoader(array($loader, "loadClass"));

(new App())->run();

これでやっと実行できるようになりました。php main.php を実行させて問題なく動くのを確認してみましょう。

@PostConstruct アノテーションによる初期化メソッドの定義

これまでUser クラスを初期化するために、 App クラスで User::init() メソッドを呼び出していました。 これを @PostConstruct アノテーションすることで、インジェクタに初期化メソッドの呼び出しを任せようと思います。

src/Madapaja/Ray/Di/Sample01/App.php


// ...

class App
{
public function run()
{
$di = Injector::create([new UserModule]);
$user = $di->getInstance('Madapaja\Ray\Di\Sample01\Model\User');

// $user->init();

// ...

App.phpの修正は、12行目を削除するだけです。
次に User クラスにアノテーションを追加します。

src/Madapaja/Ray/Di/Sample01/Model/User.php


namespace Madapaja\Ray\Di\Sample01\Model;

use Ray\Di\Di\Inject;
use Ray\Di\Di\Named;
use Ray\Di\Di\PostConstruct;

class User
{

// ...

/**
* @PostConstruct
*/
public function init()
{
return $this->db->query('CREATE TABLE User (Id INTEGER PRIMARY KEY, Name TEXT, Age INTEGER)');
}

// ...

名前空間を use して、@PostConstruct アノテーションを追加するだけです。 これだけで、インジェクタの getInstance メソッドでインスタンスを取得する際、コンストラクタ後にアノテーションしたメソッドが呼び出されるようになります。

いよいよ Ray.Aop の登板!

という所なのですが、またもや長くなってきた(というか疲れた)ので、其の参に続きます!にんにん!

(7/12追記)続き書きました!

参考リンク

PHP5.4+フレームワーク BEAR.Sundayを理解するためにRay.Diを触ってみるの巻シリーズ