AOP
アスペクト指向プログラミングは、横断的関心事の問題を解決します。対象メソッドの前後に、任意の処理をインターセプターで織り込むことができます。 対象となるメソッドはビジネスロジックなどの本質的関心事のみに関心を払い、インターセプターはログや検証などの横断的関心事に関心を払います。
BEAR.SundayはAOP Allianceに準拠したアスペクト指向プログラミングをサポートします。
インターセプター
インターセプターのinvoke
メソッドでは$invocation
メソッド実行変数を受け取り、メソッドの前後に処理を加えます。これはインターセプター元メソッドを実行するためだけの変数です。前後にログやトランザクションなどの横断的処理を記述します。
use Ray\Aop\MethodInterceptor;
use Ray\Aop\MethodInvocation;
class MyInterceptor implements MethodInterceptor
{
public function invoke(MethodInvocation $invocation)
{
// メソッド実行前の処理
// ...
// メソッド実行
$result = $invocation->proceed();
// メソッド実行後の処理
// ...
return $result;
}
}
束縛
モジュールで対象となるクラスとメソッドをMatcher
で”検索”して、マッチするメソッドにインターセプターを束縛します。
$this->bindInterceptor(
$this->matcher->any(), // どのクラスでも
$this->matcher->startsWith('delete'), // "delete"で始まるメソッド名のメソッドには
[Logger::class] // Loggerインターセプターを束縛
);
$this->bindInterceptor(
$this->matcher->subclassesOf(AdminPage::class), // AdminPageの継承または実装クラスの
$this->matcher->annotatedWith(Auth::class), // @Authアノテーションがアノテートされているメソッドには
[AdminAuthentication::class] // AdminAuthenticationインターセプターを束縛
);
Matcher
では以下のような指定も可能です:
- Matcher::any - 無制限
- Matcher::annotatedWith - アノテーション
- Matcher::subclassesOf - 継承または実装されたクラス
- Matcher::startsWith - 名前の始めの文字列
- Matcher::logicalOr - OR条件
- Matcher::logicalAnd - AND条件
- Matcher::logicalNot - NOT条件
インターセプターに渡されるMethodInvocation
では、対象のメソッド実行に関連するオブジェクトやメソッド、引数にアクセスすることができます。
- MethodInvocation::proceed - 対象メソッド実行
- MethodInvocation::getMethod - 対象メソッドリフレクションの取得
- MethodInvocation::getThis - 対象オブジェクトの取得
- MethodInvocation::getArguments - 呼び出し引数配列の取得
リフレクションのメソッドでアノテーションを取得することができます。
$method = $invocation->getMethod();
$class = $invocation->getMethod()->getDeclaringClass();
$method->getAnnotations()
- メソッドアノテーションの取得$method->getAnnotation($name)
$class->getAnnotations()
- クラスアノテーションの取得$class->getAnnotation($name)
カスタムマッチャー
独自のカスタムマッチャーを作成するには、AbstractMatcher
のmatchesClass
とmatchesMethod
を実装したクラスを作成します。
contains
マッチャーを作成するには、2つのメソッドを持つクラスを提供する必要があります。
1つはクラスのマッチを行うmatchesClass
メソッド、もう1つはメソッドのマッチを行うmatchesMethod
メソッドです。いずれもマッチしたかどうかをboolで返します。
use Ray\Aop\AbstractMatcher;
/**
* 特定の文字列が含まれているか
*/
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);
}
}
モジュール
class AppModule extends AbstractAppModule
{
protected function configure()
{
$this->bindInterceptor(
$this->matcher->any(),
new ContainsMatcher('user'), // 'user'がメソッド名に含まれているか
[UserLogger::class]
);
}
};