Seasar DI Container with AOP

S2Containerリファレンス

作成すべきファイル

S2Containerを使用するためには、定義ファイルを作成する必要があります。定義ファイルは、コンポーネントを組み立てるための設計書のようなものです。定義ファイルの拡張子は .dicon、または .xml です。diconは「ダイコン」と読みます

S2Containerの定義

S2Containerの定義は、次のようになります。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="..." class="...">
            ...
    </component>
    <component name="..." class="...">
            ...
    </component>
</components>

DOCTYPEは省略できません。diconファイルを作成する場合は、上記のサンプルをコピー&ペーストしてください。ルートはcomponentsタグです。コンポーネントごとに、componentタグを定義していきます。componentタグのclass属性でコンポーネントのクラス名を指定します。name属性には、コンポーネント名を指定します。詳細は、S2Container定義タグリファレンスを参照してください。

<components>
    <component name="hoge" class="HogeImpl"/>
</components>

S2Containerの生成

S2Containerを作成する場合は、次のメソッドを使用します。

- S2ContainerFactory#create($path)

引数$pathは設定ファイルのパスです。
<?php
$PATH = "path/to/ccc.dicon";
...
$container = S2ContainerFactory::create($PATH);
?>

コンポーネントの取得

S2Containerからコンポーネントを取り出すには、次のメソッドを使用します。

- S2Container#getComponent($componentKey)

引数には、コンポーネントのクラスもしくはコンポーネント名を指定できます。詳しくは、componentタグを 参照してください。コンポーネントのクラスを指定する場合、コンポーネント instanceof クラスがtrueを返すクラスなら指定することができます。しかし、S2Containerの中に指定したクラスを実装しているコンポーネントが複数ある 場合、S2Containerは、どのコンポーネントを返せばよいのか判断できないため、 TooManyRegistrationRuntimeExceptionが発生します。実装コンポーネントがユニークに決まるクラスを指定してくださ い。コンポーネント名で取得することもできます。その場合も、同一の名前をもつコンポーネントが複数登録されている場合、 TooManyRegistrationRuntimeExceptionが発生します。

例) 以下のように Hoge クラスがコンポーネント名 hoge で設定されているとします。

<components>
    <component name="hoge" class="Hoge"/>
</components>

クラスを指定してコンポーネントを取得する場合

<?php
$container = S2ContainerFactory::create($PATH);
$hoge = $container->getComponent('Hoge');
?>

コンポーネント名を指定してコンポーネントを取得する場合

<?php
$container = S2ContainerFactory::create($PATH);
$hoge = $container->getComponent('hoge');
?>

Dependency Injectionのタイプ

Dependency Injectionには以下のタイプがあります。

- Constructor Injectionコンポーネントの構成に必要な値をコンストラクタで設定する
- Setter Injectionコンポーネントの構成に必要な値をセッター・メソッドで設定する
- Method Injectionコンポーネントの構成に必要な値を初期化メソッドで設定する

MethodInjectionはS2のオリジナルです。S2はすべてのタイプとそのハイブリッド型もサポートします。

コンストラクタ・インジェクション

