メニュー

ドキュメント

S2AOPリファレンス

作成すべきファイル

 S2AOPの設定はS2Containerの設定ファイル(diconファイル)で行います。 設定ファイルの配置場所はとくに指定がありませんが、通常「Crosscutting Concern」と同じ場所に配置するか、 設定を行うコンポーネントと同じ場所に配置します。

設定ファイルの説明

aspectタグ(AOPを使用する場合は必須)

 アスペクトをコンポーネントに組み込みます。Interceptorの指定は、ボディでPHP式を使うか、 子タグでcomponentタグを使います。1つのコンポーネントに複数のアスペクトを組み込んだ場合はアスペクトの登録順に組み込まれ実行されます。 詳しい説明は独自実装のInterceptorを参照してください。

注意点
  • aspectタグで指定されたコンポーネントは、コンテナの初期化時にコンテナから取得されます。 そのため、aspectタグで指定されたコンポーネントのinstance属性がprototypeだったとしても、 Interceptorのメソッドが呼び出される度に新しいインスタンスが作成されるわけではありません。
  • Publicなメソッドにアスペクトを適用できます。
  • staticなメソッドにはアスペクトを適用できません。
  • pointcut属性を指定しない場合、実装しているインターフェースのすべてのメソッドが対象になります。すべてのメソッドを対象にするには、pointcut属性に".*"と指定します。
  • S2AOPでは実装上、メソッドを実装するクラス外部からの呼び出し時にのみアスペクトが適用されます。
    ( $this-> でメソッドを呼び出す場合はアスペクトが適用されません。) ( 1.1 系 )
  • finalなクラス、メソッドにはアスペクトを適用できません。( 1.2 系 )
  • メソッド名が、アンダーバーで始まる場合はアスペクトを適用できません。( 1.2 系 )
  • メソッド引数のデフォルト値に値を持つ配列が設定されている場合はアスペクトを適用できません。
    ( public function hoge($a = array('year' => 2007)); など) ( 1.2 系 )
pointcut属性(任意)

 カンマ区切りで対象となるメソッド名を指定することができます。pointcutを指定しない場合は、 コンポーネントが実装しているインターフェースのすべてのメソッドが対象になります。 メソッド名には正規表現も使えます。

設定例

 pointcut 属性を指定してDateのgetTime()メソッドを対象とする場合以下のようになります。

<?php
class Date {
    function Date() {}

    function getTime(){
        return '12:00:30';
    }

    function getDate(){
        return '25';
    }
}
?>
<component class="Date">
    <aspect pointcut="getTime">
        <component class="S2Container_TraceInterceptor"/>
    </aspect>
</component>
正規表現を使ってDateクラスのpublicなメソッドすべてを対象としたい場合は、以下のように設定します。
<component class="Date">
    <aspect pointcut=".*">
        <component class="S2Container_TraceInterceptor"/>
    </aspect>
</component>

interTypeタグ

 InterTypeをコンポーネントに組み込みます。InterTypeの指定は、ボディでPHP式を使うか、子タグでcomponentタグを使います。 ※PHP式については PHP式を参照してください。1つのコンポーネントに複数のInterTypeを組み込んだ場合は InterTypeの登録順に組み込まれます。詳しい説明は独自実装のInterTypeを参照してください。

設定例
<component class="Date">
    <interType>
        <component class="S2Container_PropertyInterType"/>
    </interType>
</component>

S2AOPで用意されているInterceptor

 S2AOPでは、以下のInterceptorを用意しています。また独自のInterceptorを簡単に作成できるようになっています。

TraceInterceptor

クラス名

 S2Container_TraceInterceptor

説明

 トレース処理を「Crosscutting Concern」として扱うためのInterceptorです。 DateクラスにTraceInterceptorを適用したdiconファイルは、以下のようになります。 対象とするメソッドはgetTime()とします。

<component class="Date">
    <aspect pointcut="getTime">
        <component class="S2Container_TraceInterceptor"/>
    </aspect>
