工作之余抽空看了篇中间件的文章,结合之前自己写过的例子,弄了一个PHP8.0原生注解实现中间件的小demo。核心其实借鉴了一下Laravel的中间件思想,使用array_reduce来处理中间件执行顺序。
首先定义好中间件基类规范接口
interface MiddlewareBase {
public static function handle(Closure $next) : void;
}
接着先写好两个中间件,分别在前置及后置中加入操作
class AuthMiddleware implements MiddlewareBase {
public static function handle(Closure $next) : void
{
echo '验证是否登陆' . PHP_EOL;
$next();
echo PHP_EOL . '返回token';
}
}
class LogMiddleware implements MiddlewareBase {
public static function handle(Closure $next) : void
{
echo '记录日志:访问用户中心' . PHP_EOL;
$next();
echo PHP_EOL . '记录日志:执行时间';
}
}
再把中间件注解基类写好,构造方法使用可变参数来接收中间件传参,然后提供中间件执行的方法
#[Attribute(Attribute::TARGET_METHOD)]
class Middleware {
protected array $middleware;
public function __construct(string ...$middleware){
$this->middleware = $middleware;
}
public function handle(Closure $func) : void
{
// 中间件列表去重、过滤一下非中间件类,并反转一下顺序
$middleware = array_reverse(
array_filter(array_unique($this->middleware), static fn ($class) => is_subclass_of($class, MiddlewareBase::class))
);
call_user_func(array_reduce($middleware, static function ($stack, $pipe) {
return static fn () => $pipe::handle($stack);
}, $func));
}
}
继续完善控制器,定义好需要绑定的中间件
class User {
#[Middleware(
AuthMiddleware::class,
LogMiddleware::class
)]
public function info() : void
{
echo '姓名:中间件示例';
}
}
最后把框架执行逻辑补充上,主要是利用反射来追踪注解然后调取中间件执行方法
class Application {
public function run() : void
{
$user = new User();
try {
// 使用反射操作控制器
$reflection = new ReflectionClass($user::class);
// 获取所有方法
$methods = $reflection->getMethods();
foreach ($methods as $method) {
// 获取方法的所有注解
$funcAttributes = $method->getAttributes();
if (!empty($funcAttributes) && $method->isPublic()) {
$func = $method->getClosure($user);
foreach ($funcAttributes as $attribute) {
// 将注解实例化
$obj = $attribute->newInstance();
// 如果注解为中间件,则执行中间件逻辑
if ($obj instanceof Middleware) {
$obj->handle($func);
}
}
}
}
} catch (ReflectionException $e) {
echo 'error: ' . $e->getMessage();
}
}
}
入口文件直接调取框架执行就好
$app = new Application();
$app->run();
当然真实的框架可能不止这些操作,还会涉及到路由、容器、DI、配置读取、ORM、日志及缓存处理等等功能,这里只是简单阐述一下PHP8.0原生注解中间件的实现方式。对于array_reduce处理中间件的原理不懂的童鞋,可以去PHP技术论坛查看大神的文章去理解,放上链接:middleware 原理
如何传参,resquest
加在 $next() 的参数中就好了吧