Skip to content

Latest commit

 

History

History
234 lines (171 loc) · 10.6 KB

README.ja.md

File metadata and controls

234 lines (171 loc) · 10.6 KB

Ray.Aop

アスペクト指向フレームワーク

Scrutinizer Code Quality codecov Type Coverage Build Status Total Downloads

[English]

Ray.Aop パッケージはメソッドインターセプションの機能を提供します。マッチするメソッドが実行される度に実行されるコードを記述する事ができます。トランザクション、セキュリティやログといった横断的な”アスペクト”に向いています。なぜならインターセプターが問題をオブジェクトというよりアスペクトに分けるからです。これらの用法はアスペクト指向プログラミング(AOP)と呼ばれます。

Matcher は値を受け取ったり拒否したりするシンプルなインターフェイスです。例えばRay.Aopでは2つの Matcher が必要です。1つはどのクラスに適用するかを決め、もう1つはそのクラスのどのメソッドに適用するかを決めます。これらを簡単に利用するためのファクトリークラスがあります。

MethodInterceptors はマッチしたメソッドが呼ばれる度に実行されます。呼び出しやメソッド、それらの引き数、インスタンスを調べる事ができます。横断的なロジックと委譲されたメソッドが実行されます。最後に返り値を調べて返します。インターセプターは沢山のメソッドに適用され沢山のコールを受け取るので、実装は効果的で透過的なものになります。

例:平日のメソッドコールを禁止する

メソッドインターセプターがRay.Aopでどのように機能するかを明らかにするために、週末にはピザの注文を禁止するようにしてみましょう。デリバリーは平日だけ受け付ける事にして、ピザの注文を週末には受け付けないようにします!この例はAOPで認証を使用するときにのパターンと構造的に似ています。

週末だけにするためのアノテーションを定義します。

<?php
#[Attribute(Attribute::TARGET_METHOD)]
final class NotOnWeekends
{
}

そして、インターセプトするメソッドに適用します。

<?php
class RealBillingService
{
    #[NotOnWeekends] 
    public function chargeOrder(PizzaOrder $order, CreditCard $creditCard)
    {

次に、org.aopalliance.intercept.MethodInterceptorインターフェイスを実装したインターセプターを定義します。元のメソッドを実行するためには $invocation->proceed() と実行します。

<?php
class WeekendBlocker implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        $today = getdate();
        if ($today['weekday'][0] === 'S') {
            throw new \RuntimeException(
                $invocation->getMethod()->getName() . " not allowed on weekends!"
            );
        }
        return $invocation->proceed();
    }
}

設定完了しました。このコードでは「どのクラスでも」「メソッドに@NotOnWeekendsアノテーションがある」という条件にマッチします。

<?php

use Ray\Aop\Sample\Annotation\NotOnWeekends;
use Ray\Aop\Sample\Annotation\RealBillingService;

$aspect = new Aspect();
$aspect->bind(
    (new Matcher())->any(),
    (new Matcher())->annotatedWith(NotOnWeekends::class),
    [new WeekendBlocker()]
);

$billing = $aspect->newInstance(RealBillingService::class);
try {
    echo $billing->chargeOrder();
} catch (\RuntimeException $e) {
    echo $e->getMessage() . "\n";
}

全てをまとめ(土曜日まで待って)、メソッドをコールするとインターセプターにより拒否されます。

chargeOrder not allowed on weekends!

PECL拡張

Ray.AopはPECL拡張もサポートしています。拡張機能がインストールされている場合、weaveメソッドを使用してディレクトリ内のすべてのクラスにアスペクトを適用できます。

$aspect = new Aspect();
$aspect->bind(
    (new Matcher())->any(),
    (new Matcher())->annotatedWith(NotOnWeekends::class),
    [new WeekendBlocker()]
);
$aspect->weave(__DIR__ . '/src'); // ディレクトリ内でマッチャーに一致するすべてのクラスにアスペクトを織り込みます。

$billing = new RealBillingService();
echo $billing->chargeOrder(); // インターセプターが適用されます。

PECL拡張機能を使用すると:

通常のnewキーワードを使用してコードのどこでも新しいインスタンスを作成できます。 インターセプションはfinalクラスやメソッドでも動作します。 これらの機能を使用するには、PECL拡張機能をインストールするだけで、Ray.Aopが自動的に利用します。

PECL拡張のインストール

PECL拡張機能の利用にはPHP 8.1以上が必要です。詳細はext-rayaopを参照してください。

設定オプション

Aspectインスタンスを作成する際に、オプションで一時ディレクトリを指定できます。

$aspect = new Aspect('/path/to/tmp/dir');

指定しない場合、システムのデフォルトの一時ディレクトリが使用されます。

インターセプターの詳細

呼び出されたメソッドをそのまま実行するだけのインターセプターは以下のようになります。

class MyInterceptor implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        // メソッド呼び出し前
        $result = $invocation->proceed();
        // メソッド呼び出し後
        return $result;
    }
}

$invocation->proceed()はチェーン内の次のインターセプターを呼び出します。インターセプターが存在しない場合、ターゲットメソッドを呼び出します。このチェーンにより、単一のメソッドに対して複数のインターセプターを適用し、バインドされた順序で実行することができます。

インターセプターA、B、Cがメソッドにバインドされている場合の実行フローの例:

  1. インターセプターA(前)
  2. インターセプターB(前)
  3. インターセプターC(前)
  4. ターゲットメソッド
  5. インターセプターC(後)
  6. インターセプターB(後)
  7. インターセプターA(後)

拡張されたClassReflectionとMethodReflectionはPHP 8の属性とドクトリンアノテーションを取得するメソッドを提供します。

/** @var $method \Ray\Aop\ReflectionMethod */
$method = $invocation->getMethod();
/** @var $class \Ray\Aop\ReflectionClass */
$class = $invocation->getMethod()->getDeclaringClass();

独自のマッチャー

独自のマッチャーを作成できます。例えば、ContainsMatcherを作成するには:

use Ray\Aop\AbstractMatcher;

class ContainsMatcher extends AbstractMatcher
{
    public function matchesClass(\ReflectionClass $class, array $arguments) : bool
    {
        [$contains] = $arguments;
        return (strpos($class->name, $contains) !== false);
    }

    public function matchesMethod(\ReflectionMethod $method, array $arguments) : bool
    {
        [$contains] = $arguments;
        return (strpos($method->name, $contains) !== false);
    }
}

アノテーション/属性 Ray.Aopはdoctrine/annotationまたはPHP 8のAttributesのどちらかも使用できます。

AOPアライアンス

このメソッドインターセプターのAPIはAOPアライアンスの部分実装です。

インストール

Ray.Aopの推奨インストール方法は、Composerでのインストールです。

# Ray.Aop を依存パッケージとして追加する
$ composer require ray/aop ^2.0

アトリビュートのみ使用(推奨)

SevericeLocator::setReader(new AttributeReader);`

DI Framework

DIとAOPを統合したDIフレームワークRay.Diもご覧ください。

安定性

Ray.Aopはセマンティックバージョニングに従い、後方互換性を保証します。2015年のバージョン2.0以降、PHPの進化に合わせて機能を拡張しながら互換性を維持してきました。今後もこの安定性を維持していきます。


  • この文書の大部分は Guice/AOP から借用しています。