</component>

詳しい使用方法はExample : TraceInterceptorを参照してください。

ThrowsInterceptor

クラス名

 S2Container_ThrowsInterceptor

説明

 例外処理を「Crosscutting Concern」として扱うためのInterceptorです。 使用するにはThrowsInterceptorを継承し、function handleThrowable(Exception, S2Container_MethodInvocation)を実装するだけです。 詳しい使用方法はExample : ThrowsInterceptorを参照してください。

MockInterceptor

クラス名

 S2Container_MockInterceptor

説明

 Mockを使ったテストを簡単に行うためのInterceptorです。 詳しい説明はテスト技法のモックを作成するための設定を参照してください。

DelegateInterceptor

クラス名

 S2Container_DelegateInterceptor

説明

 メソッド呼び出しを別のコンポーネントに委譲するためのInterceptorです。 使用方法はDelegateInterceptorのtargetプロパティに委譲したい相手を指定します。 委譲するときのメソッド名が異なる場合には、DeleateInterceptor#addMethodNameMap($methodName, $targetMethodName)で指定します。 例えば、bar()というメソッドをfoo->bar2()に委譲する場合、 DeleateInterceptor#setTarget(foo),DeleateInterceptor#addMethodNameMap ("bar", "bar2")のように指定します。詳しい使用方法はExample : DelegateInterceptorを参照してください。

注意

 targetプロパティに指定されたコンポーネントは、コンテナの初期化時にコンテナから取得されます。 このため、targetプロパティに指定されたコンポーネントのinstance属性がprototypeであっても、 常に同じインスタンスが使われます。メソッド呼び出しの度に新しいインスタンスをコンテナから取得したい場合は 次のPrototypeDelegateInterceptorを使用してください。

PrototypeDelegateInterceptor

クラス名

 PrototypeDelegateInterceptor

説明

 メソッド呼び出しを別のコンポーネントに委譲するためのInterceptorです。 メソッド呼び出しの度にコンポーネントをコンテナから取得します。使用方法は PrototypeDelegateInterceptorのtargetNameプロパティに委譲したい相手の名前を指定します。 委譲するときのメソッド名が異なる場合には、PrototypeDeleateInterceptor#addMethodNameMap($methodName, $targetMethodName)で指定します。 例えば、bar()というメソッドをfoo->bar2()に委譲する場合、 PrototypeDeleateInterceptor#setTarget(foo), PrototypeDeleateInterceptor#addMethodNameMap("bar","bar2")のように指定します。 詳しい使用方法はExample : PrototypeDelegateInterceptorを参照してください。

InterceptorChain

クラス名

 S2Container_InterceptorChain

説明

 複数のInterceptorをグルーピング化し再利用しやすくします。複数のInterceptorの組み合わせを 複数コンポーネントに適用する場合は、InterceptorChainで複数のInterceptorを1つにまとめて、 各コンポーネントにはInterceptorChainを指定するようにするといいでしょう。

<component name="interceptor1" .../>
<component name="interceptor2" .../>
<component name="interceptor3" .../>
<component name="chain" class="S2Container_InterceptorChain">
<initMethod name="add"><arg>interceptor1</arg></initMethod>
<initMethod name="add"><arg>interceptor2</arg></initMethod>
<initMethod name="add"><arg>interceptor3</arg></initMethod>
</component> <component ...> <aspect>chain</aspect> </component> <component ...> <aspect>chain</aspect> </component>

独自実装によるInterceptor

説明

 独自にInterceptorを作成する場合は、次のインターフェースまたは、抽象クラスを実装します。

MethodInterceptor
AbstractInterceptor

どちらの場合も実装するメソッドは、以下のinvoke()メソッドの1つだけです。

public function invoke(S2Container_MethodInvocation $methodInvocation)

