slim3中的middleware有兩種方式添加:
<?php
require 'vendor/autoload.php';
$app = new Slim\App();
// 第一種方式(應(yīng)用級中間件)
$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請求進來時也是從外層到里層再到外層。應(yīng)用級中間件先執(zhí)行瓮具,執(zhí)行到$route->run()時荧飞,會繼續(xù)執(zhí)行路由級中間件
image.png
所以訪問http://127.0.0.1:8000/hello/fdas,結(jié)果是
BEFORE middle2
BEFORE middle1
Hello, fdas
AFTER middle1
AFTER middle2
接著我們來瞅瞅源碼的實現(xiàn)名党,首先我們先看第一種方式是如何添加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耳幢,然后將一個閉包函數(shù)賦值給$this->tip。在這個函數(shù)中欧啤,call_user_func($callable, $request, $response, $next)就是調(diào)用我們middleware中的__invoke方法睛藻。
我們結(jié)合上面的例子來講一講,在我$app->add(new HMiddleWare())時
/**
* 第一次添加middleware (應(yīng)用級)
*/
$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對象
}
這樣當(dāng)我們執(zhí)行request時(可以看我的另一篇文章關(guān)于request的)邢隧,即執(zhí)行$this->tip(),
- 1店印、先走HMiddleWare的__invoke方法,輸出brfore后執(zhí)行next()
- 2府框、next()就是$app->__invoke()方法吱窝,在里面執(zhí)行$route->run()
- 3、在$route->run()添加了路由中間后在執(zhí)行迫靖,先執(zhí)行callable,輸出brfore后院峡,然后在執(zhí)行next(),
- 4系宜、next()就是route->__invoke(), 執(zhí)行完后調(diào)用callable的after照激, 在調(diào)用HMiddleWare中的after