TP5.1中间件的使用

Tp5.1也引入了中间件的功能

中间件使用

1.定义中间件类

框架可以使用它命令或者自己在application/http/middleware目录下面生成一个Check中间件,格式一定要如下:必须有handle方法,第一个参数必须是Request对象,第二个是闭包。

class Check
{
    public function handle(Request $request, \Closure $next)
    {
        $request->name = 'aaaaa';
        return $next($request); //执行$next闭包函数,并且把这个闭包函数的结果return。
    }
}

中间件的handle方法就是执行自己的代码,然后调用执行下一个中间件的代码。 结合后面的解析,我们知道$next就是通过Middleware::resolve()返回的闭包函数。这个闭包函数的执行就是从中间件队列中弹出一个中间件,然后通过call_fun_array()执行这个中间件的handle方法。所以中间件的return其实是执行最后一个中间件的return结果。

后置中间件

class After
{
    public function handle(Request $request, \Closure $next)
    {
        $response = $next($request);
        //todo 执行后置中间件的代码
        return $response;
    }
}

 2.注册中间件

1.在路由上注册(只有这个路由才会走中间件)

传'Check',会自动在application/http/middleware目录下找到Check类,也可以传完整类名

Route::get('test', 'Demo/dbTest')->middleware('Check');

2.在对应的目录模块上注册 (这个目录下的控制器方法都会走中间件)tp5.1.8+

例如在application目录下新建middleware.php,则全局都会走这个中间件,如果在application\admin目录下建middleware.php,则只有admin模块下的控制器才会走中间件。

return [
    'Check', //没有写类全名,默认在application/http/middleware下找中间件
];

3.在对应控制器注册中间件(只有这个控制器的方法才走)tp5.1.17+

控制器需要继承系统的think\Controller类,然后在控制器中定义middleware属性,例如

namespace app\index\controller;

use think\Controller;
class Index extends Controller
{
    protected $middleware = ['check'];
    public function test(){}
}

中间件原理 (Middleware类)

think\Middleware类重要属性

$queue = [],存放中间件的数组

$config = [],存放中间件的配置信息

think\Middleware类重要方法

1.buildMiddleware($middleware, $type = 'route'),$middleware可以是‘check’,check的类名,或者闭包。

//如果$middleware是闭包
if ($middleware instanceof \Closure) {
       return [$middleware, isset($param) ? $param : null];
   }
//如果是'check',会去拼接到全类名
return [[$this->app->make($middleware), 'handle'], isset($param) ? $param : null];

2.add()把解析好的中间件挂到属性$queue. 一个中间件元素的格式是[['中间件对象',‘handle’], $param];或者[闭包,$param];

$middleware = $this->buildMiddleware($middleware, $type);
if ($middleware) {
     $this->queue[$type][] = $middleware;
}

并且以$type区分开,$type默认是‘route’;

3.dispatch(Request $request, $type = 'route')中间件调度,就是执行resolve返回的闭包函数

//就是按照一定顺序执行中间件
public function dispatch(Request $request, $type = 'route')
    {
        return call_user_func($this->resolve($type), $request);
    }

4.resolve($type),返回闭包函数,这个闭包函数就是handle方法中的$next。

protected function resolve($type = 'route')
    {
        return function (Request $request) use ($type) {
            $middleware = array_shift($this->queue[$type]);            
            list($call, $param) = $middleware;
            try {
                $response = call_user_func_array($call, [$request, $this->resolve($type), $param]);
            } catch (HttpResponseException $exception) {
                $response = $exception->getResponse();
            }
            if (!$response instanceof Response) {
                throw new LogicException('The middleware must return Response instance');
            }
            return $response;
        };
    }

如何执行中间件的handle方法的,call_user_func_array(),最终得到某个中间件的返回值。并且返回值必须是Response对象,不然会包错。

//这个就是执行中间件的handle方法,而handler方法最后又会执行resolve返回的闭包方法,
//相当于又得到另外一个$call再次执行下面这段代码。所以这是一个递归的过程。
//一直循环到某个中间件,没有再调用$this->resolve($type)这个闭包参数。即在handle方法中没有执行$next($resquest);而是直接返回Response对象。然后一层一层往上归回来。
$response = call_user_func_array($call, [$request, $this->resolve($type), $param]);

$call就是[中间件对象,'handle']
$request就是handle的第一个参数
$this->resolve($type),就是handle的第二个参数,是一个闭包
$param是第三个参数
$response就是handle的返回值

 TP5.1是如何使用Middleware类库实现中间件的功能的

1.初始化模块init(),会加载模块目录下的middleware.php文件

 // 加载中间件文件,import没有传type,默认是route
            if (is_file($path . 'middleware.php')) {
                $middleware = include $path . 'middleware.php';
                if (is_array($middleware)) {
                    $this->middleware->import($middleware);
                }
            }

初始化模块方法init(),在系统初始化的时候调用一次,那时候还没有解析路由,默认模块就是Application模块,然后根据访问路径得到模块,然后会再次调用,表示初始化对应模块。所以一个进程只会加载Application/和访问模块的下的middleware.php。 

2. 路由文件导入,include rute目录下的文件。

//get方法返回的是RuleItem实例,我们知道一条路由规则生成一个RuleItem实例。
Route::get('test', 'Demo/test')->middleware('Check');

 middleware方法会把Check放到属性option中,在创建调度对象Module实例前,会把中间件加进去

 // 添加域名中间件
if (!empty($this->option['middleware'])) {
     Container::get('middleware')->import($this->option['middleware']);
     unset($this->option['middleware']);
 }

3.调度器执行方法,作为一个匿名函数,加入到中间件队列中

//通过路由解析后,最终通过$dispatch->run()其实就是执行我们的控制器方法,然后返回Response对象。
//正常路由解析后,$data为null。
$this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
            return is_null($data) ? $dispatch->run() : $data;
        });
//中间件调度,把注册好的中间件按顺序执行,直到执行到返回Response对象的那个中间件
$response = $this->middleware->dispatch($this->request);

上面三种方式,分别把middleware.php的中间件,自定义路由的中间件,控制器的执行分别加入到中间件队列。

PHP的Closure类

我们创建一个闭包函数,var_dump这个闭包函数,发现他是一个Closure类。在PHP中定义一个闭包函数其实就是一个Closure类的实例。闭包函数作为函数参数,可以看做是把这段函数代码传给函数。

猜你喜欢

转载自blog.csdn.net/littlexiaoshuishui/article/details/103431138