AOP

BEAR.Sunday AOP enables you to write code that is executed each time a matching method is invoked. It’s suited for cross cutting concerns (“aspects”), such as transactions, security and logging. Because interceptors divide a problem into aspects rather than objects, their use is called Aspect Oriented Programming (AOP).

The method interceptor API implemented is a part of a public specification called AOP Alliance.

Interceptor

MethodInterceptors are executed whenever a matching method is invoked. They have the opportunity to inspect the call: the method, its arguments, and the receiving instance. They can perform their cross-cutting logic and then delegate to the underlying method. Finally, they may inspect the return value or the exception and return. Since interceptors may be applied to many methods and will receive many calls, their implementation should be efficient and unintrusive.

use Ray\Aop\MethodInvocation;

class MyInterceptor implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        // Process before method invocation
        // ...

        // Original method invocation
        $result = $invocation->proceed();

        // Process after method invocation
        // ...

        return $result;
    }
}

Bindings

“Find” the target class and method with Matcher and bind the interceptor to the matching method in Module.

$this->bindInterceptor(
    $this->matcher->any(),                   // In any class,
    $this->matcher->startsWith('delete'),    // Method(s) names that start with "delete",
    [Logger::class]                          // Bind a Logger interceptor
);

$this->bindInterceptor(
    $this->matcher->subclassesOf(AdminPage::class),  // Of the AdminPage class or a class inherited from it
    $this->matcher->annotatedWith(Auth::class),      // Annotated method with the @Auth annotation
    [AdminAuthentication::class]                     //Bind the AdminAuthenticationInterceptor
);

There are various matchers.

With the MethodInvocation object, you can access the target method’s invocation object, method’s and parameters.

Annotations can be obtained using the reflection API.

$method = $invocation->getMethod();
$class = $invocation->getMethod()->getDeclaringClass();
  • $method->getAnnotations() // get method annotations
  • $method->getAnnotation($name)
  • $class->getAnnotations() // get class annotations
  • $class->getAnnotation($name)

Own matcher

You can have your own matcher. To create contains matcher, You need to provide a class which has two methods. One is matchesClass for a class match. The other one is matchesMethod method match. Both return the boolean result of match.

use Ray\Aop\AbstractMatcher;
use Ray\Aop\Matcher;

class ContainsMatcher extends AbstractMatcher
{
    /**
     * {@inheritdoc}
     */
    public function matchesClass(\ReflectionClass $class, array $arguments) : bool
    {
        list($contains) = $arguments;

        return (strpos($class->name, $contains) !== false);
    }

    /**
     * {@inheritdoc}
     */
    public function matchesMethod(\ReflectionMethod $method, array $arguments) : bool
    {
        list($contains) = $arguments;

        return (strpos($method->name, $contains) !== false);
    }
}

Module

class AppModule extends AbstractAppModule
{
    protected function configure()
    {
        // ...
        $this->bindInterceptor(
            $this->matcher->any(),       // In any class,
            new ContainsMatcher('user'), // When 'user' contained in method name
            [UserLogger::class]          // Bind UserLogger class
        );
    }
};