4.5. Interceptors prepared in S2AOP

4.5.1. TraceInterceptor

Using S2ApplicationContext. 

TraceInterceptor is Interceptor to handle trace processing as "Crosscutting Concern".
As an example, an aspect is applied to the following service class.

class Service {
    public function add($a, $b) {
        print __CLASS__ . ' called.' . PHP_EOL;
        return $a + $b;
    }
}

Let's create following execution script.

  • Service class is imported by the S2ApplicationContext::import method
  • TraceInterceptor is aspected to a service component by the S2ApplicationContext::registerAspect method
  • A container is generated by the S2ApplicationContext::create method
  • Service component is taken out by the S2Container::getComponent method.
  • The add method of the Service component is called.
<?php
require_once('S2Container/S2Container.php');
seasar\container\S2ApplicationContext::import(dirname(__FILE__) . '/classes');
seasar\container\S2ApplicationContext::registerAspect('new seasar\aop\interceptor\TraceInterceptor','/^Service$/');
$container = seasar\container\S2ApplicationContext::create();
$service   = $container->getComponent('Service');

print get_class($service) . PHP_EOL;
$result = $service->add(2, 3);

If you execute above script, when the add method of the service class was called, TraceInterceptor outputs a log message.

% php context.php
Service_EnhancedByS2AOP
[INFO  ] seasar\aop\interceptor\TraceInterceptor::invoke - BEGIN Service->add(2,3)
Service called.
[INFO  ] seasar\aop\interceptor\TraceInterceptor::invoke - END   Service->add(2,3) : 5 : 0.0012331008911133
%

Using S2ContainerFactory. 

If using S2ContainerFactory, it's necessary to create the following DICON file. TraceInterceptor is aspected to Service component using an aspect tag.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="trace" class="seasar\aop\interceptor\TraceInterceptor"/>
    <component class="Service">
        <aspect>trace</aspect>
    </component>
</components>

Let's create following execution script.

  • Service class is imported by ClassLoader::import method
  • DICON file is read by the S2ContainerFactory::create method
  • Service component is taken out by the S2Container::getComponent method.
  • The add method of the Service component is called.
<?php
require_once('S2Container/S2Container.php');
seasar\util\ClassLoader::import(dirname(__FILE__) . '/classes');
$container = seasar\container\factory\S2ContainerFactory::create(dirname(__FILE__) . '/example.dicon');
$service = $container->getComponent('Service');

print get_class($service) . PHP_EOL;
$result = $service->add(2, 3);

If you execute above script, when the add method of the service class was called, TraceInterceptor outputs a log message.

% php factory.php
Service_EnhancedByS2AOP
[INFO  ] seasar\aop\interceptor\TraceInterceptor::invoke - BEGIN Service->add(2,3)
Service called.
[INFO  ] seasar\aop\interceptor\TraceInterceptor::invoke - END   Service->add(2,3) : 5 : 0.0012331008911133
%
[Note]NOTE

This example is located in examples/aop/interceptor/trace.


4.5.2. MockInterceptor

Using S2ApplicationContext. 

MockInterceptor is Interceptor to easily perform a test using Mock. When using S2ApplicationContext, the return value of a method and the exception which is thrown are set up using comment annotation.

  • Notation of annotation : @S2Mock
  • Argument
    • return : A return value is set up. This value is treated as PHP Expression. (It is processed with an eval function.)
    • throw : The exception which is thrown is set up. This value is treated as PHP Expression.
  • Annotation point : method

As an example, an aspect is applied to the following service class.

class Service {
    /**
     * @S2Mock('return' => '10')
     */
    public function add($a, $b) {
        print __CLASS__ . ' called.' . PHP_EOL;
        return $a + $b;
    }
    /**
     * @S2Mock('throw' => 'new seasar\exception\NotYetImplementedException("mock exception")')
     */
    public function sub($a, $b) {
        print __CLASS__ . ' called.' . PHP_EOL;
    }
}

Let's create following execution scripts.

  • Service class is imported by S2ApplicationContext::import method
  • MockInterceptor is aspected to a service component by the S2ApplicationContext::registerAspect method
  • A container is generated by S2ApplicationContext::create method.
  • Service component is taken out by an S2Container::getComponent method.
  • The add and the sub method of an Service component are performed.
<?php
require_once('S2Container/S2Container.php');
seasar\container\S2ApplicationContext::import(dirname(__FILE__) . '/classes');
seasar\container\S2ApplicationContext::registerAspect('new seasar\aop\interceptor\MockInterceptor', '/^Service$/');
$container = seasar\container\S2ApplicationContext::create();
$service = $container->getComponent('Service');

print $service->add(2, 3) . PHP_EOL;
try {
    $result = $service->sub(3, 2);
} catch(Exception $e) {
    print get_class($e) . ' : ' . $e->getMessage() . PHP_EOL;
}

Execution of the add method of a service class will return 10, not as an actual calculation result(5) but as a Mock value. Execution of sub method will throw the NotYetImplementedException.

% php context.php
10
seasar\exception\NotYetImplementedException : mock exception
%

Using S2ContainerFactory. 