AbstractInterceptorは、MethodInterceptorをimplementsした抽象クラスです。 AbstractInterceptorには、Proxyオブジェクトを取得するcreateProxy()メソッドと アスペクトを適用するクラスを取得するgetTargetClass()メソッドがあります。 アスペクトを適用したクラス名を必要とするInterceptor(例えば、ログ出力を行うInterceptor)を作成する場合は、 AbstractInterceptorを使用することで簡単にクラス名を取得することができます。

public function createProxy($proxyClass)
protected function getTargetClass(S2Container_MethodInvocation $methodInvocation)

MethodInvocationのgetThis()、getMethod()、getArguments()で対象となるオブジェクト、 メソッド、引数を取得できます。proceed()を呼び出すと実際のメソッドが呼び出され実行結果を取 得することができます。以下のような独自のInterceptorを作成したとします。

作成例
<?php
class SampleInterceptor implements S2Container_MethodInterceptor {
    public function invoke(S2Container_MethodInvocation $invocation){

        print "Before \n";             <-- 呼ぶ前は Before

        $ret = $invocation->proceed();

        print "After \n";              <-- 呼んだ後は After

        return $ret;
    }
}
?>

MethodInvocation#proceed()を呼ぶ前と後で2分され、呼ぶ前は Beforeの個所を実行し、 呼んだ後はAfterの個所を実行します。1つのコンポーネントに複数のアスペクトが定義されている場合は、 以下のよう実行されます。

  1. Aspectの登録順にMethodInterceptorのbefore部分が実行されます。
  2. 最後のMethodInterceptorのbefore部分を実行した後にコンポーネント自身のメソッドが呼び出されます。
  3. Aspectの登録の逆順にMethodInterceptorのafter部分が実行されます。

詳しい使用方法は独自実装によるInterceptorを参照してください。

S2AOPで用意されているInterType

 S2AOPでは、以下のInterTypeを用意しています。また独自のInterTypeを作成できるようになっています。

PropertyTraceInterType

クラス名

 S2Container_PropertyInterType

説明

 フィールドに対するsetter / getterメソッドを追加するInterTypeです。

注意点

 privateやprotectedのフィールドに対するgetter / setterはフィールドのデフォルト値を参照できません。
参照を伴うフィールドにはpublicにしてください。

<component class="Hoge">
    <interType>
        <component class="S2Container_PropertyInterType"/>
    </interType>
</component>

以下のクラスには、フィールドhogeに対してsetHoge/getHoge、フィールドfooに対してsetFoo/getFooが生成されます。

<?php
class Hoge {
    public $hoge;
    private $foo;
}
?>

SerializableInterType

クラス名

 S2Container_SerializableInterType

説明

 serialize/unserializeメソッドを生成します。
SPLのSerializableインタフェースを 実装していない場合は自動的に実装します。

<component class="Hoge">
    <interType>
        <component class="S2Container_SerializableInterType"/>
    </interType>
</component>

InterTypeChain

クラス名

 S2Container_InterTypeChain

説明

 複数のInterTypeをグルーピング化し、再利用しやすくします。 複数のInterTypeの組み合わせを複数コンポーネントに適用する場合は、 InterTypeChainで複数のInterTypeを1つにまとめて、各コンポーネントにはInterTypeChainを指定するようにするといいでしょう。

<component name="interType1" .../>
<component name="interType2" .../>
<component name="interType3" .../>
<component name="chain" class="S2Container_InterTypeChain">
    <initMethod name="add"><arg>interType1</arg></initMethod>
    <initMethod name="add"><arg>interType2</arg></initMethod>
    <initMethod name="add"><arg>interType3</arg></initMethod>
</component>

<component ...>
    <interType>chain</interType>
</component>
<component ...>
    <interType>chain</interType>
</component>

独自実装によるInterType

説明

 独自にInterTypeを作成する場合は、次のインターフェースまたは、抽象クラスを実装します。

S2Container_InterType
S2Container_AbstractInterType

S2Container_InterTypeを実装するクラスは、以下のメソッドを実装します。

