LoginSignup
2
1

More than 3 years have passed since last update.

BEARでインターセプターを実装する時の覚え書き(アノテーション、マッチャー)

Last updated at Posted at 2017-12-23

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でインターセプターを実装する時のトピックを挙げて、リソースをインターセプトしたい場合の実装例も記しました。適切なアノテーション、適切なマッチャーを使うことは良いプラクティスだと思います。


  1.  @Named (名前束縛) : Ray.Di のビルトインアノテーションで 名前 を指定して束縛します 

  2.  カスタムマッチャーの作成手順についてはBEARマニュアル参照 

  3.  アンターゲット束縛:コンクリートクラスの束縛ができます(Ray.Di マニュアル参照) 

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1