If using S2ContainerFactory, the following DICON file is created. MockInterceptor is applied to Service component using aspect tag. With an initMethod tag, a return value of the add method is set up by the setRetrunValue method. Moreover, to the sub method, the exception which is thrown is set up by the setThrowable method.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
"http://www.seasar.org/dtd/components21.dtd">
<components>
    <component name="mock" class="seasar\aop\interceptor\MockInterceptor">
        <initMethod name="setReturnValue">
            <arg>"add"</arg>
            <arg>20</arg>
        </initMethod>
        <initMethod name="setThrowable">
            <arg>"sub"</arg>
            <arg>new seasar\exception\NotYetImplementedException('mock exception')</arg>
        </initMethod>
    </component>

    <component class="Service">
        <aspect>mock</aspect>
    </component>
</components>

If a setup of Mock value is omitted, information of a comment annotation is used.

Let's create following execution scripts.

  • Service class is imported by ClassLoader::import method
  • DICON file is read by the S2ContainerFactory::create method
  • Service component is taken out by the S2Container::getComponent method.
  • The add and the sub method of Service component are performed.
<?php
require_once('S2Container/S2Container.php');
seasar\util\ClassLoader::import(dirname(__FILE__) . '/classes');
$container = seasar\container\factory\S2ContainerFactory::create(dirname(__FILE__) . '/example.dicon');
$service = $container->getComponent('Service');

print $service->add(2, 3) . PHP_EOL;
try {
    $result = $service->sub(3, 2);
} catch(Exception $e) {
    print get_class($e) . ' : ' . $e->getMessage() . PHP_EOL;
}

Execution of the add method of Service class will return 20, not as an actual calculation result(5) but as Mock value. Execution of the sub method will throw the NotYetImplementedException.

% php factory.php
20
seasar\exception\NotYetImplementedException : mock exception
%
[Note]NOTE

This Example is located in examples/aop/interceptor/mock


4.5.3. InterceptorChain

InterceptorChain group-izes two or more Interceptor. Thereby, reuse of Interceptor is made easy.
As an example, your can set TraceInterceptor and MockInterceptor to InterceptorChain and aspect it to the following service class.

class Service {
    public function add($a, $b) {
        print __CLASS__ . ' called.' . PHP_EOL;
        return $a + $b;
    }
}

Let's create following DICON file. The aspect of the chain component is applied to Service component using aspect tag. By chain component, the trace component and mock component are registered into InterceptorChain using the initMethod tag. Moreover, trace component and mock component are also registered.

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

    <component name="chain" class="seasar\aop\interceptor\InterceptorChain">
        <initMethod name="add"><arg>trace</arg></initMethod>
        <initMethod name="add"><arg>mock</arg></initMethod>
    </component>

    <component name="trace" class="seasar\aop\interceptor\TraceInterceptor"/>
    <component name="mock" class="seasar\aop\interceptor\MockInterceptor">
        <initMethod name="setReturnValue">
            <arg>"add"</arg>
            <arg>20</arg>
        </initMethod>
    </component>
</components>

Please create following execution scripts.

  • Service class is imported by ClassLoader::import method
  • DICON file is read by the S2ContainerFactory::create method
  • Service component is take out by the S2Container::getComponent method.
  • The add method of Service component is performed.
<?php
require_once('S2Container/S2Container.php');
seasar\util\ClassLoader::import(dirname(__FILE__) . '/classes');
$container = seasar\container\factory\S2ContainerFactory::create(dirname(__FILE__) . '/example.dicon');
$service = $container->getComponent('Service');

print $service->add(2, 3) . PHP_EOL;

when the add method of the service class was called, TraceInterceptor outputs a log message. And the add method will return 20, not as an actual calculation result(5) but as Mock value.

% php factory.php
[INFO  ] seasar\aop\interceptor\TraceInterceptor::invoke - BEGIN Service->add(2,3)
[INFO  ] seasar\aop\interceptor\TraceInterceptor::invoke - END   Service->add(2,3) : 5 : 0.0012331008911133
20
%
[Note]NOTE

This Example is located in examples/aop/interceptor/chain.


4.5.4. Implementing Interceptor

When you create original interceptor, it is necessary to implements following MethodInterceptor.

  • seasar\aop\MethodInterceptor
    namespace seasar\aop;
    interface MethodInterceptor {
        /**
         * @param MethodInvocation $invocation
         */
        public function invoke(MethodInvocation $invocation);
    }
    

Let's create following sample Interceptor.

class SampleInterceptor implements seasar\aop\MethodInterceptor {
    public function invoke(seasar\aop\MethodInvocation $invocation){

        print 'Before' . PHP_EOL;            // <-- Processing before calling following Interceptor or a following actual method

        $result = $invocation->proceed();

        print 'After' . PHP_EOL;             // <-- Processing after calling following Interceptor or a following actual method

        return $result;
    }
}

Execution of MethodInvocation::proceed method will call following Interceptor or following actual method. If two or more aspects are applied to a component, execution will be done as-follows.

  • The Before part of MethodInterceptor is performed in order of registration of Aspect.
  • After performing the Before part of the last MethodInterceptor, a component's own method is called.
  • The After part of MethodInterceptor is performed by reverse order of registration of Aspect.

You are able to get the aspect taget instance, ReflectionMethod and method arguments by MethodInvocation instance passed by the argument of MethodInterceptor::invoke method.



© Copyright The Seasar Foundation and the others 2005-2010, all rights reserved.