摘要:至此,返回本次请求的响应。最内层即路由分发,解析请求,返回响应的操作代码展示,后续分析即执行匿名函数,位于下面方法中。
Laravel 中间件 代码展示
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()); // $this->dispatchToRouter(),后期继续 } new IlluminateRoutingPipeline($this->app): public function __construct(Container $container = null) { $this->container = $container; } public function send($passable) { $this->passable = $passable; return $this; } public function through($pipes) { #################################################################################### # $this->middleware = [ # # IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class, # # IlluminateFoundationHttpMiddlewareValidatePostSize::class, # # AppHttpMiddlewareTrimStrings::class, # # IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class, # # ]; # #################################################################################### $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this; } // 中间件的本质 public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) ); // 上面操作完之后返回一个匿名函数,接受一个参数 return $pipeline($this->passable); } // 注意这个是子类里面会调用的父carry,区别在于子类加入了异常处理 protected function carry() { // 接受匿名函数$stack参数和$pipe参数,返回匿名函数,再将此匿名函数作为第一个参数$stack迭代传入。 return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { if ($pipe instanceof Closure) { return $pipe($passable, $stack); } elseif (! is_object($pipe)) { list($name, $parameters) = $this->parsePipeString($pipe); $pipe = $this->getContainer()->make($name); $parameters = array_merge([$passable, $stack], $parameters); } else { $parameters = [$passable, $stack]; } // 中间件作用是提供了一种方便的机制来过滤进入应用的 HTTP 请求 // $this->method默认为handle,可通过via方法进行设置 return $pipe->{$this->method}(...$parameters); }; }; } protected function parsePipeString($pipe) { // 不带参数的pipe(class)或带参数(参数以,分割)的pipe(class:param1,param2,param3...) list($name, $parameters) = array_pad(explode(":", $pipe, 2), 2, []); if (is_string($parameters)) { $parameters = explode(",", $parameters); } // $parameters为[]数组或[param1,param2,param3...] return [$name, $parameters]; } protected function prepareDestination(Closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; }
前置条件
array_reduce接受三个参数,第一个参数接收数组,第二个参数函数名(也可以是匿名函数,函数有两个参数,分别代表$result和$item),第三个参数(可选),该参数将被当成是数组中的第一个值来处理,或者如果数组为空的话就作为最终返回值。
匿名函数也叫闭包函数(closures),允许临时创建一个没有指定名称的函数,通过 Closure 类来实现的。
当对闭包函数进行赋值时,PHP 便会自动将此种表达式转换成内置类 Closure 的对象对象在进行赋值。
当闭包函数使用到 use 使用外部数据时,会在 Closure 对象生成一个 static 属性数组进行存放。
当闭包函数使用到参数时,会在 Closure 对象生成一个 parameter 属性数组进行存放。
public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) ); return $pipeline($this->passable); }
生成最终匿名函数的过程:
array_reduce执行第一次时得到如下简化的匿名函数返回,将会继续作为第一个参数进行迭代: object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#1 (0) { // $this->prepareDestination($destination) } ["pipe"]=> string(15) "IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull" } } 第二次: object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#1 (0) { // $this->prepareDestination($destination) } ["pipe"]=> string(15) "IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull" } } ["pipe"]=> string(15) "AppHttpMiddlewareTrimStrings" } } 第三次: object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#1 (0) { // $this->prepareDestination($destination) } ["pipe"]=> string(15) "IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull" } } ["pipe"]=> string(15) "AppHttpMiddlewareTrimStrings" } } ["pipe"]=> string(15) "IlluminateFoundationHttpMiddlewareValidatePostSize" } }
依次类推,最终得到一个匿名函数如下(接受一个参数,此匿名函数内部使用上面递归形式的$pipe和$stack)。
function ($passable) { if ($pipe instanceof Closure) { return $pipe($passable, $stack); } elseif (! is_object($pipe)) { list($name, $parameters) = $this->parsePipeString($pipe); $pipe = $this->getContainer()->make($name); // 实例化middleware $parameters = array_merge([$passable, $stack], $parameters); } else { $parameters = [$passable, $stack]; } return $pipe->{$this->method}(...$parameters); // 语法糖模式,因为middleware参数可有可无 };
最终匿名函数的调用过程(从最外层开始,这就是前面为什么要array_reverse,一层一层往里拨,整体上的处理是:$pipe若是匿名函数,直接调用。若是字符串,则解析成对应的类和参数,make类,再组成参数数组。最后调用$pipe->handle)
最外层($pipe=IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode): (new IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode)->handle($passable, $stack) 执行完前置操作后,调用$stack($passable),继续进行下一层 下一层($pipe=IlluminateFoundationHttpMiddlewareValidatePostSize): (new IlluminateFoundationHttpMiddlewareValidatePostSize)->handle($passable, $stack) 执行完前置操作后,调用$stack($passable),继续进行下一层 **以此类推,当每层中间件的前置任务全部完成,即递归执行到最里面一层(路由分发,解析请求,返回响 应),再由最内层一层一层往回走,执行每层中间件的后置任务。至此,返回本次请求的响应。**
最内层(即路由分发,解析请求,返回响应)的操作代码展示,后续分析:即执行$destination($passable)匿名函数,位于下面方法中。
protected function prepareDestination(Closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; } $destination: protected function dispatchToRouter() { return function ($request) { $this->app->instance("request", $request); return $this->router->dispatch($request); }; }
说明:
所有的后置操作,都是执行到最内层,递归往回走时才会执行逐层执行。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/22602.html
摘要:又限于层的内容太多,我在这篇中将整理路由中间件控制器部分内容。前者定义页面路由,默认应用中间件组后者定义无状态路由,会应用中间件组。命名路由可以为指定路由或者控制器方法命名,也可以为已命名的路由生成。 showImg(https://segmentfault.com/img/remote/1460000010882838); 上图列出了 Laravel HTTP 层的相关知识大纲。由于...
摘要:而我的新轮子也并不是专门解决它的问题的,而是顺便解决而已。概述这个包,支持在所有的项目中使用。一旦出现成员,代表允许全部。列出允许跨域请求的方法列表,默认是代表所有方法。信息地址嗯,新轮子,求一波。 showImg(https://segmentfault.com/img/bV5VxN?w=844&h=656); 是的,可能了解 Laravel 的都知道,在 Laravel 中简单的设...
摘要:中的中间件是中的一个重点本篇将从源码的角度去讲解中的中间件洞察中的中间件是如何运行的明白为何我们使用中间件的时候要进行那些步骤本篇文章假设读者已经掌握中间件的基本用法如果不了解其用法可以移步查看中间件的使用我们都知道使用中间件有三个步骤使用 Laravel中的中间件是laravel中的一个重点,本篇将从源码的角度去讲解Lravel中的中间件,洞察Laravel中的中间件是如何运行的,明...
摘要:直到所有中间件都执行完毕,最后在执行最后的即上述的方法如果上述有地方难懂的,可以参考这边文章内置函数在中的使用以上是在通过全局中间件时的大致流程,通过中间件和路由中间件也是一样的,都是采用管道流操作,详情可翻阅源码 简介 Laravel 中间件提供了一种方便的机制来过滤进入应用的 HTTP 请求, 如ValidatePostSize用来验证POST请求体大小、ThrottleReque...
摘要:合适和够用是最完美的追求。比如从页面去请求的资源。它允许浏览器向跨源服务器,发出请求,从而克服了只能同源使用的限制。定义在中的路由都是无状态的,并且会应用中间件组。 关于作者 程序开发人员,不拘泥于语言与技术,目前主要从事PHP和前端开发,使用Laravel和VueJs,App端使用Apicloud混合式开发。合适和够用是最完美的追求。 个人网站:http://www.linganm...
摘要:合适和够用是最完美的追求。比如从页面去请求的资源。它允许浏览器向跨源服务器,发出请求,从而克服了只能同源使用的限制。定义在中的路由都是无状态的,并且会应用中间件组。 关于作者 程序开发人员,不拘泥于语言与技术,目前主要从事PHP和前端开发,使用Laravel和VueJs,App端使用Apicloud混合式开发。合适和够用是最完美的追求。 个人网站:http://www.linganm...
阅读 4932·2021-11-25 09:43
阅读 1185·2021-11-24 09:38
阅读 1891·2021-09-30 09:54
阅读 2799·2021-09-23 11:21
阅读 2366·2021-09-10 10:51
阅读 2367·2021-09-03 10:45
阅读 1163·2019-08-30 15:52
阅读 1765·2019-08-30 14:13