BEARのAOP をよく使っています。この記事はBEARでインターセプターを実装するときの私の覚え書きです。
カスタムアノテーションを作るべきとき
- 「単にアプリケーションの設定値を束縛したい」という用途であれば、必ずしもカスタムアノテーションを用意する必要はない。その用途ならばビルトインアノテーションの
@Named
1 が簡易的で便利。 - 基本的にはカスタムアノテーションクラスを作成することを検討。
モジュールとカスタムアノテーション
- 必要に応じてモジュールの分割を行う(独立した機能モジュールでのインターセプター、カスタムアノテーションの設計)。
- 例: 認可のアノテーション
Accessible
を作成したい場合
<?php
namespace MyVendor\MyPackage\Security\Module;
// (`AppModule` に直接束縛を書くのではなく、)
// セキュリティを扱う独立した機能として `SecurityModule` を作成、
// その中で認可機能を束縛。
class SecurityModule extends AbstractModule
{
public function configure()
{
$this->bindInterceptor(
$this->matcher->subclassesOf(ResourceObject::class),
$this->matcher->annotatedWith(Accessible::class),
[AccessibleInterceptor::class]
);
}
}
use MyVendor\MyPackage\Security\Module\Accessible;
// `SecurityModule` のアノテーションを利用
/**
* @Accessible("administrator")
*/
public function onGet(string $articleId)
{
// ...
}
マッチャー(Matcher)
- モジュールの束縛で適切に対象を指定(インターセプタークラス内で実行対象をチェックしているとしても明示的に設定)
- 必要に応じてカスタムマッチャー 2 を作成
リソースのHTTPメソッドをインターセプトしたい場合の実装例
// リソースのメソッドのためのカスタムマッチャー
class IsHttpMethodMatcher extends EqualsToMatcher
{
public function __construct()
{
parent::__construct([
'onGet',
'onPost',
'onPut',
'onPatch',
'onDelete',
'onHead',
]);
}
}
// 上記 `IsHttpMethodMatcher` のスーパークラス。
// 同一かどうかのカスタムマッチャー。
use Ray\Aop\AbstractMatcher;
class EqualsToMatcher extends AbstractMatcher
{
/**
* @var array
*/
private $values;
/**
* @param string|array $values
*/
public function __construct($values)
{
parent::__construct();
$this->values = (array) $values;
}
/**
* {@inheritdoc}
*/
public function matchesClass(\ReflectionClass $class, array $arguments)
{
return in_array(strtolower($class->getName()), array_map('strtolower', $this->values));
}
/**
* {@inheritdoc}
*/
public function matchesMethod(\ReflectionMethod $method, array $arguments)
{
return in_array(strtolower($method->getShortName()), array_map('strtolower', $this->values));
}
}
// モジュールでの束縛
$this->bindInterceptor(
$this->matcher->subclassesOf(ResourceObject::class),
new IsHttpMethodMatcher(),
[FooInterceptor::class]
);
上記は、@iteman さんによるプライベートプロジェクトのコードを一部改変したものです。下記パッケージにしてあります。
※リソースのインターセプターの用途としては自分のドメインに合ったフレームワークを作りたいときを想定しています。BEARは標準セットで使うこともできますが、拡張に対して開かれているので自分のドメインにあったフレームワークを作っていくアプローチを採ることも可能です。
インターセプターとサービスの協調
- インターセプターに複雑な仕事をさせる場合はサービスの利用を検討
- サービスにバリエーションの考慮が不要な場合はサービスを直接
アンターゲット束縛
3 してインターセプターへインジェクト
インターセプター --- depends ---> サービス
- フレームワークとサービスの境界を設計 (扱う情報の正規化はサービスに引き渡す前に済ませる)
まとめ
BEARでインターセプターを実装する時のトピックを挙げて、リソースをインターセプトしたい場合の実装例も記しました。適切なアノテーション、適切なマッチャーを使うことは良いプラクティスだと思います。