slim3中的middleware有两种方式添加:
<?php
require 'vendor/autoload.php';
$app = new Slim\App();
// 第一种方式(应用级中间件)
$app->add(new HMiddleWare());
// 第二种方式 (路由中间件)
$app->get('/hello/{name}', function ($request, $response, $args) {
return $response->getBody()->write("Hello, " . $args['name']);
})->add(function($request, $response, $next) {
$response->getBody()->write('BEFORE middle1');
$response = $next($request, $response);
$response->getBody()->write('AFTER middle1');
return $response;
});
$app->run();
class HMiddleWare
{
function __invoke($request, $response, $next)
{
$response->getBody()->write('BEFORE middle2');
$response = $next($request, $response);
$response->getBody()->write('AFTER middle2');
return $response;
}
}\
slim添加中间件时会一层一层的套,先添加的在最里面,后添加的在最外面,这样request请求进来时也是从外层到里层再到外层。应用级中间件先执行,执行到$route->run()时,会继续执行路由级中间件
所以访问http://127.0.0.1:8000/hello/fdas,结果是
BEFORE middle2
BEFORE middle1
Hello, fdas
AFTER middle1
AFTER middle2
接着我们来瞅瞅源码的实现,首先我们先看第一种方式是如何添加middleware,
public function add($callable)
{
return $this->addMiddleware(new DeferredCallable($callable, $this->container));
}
protected function addMiddleware(callable $callable)
{
if ($this->middlewareLock) {
throw new RuntimeException('Middleware can’t be added once the stack is dequeuing');
}
// 第一次将app对象赋值给$this->tip
if (is_null($this->tip)) {
$this->seedMiddlewareStack();
}
$next = $this->tip;
$this->tip = function (
ServerRequestInterface $request,
ResponseInterface $response
) use (
$callable,
$next
) {
$result = call_user_func($callable, $request, $response, $next);
if ($result instanceof ResponseInterface === false) {
throw new UnexpectedValueException(
'Middleware must return instance of \Psr\Http\Message\ResponseInterface'
);
}
return $result;
};
return $this;
}
protected function seedMiddlewareStack(callable $kernel = null)
{
if (!is_null($this->tip)) {
throw new RuntimeException('MiddlewareStack can only be seeded once.');
}
if ($kernel === null) {
$kernel = $this;
}
$this->tip = $kernel;
}
先判断$this->tip是否为null,即第一次将app对象赋值给$this->tip,然后将$this->tip赋值给$next,然后将一个闭包函数赋值给$this->tip。在这个函数中,call_user_func($callable, $request, $response, $next)就是调用我们middleware中的__invoke方法。
我们结合上面的例子来讲一讲,在我$app->add(new HMiddleWare())时
/**
* 第一次添加middleware (应用级)
*/
$callable => HMiddleWare对象
$this->tip => app对象
$next => app对象
$this->tip => function($request, $response) use (HMiddleWare对象, app对象) {
$result = call_user_func(HMiddleWare对象, $request, $response, app对象);
}
/**
* 第二次添加middleware (路由级)
*/
$callable => function($request, $response, $next) {
$response->getBody()->write('BEFORE middle1');
$response = $next($request, $response);
$response->getBody()->write('AFTER middle1');
return $response;
}
$this->tip => route对象
$next => route对象
$this->tip => function($request, $response) use ($callable, route对象) {
$result = call_user_func(
function($request, $response, $next) {
$response->getBody()->write('BEFORE middle1');
$response = $next($request, $response);
$response->getBody()->write('AFTER middle1');
return $response;
},
$request,
$response,
route对象
}
这样当我们执行request时(可以看我的另一篇文章关于request的),即执行$this->tip(),
- 1、先走HMiddleWare的__invoke方法,输出brfore后执行next()
- 2、next()就是$app->__invoke()方法,在里面执行$route->run()
- 3、在$route->run()添加了路由中间后在执行,先执行callable,输出brfore后,然后在执行next(),
- 4、next()就是route->__invoke(), 执行完后调用callable的after, 在调用HMiddleWare中的after