public function introduce(ReflectionClass $targetClass, $enhancedClass)

targetClassはInterTypeが適用されるクラスです。enhancedClassはInterTypeを組み込むクラスで、 先に登録されているInterceptorやInterTypeが適用済みの場合もあります。

S2Container_InterTypeは以下の定数を提供します。

const PUBLIC_ = "public"
const PROTECTED_ = "protected"
const PRIVATE_ = "private"
const STATIC_ = "static"
const CONST_ = "const"

これらの定数は以下に説明するS2Container_AbstractInterTypeを使用する場合にアクセス修飾子で使用します。

S2Container_AbstractInterTypeは、S2Container_InterTypeをimplementsした抽象クラスです。S2Container_AbstractInterTyeのサブクラスは次のメソッドを実装します。

public function introduce(ReflectionClass $targetClass, $enhancedClassName)

S2Container_AbstractInterTypeのサブクラスは、次のメソッドで必要なオブジェクトを取得することができます。

protected function getTargetClass()
protected function getEnhancedClass()
protected function getEnhancedClassName()

S2Container_AbstractInterTypeは、フィールドやメソッド,実装するインタフェースを追加するためのユーティリティメソッドを提供します。 以下はその一部です。

protected function addInterface($className)
protected function addConstant($name, $value)
protected function addStaticProperty(array $modify, $name, $value = null)
protected function addProperty(array $modify, $name, $value = null)
protected function addStaticMethod(array $modify, $name, $src)
protected function addMethod(array $modify, $name, $src)

詳しい使用方法はwiki.s2php5.jpのintertypeを参照してください。

diconファイルを使用しないでアスペクトを組み込む方法

 diconファイルの設定を行わずプログラム上でアスペクトを組み込むこともできます。 作成方法は次のようになります。

  • PointcutImplのコンストラクタの引数で対象となるメソッド名を指定(複数可)します。 クラスを指定することで、そのクラスが実装しているインターフェースのメソッドを すべて自動的に適用させることもできます。
  • AspectImplのコンストラクタの第1引数にInterceptorを指定して、第2引数にPointcutImplで作成したPointcutを指定します。
  • AopProxyFactoryのcreateメソッドで、対象オブジェクト、クラス名、AspectImplで作成したAspectの配列を指定します。

DateクラスにTraceInterceptorをプログラム上で適用する場合は、次のようになります。対象となるメソッドはgetTime()とします。

<?php
require_once(dirname(__FILE__) . '/trace.inc.php');

$pointcut = new S2Container_PointcutImpl(array("getTime"));
$aspect = new S2Container_AspectImpl(new S2Container_TraceInterceptor(), $pointcut);
$proxy = S2Container_AopProxyFactory::create(new Date(),'Date', array($aspect));
$proxy->getTime();
?>

このサンプルは、s2container.php5/examples/aop/traceinterceptor以下に用意されています。


Example

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

TraceInterceptor

TraceInterceptorを使用してDateクラスのgetTime()メソッドが呼ばれた場合にトレースを出力させましょう。作成するファイルは以下のとおりです。

  • コンポーネントを定義するdiconファイル(Trace.dicon)
  • 設定が正しく行われているか確認する実行スクリプト(AopTraceClient.php)
diconファイルの作成
  • TraceInterceptorをコンポーネント定義します。name属性をtraceInterceptorとします。
  • Dateクラスのコンポーネントの定義します。pointcut属性にgetTime()メソッドを指定します。 aspectタグにtraceInterceptorを指定します。

 Trace.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="traceInterceptor" class="S2Container_TraceInterceptor"/>
    <component class="Date">
        <aspect pointcut="getTime">
            traceInterceptor
        </aspect>
    </component>