コンストラクタ・インジェクションとは、任意のコンストラクタの引数値にDependency Injectionします。
S2Containerの定義ファイルには、次の内容を記述します。

  • コンポーネントの指定
    コンポーネントは、componentタグで組み立てます。class属性でクラス名を指定します。
    name属性でコンポーネントに名前を付けることもできます。
  • コンストラクタの引数の指定
    コンポーネントのコンストラクタの引数は、componentタグの子タグであるargタグを使って指定します。
    文字列の場合は、ダブルコーテーション(")で囲みます。
<components>
    <component name="..." class="...">
          <arg>...</arg>
    </component>
</components>
詳しい使用方法は、Exampleのコンストラクタ・インジェクションを参照してください。

セッター・インジェクション

セッター・インジェクションとは、任意のプロパティにセッターメソッドを使用してDependency Injectionします。
S2Containerの定義ファイルには、次の内容を記述します。

  • コンポーネントの指定
    コンポーネントの指定は、コンストラクタ・インジェクションと同様です。
    name属性でコンポーネントに名前を付けることもできます。
  • プロパティの指定
    コンポーネントのプロパティは、componentタグの子タグであるpropertyタグを使って指定します。
    name属性でプロパティ名を指定します。
<components>
    <component name="..." class="...">
          <property name="...">...</property>
    </component>
</components>
詳しい使用方法は、Exampleのセッター・インジェクションを参照してください。

メソッド・インジェクション

メソッド・インジェクションとは、任意のメソッドを呼び出して、Dependency Injectionします。
S2Containerの定義ファイルには、次の内容を記述します。

  • コンポーネントの指定
    コンポーネントの指定は、コンストラクタ・インジェクションと同様です。
    name属性でコンポーネントに名前を付けることもできます。
  • 初期化メソッドの指定
    initMethodタグを使って、コンポーネントの任意のメソッドを呼び出します。name属性で、メソッド名を指定します。引数は、argタグを子タグに使います。name属性を省略して、ボディで、PHP式を使うこともできます。その際、コンポーネント自身は$componentで表します。
<componets>
    <component name="..." class="...">
          <initMethod>...</initMethod>
    </component>
</components>
詳しい使用方法は、Exampleのメソッド・インジェクションを参照してください。

S2Container定義の分割とインクルード

す べてのコンポーネントを1つのファイルに記述すると、直ぐに肥大化してしまい管理が難しくなります。そのため、コンポーネントの定義を複数に分割する機能 と分割された定義をインクルードして1つにまとめる機能がS2Containerにあります。S2Container定義ファイルのインクルードは次のよ うにして行います。

<components>
    <include path="bar.dicon"/>
</components>

includeタグのpath属性で取り込みたいS2Container定義ファイルのパスを指定します。詳しくは、includeタグを参照してください。
コンポーネントの検索順は、先ず自分自身に登録されているコンポーネントを探して、見つからない場合は、includeされている順に子供のS2Containerに登録されているコンポーネントを検索し、最初に見つかったコンポーネントが返されます。
次のような場合は、Foo(自身のコンポーネント)→aaa(子供のS2Container)→bbb(子供のS2Container)の順に検索します。

<components>
    <include path="aaa.dicon"/>
    <include path="bbb.dicon"/>
    <component class="Foo" />
</components>

自動でコンストラクタ・インジェクションセッター・インジェクションを行う場合、S2Containerはインクルード先のコンポーネントを自動インジェクションすることができます。自動でDependency Injectionを行う場合の条件は自動バインディングを参照してください。
次のようにセッター・インジェクションでプロパティに指定するコンポーネントがインクルード先のaaa.diconとbbb.diconに登録されている場合、各HelloClientでは、どちらのコンポーネントが使用されるかをみてましょう。

root.dicon
<components>
    <include path="aaa.dicon"/>
    <include path="bbb.dicon"/>
    <component name="root" class="RootHelloClient"/>
</components>
aaa.dicon
<components>
    <component class="HelloImpl">
        <property name="message">"Hello Aaa!"</property>
    </component>

    <component name="aaa" class="AaaHelloClient"/>
</components>
bbb.dicon
<components>
    <component class="HelloImpl">
        <property name="message">"Hello Bbb!"</property>
    </component>

    <component name="bbb" class="BbbHelloClient"/>
</components>

各コンポーネントの内容は、次のようになります。

<?php
interface HelloClient {
    public function showMessage();
}
?>
<?php
class RootHelloClient implements HelloClient {

    private $hello_;

    public function setHello(Hello $hello) {
        $this->hello_ = $hello;
    }

    public function getHello() {
        return $this->hello_;
    }

    public function showMessage() {
        print $this->getHello()->getMessage();
    }
}
?>

AaaHelloClientとBbbHelloClientはRootHelloClient同様の実装です。

<?php
interface Hello {
    public function setMessage($helloMessage);
    public function getMessage();
}
?>
<?php
class HelloImpl implements Hello {
    private $helloMessage_;

    public function setMessage($helloMessage) {
        $this->helloMessage_ = $helloMessage;
    }

    public function getMessage() {
        return $this->helloMessage_;
    }
}
?>

HelloImplはMessageプロパティを定義しているだけです。各HelloClientのshowMessage()を呼び出した場合の実行結果は次のようになります。

RootHelloClientの実行結果
Hello Aaa!

まず、S2Containerはroot.diconにHelloImplが登録されているかを検索しま す。root.diconにはないので、次にインクルード先のaaa.diconを検索します。aaa.diconにはHelloImplが登録されてい るので、そのコンポーネントを使用します。

AaaHelloClientの実行結果
Hello Aaa!

AaaHelloClientは、aaa.diconに登録されているコンポーネントを使用します。自 動でインジェクションを行う場合、子供のS2Containerは親のS2Containerのコンポーネントを使用することはできません。例えば、 root.diconにHelloImplを登録していてもAaaHelloClientには自動インジェクションされないということです。

BbbHelloClientの実行結果
Hello Bbb!

AaaHelloClientと同様にBbbHelloClientもbbb.diconに登録されているコンポーネントを使用します。このサンプルは、s2container.php5/examples/dicon/include以下に用意されています。

名前空間

コンポーネントの定義を分割した場合に、複数のコンポーネント定義間で名前が衝突しないように、componentsタグのnamespace属性で名前空間を指定することができます。

foo.dicon
<components namespace="foo">
    <component name="aaa" .../>
    <component name="bbb" ...>
        <arg>aaa</arg>
    </component>
</components>
bar.dicon
<components namespace="bar">
    <include path="foo.dicon"/>
    <component name="aaa" .../>
    <component name="bbb" ...>
        <arg>aaa</arg>
    </component>
    <component name="ccc" ...>
        <arg>foo.aaa</arg>
    </component>
</components>
app.dicon
<components>
    <include path="bar.dicon"/>
</components>

同一のコンポーネント定義内では、名前空間なしで参照できます。他のS2Container定義のコ ンポーネントを参照する場合は、名前空間.をコンポーネント名の頭につけます。foo.aaaとbar.aaaは同じ名前がついていますが、名前空間が異 なっているので、違うコンポーネントとして認識されます。慣習として、定義ファイルの名前は、名前空間.diconにすることを推奨します。

インスタンス管理

S2Containerで、コンポーネントのインスタンスをどのように管理するのかを指定するのが、componentタグのinstance 属性です。デフォルトはsingletonで、これは S2Container#getComponent()によって返されるコンポーネントは常に同じ だという意味です。S2Container#getComponent()を呼び出すたびに、新たに作成されたコンポーネントを返して欲しい場合は、 instance属性にprototypeを指定します。リクエスト($_REQUEST)ごとにコンポーネントを管理したい場合は、 instance属性にrequestを指定します。セッション($_SESSION)ごとにコンポーネントを管理したい場合は、instance属 性にsessionを指定します。

プレゼンテーションのフレームワークと組み合わせるときに、プレゼンテー ションフレームワークが作成したインスタンスに対して、S2Containerで管理されているコンポーネントをセットしたい場合があります。そのような S2Container外のコンポーネントに対してDependency Injectionしたいときには、次のメソッドを使用します。

- S2Container#injectDependency($outerComponent, $componentKey)

第一引数には、外部のコンポーネントを指定します。第二引数には、外部コンポーネントのクラス、またはコンポーネント名を指定します。
そのとき、S2Container定義では、instance属性にouterを指定します。

instance属性 説明
singleton(default) S2Container#getComponent()を何度呼び出しても同じインスタンスが返されます。
prototype S2Container#getComponent()を呼び出すたびに新たなインスタンスが返されます。
request リクエスト毎に1つのインスタンスが作成されます。name属性に指定した名前で、コンポーネントがリクエストに格納されます。
session セッション毎に1つのインスタンスが作成されます。name属性に指定した名前で、コンポーネントがセッションに格納されます。
outer コンポーネントのインスタンスは、S2Container外で作成し、Dependency Injectionだけを行います。アスペクトコンストラクタ・インジェクションは適用できません。

ライフサイクル

initMethodやdestroyMethodでコンポーネントのライフサイクルもコンテナで管理することができます。S2Containerの開始時(S2Container#init())にinitMethodタグで指定したメソッドが呼び出され、S2Containerの終了時(S2Container#destroy())にdestroyMethodタグで 指定したメソッドが呼び出されるようになります。initMethodはコンポーネントがコンテナに登録した順番に実行され、destroyMethod はその逆順に呼び出されることになります。instance属性がsingleton以外の場合、destroyMethodを指定しても無視されます。HashMap#put()メソッドに初期化(aaaに111を設定)・終了処理(aaaにnullを設定)を設定する場合は、次のようになります。

<components namespace="bar">
    <component name="map" class="HashMap">
        <initMethod name="put">
            <arg>"aaa"</arg>
            <arg>111</arg>
        </initMethod>
        <destroyMethod name="put">
            <arg>"aaa"</arg>
            <arg>null</arg>
        </destroyMethod>
    </component>
</components>

自動バインディング

コンポーネント間の依存関係は、型がインターフェースの場合、コンテナによって自動的に解決されます。これがS2Containerのデフォルトですが、componentタグのautoBinding属性を指定することで細かく制御することもできます。

autoBinding 説明
auto(default) コンストラクタの引数が明示的に指定されている場合は、それに従います。
指定されていない場合、型がインターフェースの場合は自動的にバインドします。
型がインタフェースでない引数にはdefault値を使用します。default値が取得できない場合はnull値を使用します。
プロパティが明示的に指定されている場合はそれに従います。
明示的に指定されていないプロパティで、型がインターフェースの場合は自動的にバインドします。
constructor コンストラクタの引数が明示的に指定されている場合は、それに従います。
指定されていない場合、型がインターフェースの場合は自動的にバインドします。
型がインタフェースでない引数にはdefault値を使用します。default値が取得できない場合はnull値を使用します。
プロパティが明示的に指定されている場合は、それに従います。
property コンストラクタの引数が明示的に指定されている場合は、それに従います。
型がインターフェースのプロパティを自動的にバインドします。
none コンストラクタの引数が明示的に指定されている場合は、それに従います。
プロパティが明示的に指定されている場合はそれに従います。

詳しくは、自動バインディング(コンストラクタ・インジェクション)自動バインディング(セッター・インジェクション)を参照してください。

コンポーネントでS2Containerを利用する

コ ンポーネントはS2Containerに依存しないことが望ましいのですが、コンポーネントによっては、S2Containerのメソッドを呼び出したい 場合もあるでしょう。S2Container自身もcontainerという名前で登録されているので、arg、propertyタグのボディで containerを指定することで、コンテナのインスタンスを取得できます。また、S2Container型のsetterメソッドを定義しておいて自 動バインディングで設定することもできます。arg、propertyタグでcontainerを指定する場合は、次のようになります。

<components>
    <component class="BarImpl">
        <arg>container</arg>
    </component>

    <component class="FooImpl">
        <property name="foo">container</property>
    </component>
</components>

AOPの適用

コンポーネントにAOPを適用することもできます。例えば、DateクラスにTraceInterceptorを適用したい場合次のようにします。

<?php
class Date {
    function Date() {}
    
    function getTime(){
        return '12:00:30';
    }
}
?>
<components>
    <component name="traceInterceptor"
               class="S2Container_TraceInterceptor"/>
    <component class="Date">
        <aspect>traceInterceptor</aspect>
    </component>
</components>

aspectタグのボディでInterceptorの 名前を指定します。pointcut属性にカンマ区切りで対象となるメソッド名を指定することができます。pointcut属性を指定しない場合は、コン ポーネントが実装しているインターフェースのすべてのメソッドが対象になります。メソッド名には正規表現も使えます。この定義を使うサンプルは次のようになります。

<?php
$PATH ="Aop.dicon";
$container = S2ContainerFactory::create($PATH);
$date = $container->getComponent('Date');
$date->getTime();
?>

実行結果は次のようになります。

BEGIN Date#getTime()
END   Date#getTime() : 12:00:30

メタデータ

components、component、arg、propertyタグにメタデータを指定することもできます。metaタグはメタデータを指定したいタグの子タグに指定します。例えば、componentsタグにメタデータを指定したい場合次のようにします。

<components>
    <meta name="aaa">111</meta>
</components>

components、component、arg、propertyタグに指定したメタデータの情報は、S2Container、ComponentDef、ArgDef、PropertyDefで定義されている次のメソッドで取得することが出来ます。

  • public function getMetaDefSize()
  • public function getMetaDef($name)

S2Container定義タグリファレンス

DOCTYPE

DOCTYPEは、XML宣言の次に指定します。下記のように指定してください。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="hello" class="HelloConstructorInjection">
        <arg>"Hello World!"</arg>
    </component>
</components>

componentsタグ(必須)

ルートのタグになります。

namespace属性(任意)

名前空間を指定することができます。PHPの識別子として使えるものにします。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components namespace="hoge">
    ...
</components>

includeタグ(任意)

分割されたS2Containerの定義を取り込む場合に使います。

path属性(必須)

定義ファイルのパスを指定することができます。componentタグの前に記述する必要があります。定義ファイルへのパスは絶対パスになります。パスの先頭に%・・・%で囲む事で PHP で定義した定数を指定できます。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <include path="path/to/ccc.dicon" />
    <include path="%DICON_DIR%/ddd.dicon" />
</components>

componentタグ(任意)

コンポーネントを定義します。

class属性(任意)

クラス名を指定します。ボディでPHP式を使ってコンポーネントを指定した場合は、class属性を省略することができます。PHP式を使った場合にclass属性を指定すると、型チェックを行います。

name属性(任意)

名前を指定することもできます。PHPの識別子として使えるものにします。詳しくは、コンポーネントの取得を参照してください。

instance属性(任意)

S2Containerがどのようにコンポーネントのインスタンスを管理するのかを指定することができます。singleton(デフォルト)、prototype、outer、request、sessionを指定することができます。詳しくはインスタンス管理を参照してください。

autoBinding属性(任意)

S2Containerがコンポーネントの依存関係をどのように解決するのかを指定できます。auto(デフォルト)、constructor、property、noneを指定することができます。詳しくは、自動バインディングを参照してください。

argタグ(任意)

componentタグの子タグとして使った場合は、コンストラクタの引数になります。記述した順番でコンストラクタに渡されます。 initMethodタグdestroyMethodタグの子タグとして使った場合は、メソッドの引数になります。記述した順番でメソッドに渡されます。 引数として渡される実際の値は、ボディでPHP式を使うか、子タグでcomponentタグを使います。

propertyタグ(任意)

componentタグの子タグとして使います。プロパティとして設定される実際の値は、ボディでPHP式を使うか、子タグでcomponentタグを使います。

name属性(必須)

プロパティ名を指定します。

metaタグ(任意)

componentsタグcomponentタグargタグpropertyタグの子タグとして使います。メタデータの値は、ボディでPHPを使うか、子タグでcomponentタグを使います。

name属性(任意)

メタ名を指定します。

initMethodタグ(任意)

componentタグの子タグとして使います。引数は、子タグでargタグを使います。name属性を書かずにPHP式を使って、コンポーネントのメソッドを呼び出すこともできます。initMethodタグが定義されているコンポーネント自身を表す$componentがinitMethodタグ内だけで有効なオブジェクトとして使えます。

name属性(任意)

メソッド名を指定します。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component class="HashMap">
        <initMethod name="put">
            <arg>"aaa"</arg>
            <arg>111</arg>
        </initMethod>
        <initMethod>$component->put("aaa", 111);</initMethod>
        <initMethod>print "Hello";</initMethod>
    </component>
</components>

destroyMethodタグ(任意)

initMethodタグと同様です。

aspectタグ(任意)

アスペクトをコンポーネントに組み込みます。詳しくは、aspectタグの説明を参照してください。

descriptionタグ(任意)

componentsタグcomponentタグargタグpropertyタグの子タグとしてdescriptionタグを使うことができます。自由に説明を記述できます。


Example

以下のサンプルを実行する場合は、セットアップを行う必要があります。

コンストラクタ・インジェクション

コンストラクタ・インジェクションを使ってメッセージを表示しましょう。作成するファイルは以下のとおりです。

  • インターフェース(Hello.class.php)
  • 実装クラス(HelloConstructorInjection.class.php)
  • diconファイル(HelloConstructorInjection.dicon)
  • 実行スクリプト(HelloConstructorInjectionClient.php)
インターフェースの作成
  • showMessage()を定義します。

先 ず最初はインターフェースを考えます。インターフェースと実装を分離することで、コンポーネントの利用者は、インターフェースを知っていれば実装のことは 知らなくても済むようになります。また、テストの時には実装をモックに置き換えることで簡単にテストできるようになります。

<?php
interface Hello {
    public function showMessage();
}
?>
実装クラスの作成
  • 引数が1つのンストラクタを定義します。
  • showMessage()を実装します。

次はいよいよ実装です。コンストラクタでメッセージを受け取り、showMessage()で受け取ったメッセージを出力します。

<?php
class HelloConstructorInjection implements Hello {
    private $message;

    public function HelloConstructorInjection($message) {
        $this->message = $message;
    }

    public void showMessage() {
        print $this->message . "\n";
    }
}
?>
diconファイルの作成
  • componentタグでコンポーネントを定義します。
  • componentタグの子タグのargタグでコンストラクタの引数値を定義します。

メッセージをコンポーネントに設定するのは、S2Containerの仕事です。定義ファイルに基づいてコンポーネントを組み立てます。

s2container.php5/examples/dicon/injection/HelloConstructorInjection.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="hello" class="HelloConstructorInjection">
        <arg>"Hello World!"</arg>
    </component>
</components>
実行スクリプトの作成

s2container.php5/examples/dicon/injection/HelloConstructorInjectionClient.php

<?php
require_once(dirname(dirname(dirname(__FILE__))) . '/example.inc.php');
require_once('Hello.class.php');
require_once('HelloConstructorInjection.class.php');

$PATH =	EXAMPLE_DIR . "/dicon/injection/HelloConstructorInjection.dicon";
		
$container = S2ContainerFactory::create($PATH);
$hello = $container->getComponent('Hello');
$hello->showMessage();
		
$hello2 = $container->getComponent("hello");
$hello2->showMessage();
?>
実行結果

argタグで指定した文字列が正しく表示されていることが確認できます。

% php HelloConstructorInjectionClient.php
Hello World!
Hello World!
%

この演習は、s2container.php5/examples/dicon/injection以下に用意されています。

セッター・インジェクション

セッター・インジェクションを使ってメッセージを表示しましょう。作成するファイルは以下のとおりです。

  • インターフェース(Hello.class.php)
  • 実装クラス(HelloSetterInjection.class.php)
  • diconファイル(HelloSetterInjection.dicon)
  • 実行スクリプト(HelloSetterInjectionClient.php)
インターフェースの作成
  • showMessage()を定義します。

イ ンターフェースはコンストラクタ・インジェクションの場合と同じです。プロパティに対するゲッター・メソッド、セッター・メソッドを定義する必要はありま せん。なぜなら、Dependency Injectionするのにコンストラクタを使うのかセッター・メソッドを使うのかは実装の問題だからです。

<?php
interface Hello {
    public void showMessage();
}
?>
実装クラスの作成
  • セッター・メソッド(setMessage)を定義します。
  • showMessage()を実装します。

次は実装です。セッター・メソッドでメッセージを受け取り、showMessage()で受け取ったメッセージを出力します。

<?php
class HelloSetterInjection implements Hello {
    private $message;

    public function HelloSetterInjection() {
    }

    public function setMessage($message) {
        $this->message = $message;
    }

    public function showMessage() {
        print $this->message . "\n";
    }
}
?>
diconファイルの作成
  • componentタグでコンポーネントを定義します。
  • componentタグの子タグのpropertyタグでコンポーネントのプロパティ値を定義します。

s2container.php5/examples/dicon/injection/HelloSetterInjection.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component class="HelloSetterInjection">
        <property name="message">"Hello World!"</property>
    </component>
</components>
実行スクリプトの作成
  • S2ContainerFactory#create($path)を呼び出してS2Containerを作成します。
  • getComponent()を使用して、S2Containerからコンポーネントを取り出します。
  • 取得したコンポーネントのメソッドを呼び出します。

s2container.php5/examples/dicon/injection/HelloSetterInjectionClient.php

<?php
require_once(dirname(dirname(dirname(__FILE__))) . '/example.inc.php');
require_once('Hello.class.php');
require_once('HelloSetterInjection.class.php');

$PATH = EXAMPLE_DIR . "/dicon/injection/HelloSetterInjection.dicon";
		
$container = S2ContainerFactory::create($PATH);
$hello = $container->getComponent('Hello');
$hello->showMessage();
?>
実行結果

propertyタグで指定した文字列が正しく表示されていることが確認できます。

% php HelloSetterInjectionClient.php
Hello World!
%

この演習は、s2container.php5/examples/dicon/injection以下に用意されています。

メソッド・インジェクション

メソッド・インジェクションを使ってメッセージを表示しましょう。作成するファイルは以下のとおりです。

  • インターフェース(Hello.class.php)
  • 実装クラス(HelloMethodInjection.class.php)
  • diconファイル(HelloMethodInjection.dicon)
  • 実行スクリプト(HelloMethodInjectionClient.php)
インターフェースの作成
  • showMessage()を定義します。

追加のメソッドを複数回呼び出すようなケースが代表的な使い方ですが、今回の演習では、インターフェースはコンストラクタ・インジェクションの場合と同じにしました。

<?php
interface Hello {
    public function showMessage();
}
?>
実装クラスの作成
  • addMessage()を定義します。
  • showMessage()を実装します。

次は実装です。addMessage($message)でメッセージを複数回追加して、showMessage()で受け取ったメッセージを出力します。

<?php
class HelloMethodInjection implements Hello {

    private $buf = "";
    
    public function HelloMethodInjection() {}
    
    public function addMessage($message) {
        $this->buf .= $message;
    }

    public function showMessage() {
        print $this->buf . "\n";
    }
}
?>
diconファイルの作成
  • componentタグでコンポーネントを定義します。
  • componentタグの子タグのinitMethodタグでコンポーネントのメソッドの引数値を定義します。

先ずは、argタグを使ってaddMessage($message)に"Hello "を指定します。
次にPHP式を使ってaddMessage($message)に"World!"を指定します。

s2container.php5/examples/dicon/injection/HelloMethodInjection.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component class="HelloMethodInjection">
        <initMethod name="addMessage">
            <arg>"Hello "</arg>
        </initMethod>
        <initMethod>$component->addMessage("World!")</initMethod>
    </component>
</components>
実行スクリプトの作成
  • S2ContainerFactory#create($path)を呼び出してS2Containerを作成します。
  • getComponent()を使用して、S2Containerからコンポーネントを取り出します。
  • 取得したコンポーネントのメソッドを呼び出します。
<?php
require_once(dirname(dirname(dirname(__FILE__))) . '/example.inc.php');
require_once('Hello.class.php');
require_once('HelloMethodInjection.class.php');

$PATH = EXAMPLE_DIR . "/dicon/injection/HelloMethodInjection.dicon";
		
$container = S2ContainerFactory::create($PATH);
$hello = $container->getComponent('Hello');
$hello->showMessage();
?>
実行結果

initMethodタグで指定した文字列が正しく表示されていることが確認できます。

% php HelloMethodInjectionClient.php
Hello World!
%

この演習は、s2container.php5/examples/dicon/injection/以下に用意されています。

自動バインディング(コンストラクタ・インジェクション)

自動バインディングでメッセージを表示してみましょう。作成するファイルは以下のとおりです。

  • インターフェース(Hello.class.php)
  • インターフェースの実装クラス(AutoHelloConstructorInjection.class.php)
  • Mapインターフェース(Map.class.php)
  • Mapインターフェースの実装クラス(HashMap.class.php)
  • diconファイル(AutoHelloConstructorInjection.dicon)
  • 実行スクリプト(AutoHelloConstructorInjectionClient.php)
インターフェースの作成
  • showMessage()を定義します。
<?php
interface Hello {
    public function showMessage();
}
?>
実装クラスの作成
  • 引数がMapのコンストラクタを定義します。
  • showMessage()を実装します。

コンストラクタでMapを受け取り、showMessage()でhelloをキーとしてMapから値を取得して出力します。

<?php
class AutoHelloConstructorInjection implements Hello {

    private $messages;
    
    public function AutoHelloConstructorInjection(Map $messages) {
        $this->messages = $messages;
    }
    
    public function showMessage() {
        print $this->messages->get('hello') . "\n";
    }
}
?>
Mapインターフェースの作成
  • put($key,$val)を定義します。
  • get($key)を定義します。
<?php
interface Map {
    function put($key,$val);
    function get($key);
}
?>
Mapインターフェースの実装クラスの作成
  • 引数がMapのコンストラクタを定義します。
  • showMessage()を実装します。
<?php
class HashMap implements Map{
    private $pool = array();
    
    function HashMap() {}
    
    function put($key,$val){
        $this->pool[$key] = $val;
    }
    
    function get($key){
        return array_key_exists($key,$this->pool) ? $this->pool[$key] : null;
    }
}
?>
diconファイルの作成
  • componentタグでMapの実装クラスであるHashMapをコンポーネント定義します。
  • componentタグの子タグであるinitMethodタグでHashMapにキーとして"hello"、値として"Hello World!"を定義します。
  • componentタグでAutoHelloConstructorInjectionをコンポーネント定義します。autoBinding属性にautoを指定します。
    演習であるため auto を明示的に指定していますが、autoBinding属性のデフォルト値であるため本来は省略します。

AutoHelloConstructorInjection には、argタグが定義されていないことに注目してください。コンストラクタ・インジェクションを行う場合、argタグを定義する必要がありますが、 S2Container内にMapの実装クラスが登録されていれば、S2Containerがコンテナ内を検索して自動的に引数を設定します。ただし、引 数の型がインターフェースでない場合、自動バインディングはできません。

s2container.php5/examples/dicon/autobinding/AutoHelloConstructorInjection.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component class="HashMap">
        <initMethod name="put">
            <arg>"hello"</arg>
            <arg>"Hello World!"</arg>
        </initMethod>
    </component>

    <component autoBinding="auto"
               class="AutoHelloConstructorInjection"/>
</components>
実行スクリプトの作成
<?php
require_once(dirname(dirname(dirname(__FILE__))) . '/example.inc.php');
require_once('Hello.class.php');
require_once('Map.class.php');
require_once('HashMap.class.php');
require_once('AutoHelloConstructorInjection.class.php');

$PATH =	EXAMPLE_DIR . "/dicon/autobinding/AutoHelloConstructorInjection.dicon";
		
$container = S2ContainerFactory::create($PATH);
$hello = $container->getComponent('Hello');
$hello->showMessage();
?>
実行結果

HashMapの値が表示されていることから、自動的にコンストラクタの引数を設定していることが確認できます。

% php AutoHelloConstructorInjection.php
Hello World!
%

この演習は、s2container.php5/examples/dicon/autobinding以下に用意されています。

自動バインディング(セッター・インジェクション)

自動バインディングでメッセージを表示してみましょう。作成するファイルは以下のとおりです。

  • インターフェース(Hello.class.php)
  • インターフェースの実装クラス(AutoHelloSetterInjection.class.php)
  • diconファイル(AutoHelloSetterInjection.dicon)
  • 実行スクリプト(AutoHelloSetterInjectionClient.php)
インターフェースの作成
  • showMessage()を定義します。
<?php
interface Hello {
    public function showMessage();
}
?>
実装クラスの作成
  • 引数がMapのshowMessage()を実装します。

setMessage(Map map)でMapを受け取り、showMessage()でhelloをキーとしてMapから値を取得して出力します。

<?php
class AutoHelloSetterInjection implements Hello {

    private $messages;
    
    public function AutoHelloSetterInjection() {}
    
    public function setMessage(Map $messages) {
        $this->messages = $messages;
    }

    public function showMessage() {
        print $this->messages->get('hello') . "\n";
    }
}
?>
diconファイルの作成
  • componentタグでMapの実装クラスであるHashMapをコンポーネント定義します。
  • componentタグの子タグであるinitMethodタグでHashMapにキーとして"hello"、値として"Hello World!"を定義します。
  • componentタグでAutoHelloSetterInjectionをコンポーネント定義します。autoBinding属性にautoを指定します。
    演習であるため auto を明示的に指定していますが、autoBinding属性のデフォルト値であるため本来は省略します。

AutoHelloSetterInjection には、propertyタグが定義されていないことに注目してください。セッター・インジェクションを行う場合、propertyタグで任意のプロパティ を定義する必要がありますが、S2Container内にMapの実装クラスが登録されていれば、S2Containerがコンテナ内を検索して自動的に プロパティを設定します。ただし、プロパティの型がインターフェースでない場合、自動バインディングはできません。

s2container.php5/examples/dicon/autobinding/AutoHelloSetterInjection.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component class="HashMap">
        <initMethod name="put">
            <arg>"hello"</arg>
            <arg>"Hello World!"</arg>
        </initMethod>
    </component>

    <component autoBinding="auto"
               class="AutoHelloSetterInjection"/>
</components>
実行スクリプトの作成
<?php
require_once(dirname(dirname(dirname(__FILE__))) . '/example.inc.php');
require_once('Hello.class.php');
require_once('Map.class.php');
require_once('HashMap.class.php');
require_once('AutoHelloSetterInjection.class.php');

$PATH = EXAMPLE_DIR . "/dicon/autobinding/AutoHelloSetterInjection.dicon";
		
$container = S2ContainerFactory::create($PATH);
$hello = $container->getComponent('Hello');
$hello->showMessage();
?>
実行結果

HashMapの値が表示されていることから、自動的にプロパティを設定していることが確認できます。

% php AutoHelloSetterInjection.php
Hello World!
%

この演習は、s2container.php5/examples/dicon/autobinding以下に用意されています。