crysalead/filter

方法过滤系统

4.0.0 2016-02-15 14:47 UTC

This package is auto-updated.

Last update: 2024-08-29 04:08:56 UTC


README

Build Status Code Coverage

方法过滤是一种替代事件驱动架构的方法。它提供了一种在不太多污染原始源代码的情况下,在程序流程中注入/覆盖一些逻辑的方式。

有一些不同的现有方法尝试将AOP概念引入PHP

所有这些方法的目标是提供以下方法控制

        │                ▲
        │                │
 ┌──────┼────────────────┼──────┐
 │      │    Filter 1    │      │
 │      │                │      │
 │ ┌────┼────────────────┼────┐ │
 │ │    │    Filter 2    │    │ │
 │ │    │                │    │ │
 │ │┌───┼────────────────┼───┐│ │
 │ ││   │ Implementation │   ││ │
 │ ││   ▼                │   ││ │
 │ ││                        ││ │
 │ │└────────────────────────┘│ │
 │ └──────────────────────────┘ │
 └──────────────────────────────┘

这个AOP库的目标是使用即时代码修补技术,将li3过滤系统的简单性引入,使得任何类型的方法都可以被过滤(甚至供应商的方法)。

示例

让我们来看以下代码示例

class Home
{
    public static function version()
    {
        return '1.0.0';
    }

    public function enter($name)
    {
        return "Welcome {$name}!";
    }
}

在这个点上,我们无法像在JavaScript或其他更宽容的语言中那样更改方法的行为。在这个点上,我们可以以两种方式实现上述方法。首先,手动添加一些样板代码使方法可过滤,或者简单地启用一些即时修补,这将动态地进行重写。

手动方式

为了展示AOP在底层的工作方式,我将首先展示如何手动使方法可过滤(即不使用即时修补)。

要使方法可过滤,需要一些样板代码

namespace City;

use Lead\Filter\Filters;

class Home {

    public static function version()
    {
        Filters::run(get_called_class(), __FUNCTION__, [], function($next) {
            return '1.0.0'; // Your inchanged code here
        });
    }

    public function enter($name)
    {
        Filters::run($this, __FUNCTION__, [$name], function($next, $name) {
            return "Welcome {$name}!"; // Your inchanged code here
        });
    }
}

思路是将方法逻辑包裹在一个闭包中,并在参数列表中添加一个强制性的$next参数。$next代表要应用的过滤链,将在过滤中用于执行下一个可应用过滤器。

一旦代码被重写,现在就可以设置过滤器了

use Lead\Filter\Filters;

Filters::apply('city\Home', 'version', function($next) {
    $version = $next();
    return "Version: {$version}";
});

$home = new Home();
Filters::apply($home, 'enter', function($next, $name) {
    $name = "Mister {$name}";
    return $next($name);
});

echo "You are using the Home " . Home::version();
echo $home->enter('Bob');

然后它将产生

You are using the Home Version 1.0.0
Welcome Mister Bob

自动方式

对于自动方式,我们将使用JIT代码修补器使重写步骤自动进行,并对用户透明。

这通过使用Filters::patch()完成。修补器必须在尽可能早的时候初始化,例如在composer自动加载器包含之后。

include __DIR__ . '/../vendor/autoload.php';

use Lead\Filter\Filters;

Filters::patch(true);

注意:修补器适用于由composer自动加载器加载的类。如果类使用requireinclude包含,或者在使用Filters::patch(true)调用之前已经加载,则不会进行修补。

使用Filters::patch(true)是设置修补器的简单方法,但你应该记住,你的所有代码以及你的供应商代码都将被修补。即使修补后的类被缓存,将所有方法包裹在过滤器闭包中也可能消耗时间。

因此,首选方法是只修补需要的文件

Filters::patch([
 'City\Home',
 'An\Example\ClassName::foo',
 'A\Second\Example\ClassName' => ['foo', 'bar'],
]);

因此,您可以使自己的方法和供应商的方法都可以被过滤。

也可以配置缓存路径,如下所示

Filters::patch([
 'City\Home',
 'An\Example\ClassName::foo',
 'A\Second\Example\ClassName' => ['foo', 'bar'],
], [
    'cachePath' => __DIR__ . '/../cache/jit',
]);

注意:确保apache能够写入你的缓存文件夹。

API

使方法可过滤

手动:

Filters::run($context, $method, $args, $closure);

或自动:

Filters::patch(['City\Home']);

将过滤器应用于类或实例

$id = Filters::apply($context, $method, $closure);

从类或实例中分离过滤器

从可调用关联中分离所有过滤器

Filters::detach($context, $method);

仅分离特定过滤器

Filters::detach($id);

导出/恢复过滤系统。

获取器

$filters = Filters::get();

设置器

Filters::set($filters);

清除注册的闭包和应用的过滤器。

Filters::reset();

注意:它还会断开所有静态附加的过滤器(即它不会影响实例方法的过滤器)。

全局启用/禁用过滤器系统

Filters::enable(); // Enable
Filters::enable(false); // Disable

注意:它不会断开任何过滤器,只是简单地绕过Filters::run()中的过滤器。

常见问题解答(FAQ)

  • 是否可以为所有实例应用过滤器? 是的,为了实现这种行为,您需要使用类名字符串作为上下文来设置您的过滤器。

  • 子类是否继承父类级别设置的过滤器? 是的。