</components>
実行スクリプトの作成
  • S2Container#create()メソッドの最初の引数に作成したdiconファイル(Trace.dicon)のパスを指定してコンテナを作成します。
  • S2Container#getComponent()メソッドの第1引数にコンポーネントに登録したクラス名 (Date)を指定してコンポーネントを取得します。
  • トレースがAspectされるか確認するために取得したコンポーネントのDateのgetTime()メソッドを実行します。

 AopTraceClient.php

<?php
require_once(dirname(__FILE__) . '/trace.inc.php');

$PATH = EXAMPLE_DIR . "/aop/traceinterceptor/Trace.dicon";

$container = S2ContainerFactory::create($PATH);
$date = $container->getComponent('Date');
$date->getTime();
?>
実行結果

 メソッドが呼ばれる前と後でトレースが出力されているのが確認できます。

% php AopTraceClient.php
BEGIN Date#getTime()
END   Date#getTime() : 12:00:30
%

このサンプルは、s2container.php5/examples/aop/traceinterceptor以下に用意されています。


ThrowsInterceptor

 (1) ThrowsInterceptorを使って、例外が発生した場合でも処理を続けられるようにしましょう。 作成するファイルは以下のようになります。

  • Exceptionを発生させるクラス(Checker.class.php)
  • ThrowsInterceptorを継承して作成するInterceptor(HandleThrowableInterceptor.class.php)
  • コンポーネントの定義をするdiconファイル(Checker.dicon)
  • 設定が正しく行われているか確認する実行ファイル(AopCheckerClient.php)
例外を発生させるクラスの作成
  • run()メソッドの引数がnullでは無い場合は、引数の文字をコンソールに出力します。
  • 引数がnullの場合は、Exceptionを発生させます。

 Checker.class.php

<?php
class Checker {
    public function check($str) {
        if ($str != null) {
            print $str . "\n";
        } else {
            throw new Exception("null");
        }
    }
}
?>
ThrowsInterceptorを継承するInterceptorの作成
  • ThrowsInterceptorを継承します。
  • handleThrowable(Exception, S2Container_MethodInvocation)を実装します。

 HandleThrowableInterceptor.class.php

<?php
class HandleThrowableInterceptor extends S2Container_ThrowsInterceptor {
    public function handleThrowable(Exception $t,
                                    S2Container_MethodInvocation $invocation){
    }
}
?>
diconファイルの作成
  • 作成したInterceptorをコンポーネント定義します。name属性をhandleThrowableInterceptorとします。
  • 例外を発生させるCheckerクラスのcheck()メソッドに作成したHandleThrowableInterceptorをアスペクトします。

 Checker.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="handleThrowableInterceptor" class="HandleThrowableInterceptor"/>
    <component class="Checker">
        <aspect pointcut="check">
            handleThrowableInterceptor
        </aspect>
    </component>
</components>
実行ファイルの作成
  • S2Container#create()メソッドの第1引数に作成したdiconファイル(Checker.dicon)のパスを指定してコンテナを作成します。
  • S2Container#getComponent()メソッドの第1引数にコンポーネントに登録したクラス名(Checker)を指定して取得します。
  • Checker#check()メソッドの引数に"foo"の文字列を渡します。
  • Checker#check()メソッドの引数にnullを渡して例外を発生させます。
  • Checker#check()メソッドの引数に"hoge"の文字列を渡します。

 AopCheckerClient.php

<?php
require_once(dirname(__FILE__) . '/throws.inc.php');

$PATH = EXAMPLE_DIR . "/aop/throwsinterceptor/Checker.dicon";
$container = S2ContainerFactory::create($PATH);
$checker = $container->getComponent('Checker');
try{
    $checker->check("foo");
}catch(Exception $e){
    print "Exception : " . $e->getMessage() . "\n";
}

try{
    $checker->check(null);
}catch(Exception $e){
    print "Exception : " . $e->getMessage() . "\n";
}

try{
    $checker->check("hoge");
}catch(Exception $e){
    print "Exception : " . $e->getMessage() . "\n";
}
?>
実行結果

 例外で処理が止まっていないことが確認できます。

