ray/aop

面向切面框架

维护者

详细信息

github.com/ray-di/Ray.Aop

来源

问题

资助包维护!
koriym
Tidelift

安装量: 1,241,484

依赖者: 36

建议者: 0

安全: 0

星标: 98

关注者: 13

分支: 27

开放问题: 0

2.16.2 2024-09-03 02:52 UTC

README

面向切面框架

Scrutinizer Code Quality codecov Type Coverage Continuous Integration Total Downloads

ray-di logo

[日语]

Ray.Aop 包提供方法拦截。此功能使您能够编写在匹配的方法被调用时执行的代码。它适用于跨切面关注点("方面"),如事务、安全和日志记录。因为拦截器将问题划分为方面而不是对象,所以它们的使用称为面向切面编程(AOP)。

匹配器是一个简单的接口,它接受或拒绝一个值。对于 Ray.AOP,您需要一个匹配器:一个定义哪些类参与,另一个用于这些类的成员方法。为了使这变得简单,有一个工厂类来满足常见的场景。

方法拦截器在匹配的方法被调用时执行。它们有机会检查调用:方法、其参数和接收实例。它们可以执行它们的跨切面逻辑,然后委托给底层方法。最后,它们可以检查返回值或异常并返回。由于拦截器可以应用于许多方法并将接收许多调用,因此它们的实现应该是高效且不干扰的。

示例:禁止周末调用方法

为了说明方法拦截器如何与 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();
    }
}

最后,我们使用 Aspect 类配置一切

use Ray\Aop\Aspect;
use Ray\Aop\Matcher;

$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";
    exit(1);
}

将所有这些放在一起(并等待周六),我们看到方法被拦截,我们的订单被拒绝

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'); // Weave the aspects to all classes in the directory that match the matcher.

$billing = new RealBillingService();
echo $billing->chargeOrder(); // Interceptors applied

使用 PECL 扩展

  • 您可以在代码的任何位置使用正常的 new 关键字创建新实例。
  • 拦截器甚至可以在 final 类和方法中工作。

要使用这些功能,只需安装 PECL 扩展,Ray.Aop 将在可用时自动利用它。需要 PHP 8.1+ 以使用 PECL 扩展。

安装 PECL 扩展

要使用 PECL 扩展,需要 PHP 8.1 或更高版本。有关更多信息,请参阅ext-rayaop

配置选项

在创建 Aspect 实例时,您可以可选地指定一个临时目录

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

如果没有指定,将使用系统默认的临时目录。

这总结了Ray.Aop的基本用法。有关更详细的信息和高级用法,请参阅完整文档。

自定义匹配器

您可以拥有自己的匹配器。要创建contains匹配器,您需要提供一个包含两个方法的类。一个是用于类匹配的matchesClass方法,另一个是用于方法匹配的matchesMethod方法。两者都返回匹配结果的布尔值。

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

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

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

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

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

拦截器详情

在拦截器中,将一个MethodInvocation对象传递给invoke方法。

class MyInterceptor implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        // Before method invocation
        $result = $invocation->proceed();
        // After method invocation
        return $result;
    }
}

$invocation->proceed()调用链中的下一个拦截器。如果没有更多的拦截器,它将调用目标方法。这种链式调用允许对单个方法使用多个拦截器,并按照绑定顺序执行。

拦截器A、B和C的示例执行流程

  1. 拦截器A(之前)
  2. 拦截器B(之前)
  3. 拦截器C(之前)
  4. 目标方法
  5. 拦截器C(之后)
  6. 拦截器B(之后)
  7. 拦截器A(之后)

这种链式机制允许您将多个横切关注点(如日志记录、安全和性能监控)组合到单个方法中。

使用MethodInvocation对象,您可以

/** @var $method \Ray\Aop\ReflectionMethod */
$method = $invocation->getMethod();
/** @var $class \Ray\Aop\ReflectionClass */
$class = $invocation->getMethod()->getDeclaringClass();
  • $method->getAnnotations() - 获取方法属性/注解
  • $method->getAnnotation($name) - 获取方法属性/注解
  • $class->->getAnnotations() - 获取类属性/注解
  • $class->->getAnnotation($name) - 获取类属性/注解

注解/属性

Ray.Aop可以与PHP 7/8的doctrine/annotation或PHP 8的Attributes一起使用。

AOP联盟

Ray.Aop实现的拦截器API是称为AOP Alliance的公开规范的一部分。

安装

推荐通过Composer安装Ray.Aop。

# Add Ray.Aop as a dependency
$ composer require ray/aop ^2.0

PHP8属性仅(推荐)

SevericeLocator::setReader(new AttributeReader);`

集成DI框架

  • 还可以查看集成了DI和AOP的DI框架Ray.Di

技术信息

有关更详细的技术信息,请参阅以下资源

  • 注意:本部分文档来自Guice/AOP。z1