crysalead / filter
方法过滤系统
Requires
- php: >=5.5
- crysalead/jit: ~2.1
Requires (Dev)
- crysalead/kahlan: ~2.1
README
方法过滤是一种替代事件驱动架构的方法。它提供了一种在不太多污染原始源代码的情况下,在程序流程中注入/覆盖一些逻辑的方式。
有一些不同的现有方法尝试将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自动加载器加载的类。如果类使用require
或include
包含,或者在使用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)
-
是否可以为所有实例应用过滤器? 是的,为了实现这种行为,您需要使用类名字符串作为上下文来设置您的过滤器。
-
子类是否继承父类级别设置的过滤器? 是的。