% php AopCheckerClient.php
foo
hoge
%

このサンプルは、s2container.php5/examples/aop/throwsinterceptor以下に用意されています。


(2) 例外を別の例外に変換するInterceptorを作成して変換したメッセージを表示させましょう。 作成するInterceptorは、先ほどThrowsInterceptorを継承して作成したクラス(HandleThrowableInterceptor.class.php) を応用して作成しましょう

例外を別の例外に変換するInterceptorの作成

 S2RuntimeExceptionを発生させてメッセージを変更します。

<?php
class HandleThrowableInterceptor extends S2Container_ThrowsInterceptor {
    public function handleThrowable(Exception $t,
                                    S2Container_MethodInvocation $invocation){
        throw new S2Container_S2RuntimeException("ESSR0007", array("arg"));
    }
}
?>

先ほど作成した実行ファイルを使って実行します。

実行結果

 エラーメッセージが変わっているのが確認できます。

% php AopCheckerClient.php
foo
Exception : arg should not be null or empty
hoge
%

このサンプルは、s2container.php5/examples/aop/throwsinterceptor以下に用意されています。


DelegateInterceptor

 S2AOPで用意されているDelegateInterceptorを使って、他のクラスのメソッドに委譲させましょう。 ここでは、インターフェースで抽象メソッドを作成して、抽象クラスと普通のクラスの両方にそのインターフェースを実装させます。 抽象クラスで実装したメソッドを普通のクラスで実装したメソッドに委譲させましょう。 作成するファイルは以下のとおりです。

  • インターフェース(IBase.class.php)
  • インターフェースを実装した抽象クラス(Dummy.class.php)
  • インターフェースを実装したクラス(Substance.class.php)
  • 委譲を設定するdiconファイル(Delegate.dicon)
  • 設定が正しく行われているか確認する実行ファイル(AopDelegateClient.class.php)
インターフェースの作成
  • 抽象メソッドを作成します。

 IBase.class.php

<?php
interface IBase {
    public function run();
}
?>
インターフェースを実装した抽象クラスの作成
  • 作成したインターフェースを実装します。

 Dummy.class.php

<?php
abstract class Dummy implements IBase {
}
?>
インターフェースを実装したクラスの作成
  • 作成したインターフェースを実装します。
  • 実装したインタフェースの抽象メソッドの実装します。

 Substance.class.php

<?php
class Substance implements IBase{
    public function run() {
        print __METHOD__ . " called.\n";
    }
}
?>
diconファイルの作成
  • 抽象クラス(Dummy)をコンポーネント定義します。
  • DelegateInterceptorのコンポーネントをaspectタグに定義します。 DelegateInterceptor#setTarget()を使って委譲したい相手を指定します。 委譲したいクラスはSubstanceクラスなのでメソッド・インジェクションを使ってセットします。

 Delegate.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component class="Dummy">
        <aspect>
            <component class="S2Container_DelegateInterceptor">
                <initMethod name="setTarget">
                    <arg>new Substance()</arg>
                </initMethod>
            </component>
        </aspect>
    </component>
</components>
実行ファイルの作成
  • S2Container#create()メソッドの最初の引数に作成したdiconファイル(Delegate.dicon)のパスを指定してコンテナを作成します。
  • S2Container#getComponent()メソッドの第1引数にコンポーネントに登録したクラス名(Dummy)を指定して取得します。
  • コンテナから取得したIBase#run()メソッドを実行します。

 AopDelegateCilent.php

<?php
require_once(dirname(__FILE__) . '/delegate.inc.php');
$PATH = EXAMPLE_DIR . "/aop/delegateinterceptor/Delegate.dicon";
$container = S2ContainerFactory::create($PATH);
$base = $container->getComponent('Dummy');
$base->run();
?>
実行結果

 コンソールに"Substance::run called."と表示されているのでDummy#run()がSubstance#run()に委譲されているのが確認できます。

