laravel5.2에서 Http의 주요 기능은 Http 요청을 필터링하는 것입니다(php aritsan
에는 미들웨어 메커니즘이 없습니다). 또한 시스템 수준(Http 필터링 계층)을 더 많이 만듭니다. 명확하고 우아하게 사용할 수 있습니다. 그러나 미들웨어를 구현하는 코드는 매우 복잡합니다. 미들웨어의 소스 코드 내용을 분석해 보겠습니다. php aritsan
是没有中间件机制的),同时也让系统的层次(Http过滤层)更明确,使用起来也很优雅。但实现中间件的代码却很复杂,下面就来具分析下有关中间件的源码的内容。
中间件本身分为两种,一种是所有http的,另一种则是针对route的。一个有中间件的请求周期是:Request得先经过Http中间件,才能进行Router,再经过Requset所对应Route的Route中间件, 最后才会进入相应的Controller代码。laravel把请求分为了两种:http和console。不同的请求方式用它自己的Kernel
来驱动Application
。Http请求则是通过IlluminateFoundationHttpKernel
类来驱动,它定义了所有的中间件,其父类IlluminateFoundationHttpKernel::handle
就是对请求进行处理的入口了
跟踪入口handle()
方法,很容易发现该函数(IlluminateFoundationHttpKernel::sendRequestThroughRouter
):
protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }
该函数会把Requset分发到Router(通过方法名就知道了), 主要的逻辑则是通过IlluminateRoutingPipeline
完成的, 作用就是让Requset通过Http中间件的检测,然后再到达Router。这里的代码看起来很优雅,但不是很好理解。所以,了解Pipeline
的运行机制就会明白中间件的使用。
Pipleline
基类是IlluminatePipelinePipeline
,它的执行在then
方法:
public function then(Closure $destination) { $firstSlice = $this->getInitialSlice($destination); $pipes = array_reverse($this->pipes); return call_user_func( array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable ); }
了解这段代码执行的意图,必须要知道array_reduce()做了什么。 为了清楚array_reduce
怎么运行的,先把array_reduce
重写一次:
//将数组中的元素,依次执行$func函数,且上一次的$func的返回值作为下一次调用$func的第一个参数输入 function array_reduce_back($arr, callable $func, $firstResult = null) { $result = $firstResult; foreach ($arr as $v) { $result = $func($result, $v); } return $result; }
所以,源代码中的$func
是getSlice()
,它返回的是一个回调函数:function($passable) use ($stack, $pipe){...}
($stack
和$pipe
被输入的具体值代替),也就是说作为上一次返回结果输入到下一次$func
的第一个参数是上述的回调函数,如此循环,当数组遍历完成,array_reduce
就返回的是一个回调函数,现在关键就是了解这个回调函数是什么样子,又如何执行?为方便讨论,可分析下面的代码:
call_user_func( array_reduce([1, 2, 3], $this->getSlice(), $firstSlice), $this->passable );
执行说明:
1.$result_0
是初始化的值 ,为$firstSlice
,即是IlluminatePipelinePipeline::getInitialSlice
的返回回调
2.每遍历一个元素,都会执行IlluminatePipelinePipeline::getSlice
的回调,同时也会返回一个回调
3.$result
中的具体执行代码都在getSlice()
中
4.最后的array_reduce
返回结果是$result_3
,是一个有多层闭包的回调函数
5.执行的是call_user_func($result_3, $this->passable)
,即执行function($this->passable) use ($result_2, 3){...}
至此已经清楚了then()
是如何运行的了,要继续下去,则需再搞定回调函数到底怎么执行的.现在再跟着sendRequestThroughRouter
中的Pipeline
走,看它是如何执行的。
// 把具体的参数带进来 return (new Pipeline($this->app)) ->send($request) ->through(['\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode']) ->then($this->dispatchToRouter());
用上面的所分析的Pipeline
执行过程,很快就会分析出最后执行的是
function($requset) use (\Illuminate\Foundation\Http\Kernel::dispatchToRouter(), '\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode') { if ($pipe instanceof Closure) { return call_user_func($pipe, $passable, $stack); } // $name和$parameters很容易得到 // $name = '\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode'; // $parameters = []; list($name, $parameters) = $this->parsePipeString($pipe); // 执行的就是\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::handle($request, \Illuminate\Foundation\Http\Kernel::dispatchToRouter()) return call_user_func_array([$this->container->make($name), $this->method], array_merge([$passable, $stack], $parameters)); }
逻辑处理已经到了IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::handle
,其代码是:
public function handle($request, Closure $next) { if ($this->app->isDownForMaintenance()) { throw new HttpException(503); } return $next($request); }
这里,它处理了这个中间件所需要过滤的条件,同时执行了$next($request)
,即IlluminateFoundationHttpKernel::dispatchToRouter()
, 这样,就把Request转到了Router中,也就完成了Http中间件的所有处理工作,而$next($request)
是每个中间件都不可少的操作,因为在回调中嵌套了回调,就是靠中间件把Request
传递到下一个回调中,也就会解析到下一个中间件,直到最后一个。紧跟上面的已分析的Pipeline
执行过程,讲其补充完整:
6.执行$result_3中的回调,getSlice
实例化中间件,执行其handle
,在中间件处理中执行回调
7.回调中还嵌套回调的,每个中间件中都需有执行回调的代码$next($request)
커널
을 사용하여 애플리케이션
을 구동합니다. Http 요청은 모든 미들웨어를 정의하는 IlluminateFoundationHttpKernel
클래스에 의해 구동되며 해당 상위 클래스인 IlluminateFoundationHttpKernel::handle
은 요청 처리를 위한 진입점입니다. h3>Http 미들웨어🎜handle()
항목 메서드를 추적하면 이 함수를 쉽게 찾을 수 있습니다( IlluminateFoundationHttpKernel::sendRequestThroughRouter): 🎜// 使用管道,发送$request,使之通过middleware ,再到$func (new Pipeline($this->app))->send($request)->through($this->middleware)->then($func);🎜이 함수는 Requset을 Router에 배포하며(메소드 이름으로 알 수 있음) 주요 로직은
IlluminateRoutingPipeline
을 통해 완성됩니다. 기능은 Requset이 Http 미들웨어 감지를 통과한 다음 라우터에 도달하도록 하는 것입니다. 여기의 코드는 우아해 보이지만 이해하기는 쉽지 않습니다. 따라서 Pipeline
의 작동 메커니즘을 이해하면 미들웨어의 사용법도 이해하게 됩니다. 🎜Pipleline
기본 클래스는 IlluminatePipelinePipeline
입니다. 실행은 then
메서드에서 이루어집니다. 🎜public function dispatchToRoute(Request $request) { // 找到具体的路由对象,过程略 $route = $this->findRoute($request); $request->setRouteResolver(function () use ($route) { return $route; }); // 执行Request匹配到Route的事件,具体的代码在这里:\Illuminate\Foundation\Providers\FoundationServiceProvider::configureFormRequests $this->events->fire(new Events\RouteMatched($route, $request)); // 这里就运行路由中间件了 $response = $this->runRouteWithinStack($route, $request); return $this->prepareResponse($request, $response); } protected function runRouteWithinStack(Route $route, Request $request) { // 获取该路由上的中间件 // 简单就点可这样写: // $middleware = App::shouldSkipMiddleware() ? [] : $this->gatherRouteMiddlewares($route); $shouldSkipMiddleware = $this->container->bound('middleware.disable') && $this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddlewares($route); // 了解Pipeline后,这里就好理解了,应该是通过管道,发送$request,经过$middleware,再到then中的回调 return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { return $this->prepareResponse( $request, $route->run($request) ); }); }🎜 이 코드 실행의 의도를 이해하려면 array_reduce()가 수행하는 작업을 알아야 합니다.
array_reduce
가 어떻게 작동하는지 이해하려면 먼저 array_reduce
를 다시 작성하세요. 🎜rrreee🎜그래서 소스 코드의 $func
는 getSlice( )
, 콜백 함수를 반환합니다: function($passable) use ($stack, $pipe){...}
($stack
및 $pipe
는 입력된 특정 값으로 대체됨) 즉, 마지막 반환 결과로 다음 $func
에 입력되는 첫 번째 매개변수가 위에서 언급한 콜백입니다. function 등입니다. 배열 순회가 완료되면 array_reduce
가 콜백 함수를 반환합니다. 이제 핵심은 이 콜백 함수의 모양과 실행 방법을 이해하는 것입니다. 논의를 촉진하기 위해 다음 코드를 분석할 수 있습니다. 🎜rrreee🎜🎜🎜Execution 지침 :$result_0
은 초기화된 값으로 $firstSlice
이며 IlluminatePipelinePipeline::getInitialSlice
의 반환 콜백입니다. >IlluminatePipelinePipeline::getSlice
의 콜백이 실행되고 콜백도 반환됩니다.$result
의 코드는 모두 getSlice()
array_reduce
반환 결과는 $result_3, 이는 다중 레이어 클로저 콜백 함수
5. 실행은 call_user_func($result_3, $this->passable)
, 즉 function($this->passable) use ($result_2 , 3){...}
🎜🎜이제 then()
이 어떻게 작동하는지 명확해졌습니다. 계속해서 콜백 함수가 어떻게 실행되는지 파악해야 합니다. 이제 sendRequestThroughRouter
의 Pipeline
을 따라 실행되는 방식을 확인하세요. 🎜rrreee🎜위에서 분석한 Pipeline
실행 프로세스를 사용하면 곧 마지막 실행이 🎜rrreee🎜인 것으로 분석됩니다. 논리적 처리가 IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::handle
에 도착했습니다. which 코드는 🎜rrreee🎜여기서 이 미들웨어에 필요한 필터링 조건을 처리하고 $next($request)
, 즉 IlluminateFoundationHttpKernel::dispatchToRouter()
를 실행합니다. >, 이렇게 Request가 Router로 전달되면서 Http 미들웨어의 모든 처리 작업이 완료되고 $next($request)
는 모든 미들웨어에 없어서는 안될 작업이기 때문입니다. 콜백은 콜백에 중첩됩니다. 즉, 미들웨어가 요청
을 다음 콜백에 전달하고 마지막 콜백까지 다음 미들웨어로 구문 분석됩니다. 위의 분석된 파이프라인
실행 프로세스를 따르고 완전히 추가합니다. 🎜🎜6. $result_3에서 콜백을 실행하고, getSlice
가 미들웨어를 인스턴스화하고 해당핸들, 미들웨어 처리에서 콜백 실행 🎜🎜7. 콜백도 콜백에 중첩되어 있으며, 각 미들웨어에는 콜백 <code>$next($request) code>을 실행하는 코드가 있어야 합니다. 콜백이 실행됩니다. 실행 순서는 3::handel, 2::handel, 1::handel, $first🎜입니다.<p>8.最里面一层,一定是传递给then()的参数,then执行的就是最后一步</p>
<p>9.执行的顺序是由数组中的最后一个,向前,到then()的参数,为了使其执行顺序是数组中的第一个到最后一个,再到then()中的参数,then()方法中就做了一个反转<code>array_reverse
现在,Pipeline的所有执行流程就都分析完了。实现代码真的很绕,但理解之后编写自定义的中间件应该就很容易了。现在再把Pipeline
的使用翻译成汉语,应该是这样的
// 使用管道,发送$request,使之通过middleware ,再到$func (new Pipeline($this->app))->send($request)->through($this->middleware)->then($func);
这样的代码不管是从语义上,还是使用上都很优雅,高!确实是高!再回到源码,Requset的流程就通过dispatchToRouter
进入到了Router
在Router中,\Illuminate\Routing\Router::dispatch
就承接了来自Http中间件的Requset, Router把Request分发到了具体的Route,再进行处理,主要代码如下:
public function dispatchToRoute(Request $request) { // 找到具体的路由对象,过程略 $route = $this->findRoute($request); $request->setRouteResolver(function () use ($route) { return $route; }); // 执行Request匹配到Route的事件,具体的代码在这里:\Illuminate\Foundation\Providers\FoundationServiceProvider::configureFormRequests $this->events->fire(new Events\RouteMatched($route, $request)); // 这里就运行路由中间件了 $response = $this->runRouteWithinStack($route, $request); return $this->prepareResponse($request, $response); } protected function runRouteWithinStack(Route $route, Request $request) { // 获取该路由上的中间件 // 简单就点可这样写: // $middleware = App::shouldSkipMiddleware() ? [] : $this->gatherRouteMiddlewares($route); $shouldSkipMiddleware = $this->container->bound('middleware.disable') && $this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddlewares($route); // 了解Pipeline后,这里就好理解了,应该是通过管道,发送$request,经过$middleware,再到then中的回调 return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { return $this->prepareResponse( $request, $route->run($request) ); }); }
如何获取Route中间件的,就可以跟gatherRouteMiddlewares
,这个代码并不难,很好跟。接下来,Request就到到达至于Controller, Request是如何到达Controller的代码就不难了,这里就不说了
成功获取Response后,在public/index.php
58行执行了$kernel->terminate($request, $response);
, 也就是在主要逻辑处理完成之后,再执行此代码,它实际上调用是的\Illuminate\Foundation\Http\Kernel::terminate
, 跟进去就很容易发现,它处理了这此请求所涉及到的中间件,并执行了各自的terminate
方法,到这里,中间件的另一个功能就展现出来了,就是主要逻辑完成之后的收尾工作.到这里为止,中间件就完成了它的使命(一个请求也就完成了)
在官方文档上讲解的很清楚注册中间
至此,中间件的实现逻辑与使用就清晰了.从执行的顺序来分,一个在Controller
之前,一个在Controller
之后,所以它一个很重要的作用就是可以让Controller
专注于自己的主要逻辑的职责更明确. 奇怪的是,但前后两种中间件的执行方式却不一样, \Illuminate\Foundation\Http\Kernel::terminate
,中间件的结束却没有使用Pipeline
, 而是直接foreach
.相同的工作却用两种代码来实现.现在看来,中间件本身并不复杂,但它带给了我两个启发,1.层次明确 2,Pipeline
所带来的优雅.
相关推荐:
Laravel 5.1框架中如何创建自定义Artisan控制台命令
위 내용은 laravel5.2 기반 미들웨어 소스코드 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!