% php AopDelegateCilent.php
Substance::run called.
%

このサンプルは、s2container.php5/examples/aop/delegateinterceptor以下に用意されています。


PrototypeDelegateInterceptor

 S2AOPで用意されているPrototypeDelegateInterceptorを使って、singletonのコンポーネントからprototypeのコンポーネントのメソッドに委譲させましょう。 ここでは、インターフェースで抽象メソッドを作成して、抽象クラス(singleton)と普通のクラス(prototype)の両方に そのインターフェースを実装させます。抽象クラスで実装したメソッドを普通のクラスで実装したメソッドに委譲させましょう。 作成するファイルは以下のとおりです。

  • インターフェース(IBase.class.php)
  • インターフェースを実装したsingletonの抽象クラス(Dummy.class.php)
  • インターフェースを実装したprototypeのクラス(Substance.class.php)
  • 委譲を設定するdiconファイル(Delegate.dicon)
  • 設定が正しく行われているか確認する実行ファイル(AopPrototypeDelegateClient.php)
インターフェースの作成
  • 抽象メソッドを作成します。

 IBase.class.php

<?php
interface IBase {
    public abstract function run();
}
?>
インターフェースを実装した抽象クラスの作成
  • 作成したインターフェースを実装します。

 Dummy.class.php

<?php
abstract class Dummy implements IBase {
}
?>
インターフェースを実装したクラスの作成
  • 作成したインターフェースを実装します。
  • 実装したインタフェースの抽象メソッドの実装します。

 Substance.class.php

<?php
class Substance implements IBase{
    private $sum = 0;

    public function run() {
        print "sum : " . $this->sum . "\n";
        $this->sum++;
    }
}
?>
diconファイルの作成
  • 抽象クラス(Dummy)をコンポーネント定義します。このコンポーネントはsingletonです。
  • PrototypeDelegateInterceptorのコンポーネントをaspectタグに定義します。PrototypeDelegateInterceptorのtargetNameプロパティに委譲したい相手の名前("target")を指定します。
  • 委譲したいクラス(Substance)をコンポーネント定義します。このコンポーネントのinstance属性はprototypeです。

 PrototypeDelegate.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component class="Dummy">
        <aspect>
            <component class="S2Container_PrototypeDelegateInterceptor">
                <property name="targetName">"target"</property>
            </component>
        </aspect>
    </component>

    <component name="target" class="Substance" instance="prototype"/>
</components>
実行ファイルの作成
  • S2Container#create()メソッドの最初の引数に作成したdiconファイル(PrototypeDelegate.dicon)のパスを指定してコンテナを作成します。
  • S2Container#getComponent()メソッドの第1引数にコンポーネントに登録したクラス名(Dummy)を指定して取得します。
  • コンテナから取得したDummy#run()メソッドを繰り返し実行します。

 AopPrototypeDelegateCilent.php

<?php
require_once(dirname(__FILE__) . '/prototype.inc.php');

$PATH = EXAMPLE_DIR . "/aop/prototypedelegateinterceptor/PrototypeDelegate.dicon";
$container = S2ContainerFactory::create($PATH);
$base = $container->getComponent('Dummy');
for ($i = 0; $i < 5; ++$i) {
   $base->run();
}
?>
実行結果

 コンソールに"sum : 0"と表示されているのでDummy#run()がSubstance#run()に委譲されているのが確認できます。 また、sumの値がすべて"0"となっていることから,run()メソッドを呼び出すたびに新しいSubstanceのインスタンスが作成されていることが分かります。

% php AopPrototypeDelegateCilent.php
sum : 0
sum : 0
sum : 0
sum : 0
sum : 0
%
委譲したいクラス(Substance)のコンポーネントのinstance属性をsingletonにすると、同一のSubstanceオブジェクトが使用されるためsumの値がインクリメントされます。
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    ・・・
    <component name="target" class="Substance" instance="singleton"/>
</components>
% php AopPrototypeDelegateCilent.php
sum : 0
sum : 1
sum : 2
sum : 3
sum : 4
%

このサンプルは、s2container.php5/examples/aop/delegateinterceptor以下に用意されています。


独自実装によるInterceptor

 クラス名、メソッド名、引数とメソッドの処理時間を計測してトレースするInterceptorを作成しましょう。また、そのInterceptorを使用して重い処理を行った時間をトレースさせましょう。作成するファイルは以下のとおりです。

  • クラス名、メソッド名、引数とメソッドの処理時間を計測して出力するInterceptor(MeasurementInterceptor.class.php)
  • 重い処理を行うクラス(HeavyProces.class.php)
  • コンポーネントの定義を行うdiconファイル(Measurement.dicon)
  • 設定が正しく行われているか確認する実行ファイル(AopMeasurementClient.php)
独自実装のIntercepterの作成
  • AbstractInterceptorクラスを実装します。
  • invoke(S2Container_MethodInvocation $invocation)メソッドを実装します。
  • getTargetClass($invocation)->getName()でクラス名を取得します。
  • $invocation->getMethod()->getName()でメソッド名を取得します。
  • $invocation->getArguments()で引数を取得します。
  • $invocation->proceed()で実際のメソッドが呼ばれるので、その前の時間を取得します。

 MeasurementInterceptor.class.php

<?php
class MeasurementInterceptor extends S2Container_AbstractInterceptor {
    public function invoke(S2Container_MethodInvocation $invocation){
        $start = 0;
        $end = 0;
        $buf = "";

        $buf = $this->getTargetClass($invocation)->getName();
        $buf .= "#";
        $buf .= $invocation->getMethod()->getName();
        $buf .= "(";
        $args = $invocation->getArguments();
        $buf .= implode($args) . ")";
        try {
            $start = $this->microtime_float();
            $ret = $invocation->proceed();
            $end = $this->microtime_float();
            $buf .= " : ";
            $t = $end - $start;
            print $buf . $t . "\n";
            return $ret;
        } catch (Exception $t) {
            $buf .= " Exception:";
            $buf .= $t->getMessage();
            throw $t;
        }
    }

    private function microtime_float() {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }
}
?>
重い処理を行うクラスの作成
  • 重い処理を行ったということにするために5秒間sleepします。

 HeavyProcess.class.php

<?php
class HeavyProcess {
    public function heavy(){
        sleep(5);
    }
}
?>
diconファイルの作成
  • 作成したMeasurementInterceptorをコンポーネント定義します。name属性をmeasurementとします。
  • HeavyProcessクラスのheavy()メソッドにMeasurementInterceptorをaspectします。

 Measurement.dicon

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components32.dtd">
<components>
    <component name="measurement" class="MeasurementInterceptor"/>
    <component class="HeavyProcess">
        <aspect pointcut="heavy">
               measurement
        </aspect>
    </component>
</components>
実行ファイルの作成
  • S2Container#create()メソッドの第1引数に作成したdiconファイル(Measurement.dicon)のパスを指定してコンテナを作成します。
  • S2Container#getComponent()メソッドの第1引数にコンポーネントに登録したクラス名(HeavyProcess)を指定して取得します。
  • コンテナから取得したHeavyProcess#heavy()メソッドを実行します。

 AopMeasurementClient.php

<?php
require_once(dirname(__FILE__) . '/original.inc.php');
$PATH = EXAMPLE_DIR . "/aop/originalinterceptor/Measurement.dicon";

$container = S2ContainerFactory::create($PATH);
$heavyProcess = $container->getComponent('HeavyProcess');
$heavyProcess->heavy();
?>
実行結果

 クラス名、メソッド名、引数とメソッドの処理時間がトレースされているのが確認できます。

% php AopMeasurementClient.php
HeavyProcess#heavy() : 5.0025010108948
%

このサンプルは、s2container.php5/examples/aop/originalinterceptor以下に用意されています。