老死机带你深入理解 Laravel 之验证器上(控制器参数解析)

前言

今天是2019年12月30号了,哈哈,旧的一年即将过去,新的一年即将来临。老司机继续带你在装逼的道路上越走越远,没错,我们要做一个硬核的装逼侠。

准备工作

由于我现在讲解的每一部分内容都不是独立存在的,与前面讲解的内容有关联,所以我希望你至少阅读过一下2篇博文,这是必要的:

  1. Laravel 之路由分发
  2. Laravel 之路由匹配

代码讲解

我们直接进入到Illuminate\Routing\Router类的:

老死机带你深入理解Laravel之验证器

直接调用dispatchToRoute方法,很简单:

老死机带你深入理解Laravel之验证器

调用runRoute方法:

老死机带你深入理解Laravel之验证器

继续调用runRouteWithinStack方法:

老死机带你深入理解Laravel之验证器

上面贴出的代码,我已经在Laravel 之路由分发Laravel 之路由匹配讲解过多次了,不熟悉的可以再次回去看一遍,不在多讲,我们直接看$route->run(),即调用
Illuminate\Routing\Route类的run方法:

老死机带你深入理解Laravel之验证器

这里我们只关心路由中指定控制器的情况,假如说我在web.php文件中,我定义了一个路由:

Route::get('/debug/args/{id}/{name}', 'Admin\DebugController@args');

请大家记住这个路由,后面我们会用到,注意了我在路由中指定了2个参数,分别是idname,控制器是DebugController,动作是args。关于DebugController的定义如下:

class  DebugController extends Controller
{

    public function args(DebugFormRequest $request, $name, $id)
    {
        dd($name, $id);
    }

}

这里先不多说,我们回到Illuminate\Routing\Route类的run方法,继续调用runController方法:

老死机带你深入理解Laravel之验证器

这里的controllerDispatcher方法返回的是Illuminate\Routing\ControllerDispatcher类的对象,关于这个我也在之前的2篇博文中说过了,我们进入到它的dispatch方法:

老死机带你深入理解Laravel之验证器

上面我标出来的就是今天分析的重点,$route->parametersWithoutNulls()返回的当前路由的参数,比如对于前面定义的路由,当我访问它时,http://localhost/debug/args/2/DennisRitche,此时我打印它的结果:

老死机带你深入理解Laravel之验证器

这个很好理解,接下来,我们继续分析resolveClassMethodDependencies方法,这个方法属于Illuminate\Routing\RouteDependencyResolverTrait 这个trait的,因为当前的ControllerDispatcher使用到了这个trait,我们来看它:

老死机带你深入理解Laravel之验证器

我们进入到resolveClassMethodDependencies方法中:

老死机带你深入理解Laravel之验证器

这个方法用于解析当前控制器方法的依赖性参数,1开始检测控制器是否存在这个方法,2才真正开始解析参数依赖,我们进入到resolveMethodDependencies方法中,这里有必要提醒一下,如果你对PHP的反射不清楚的话,请查看我之前写的这篇详解 PHP 反射的基本使用

老死机带你深入理解Laravel之验证器

这个方法本身并不复杂,但是我不得不吐槽Laravel自身设计上的一点坑,为啥我怎么说,当我访问/debug/args/2/DennisRitche这个路由的时候,控制器的打印结果如下:

老死机带你深入理解Laravel之验证器

但是,关键的问题是,这不是我想要的,因为我的控制器方法是这么定义的:

老死机带你深入理解Laravel之验证器

所以打印的结果并不是我想要的,至于原因就在这个resolveMethodDependencies方法中,在这个方法中,循环遍历$reflector->getParameters()返回的参数,每一个参数都是ReflectionParameter类的对象,transformDependency用于检测当前的参数是不是一个类对象,如果是的话,就返回一个这个类的对象:

老死机带你深入理解Laravel之验证器

getClass方法获取参数的类名,接下来的代码,就是根据它的类名创建对象,当然了首先还是判断它是否存在默认值,返回到resolveMethodDependencies方法中,我们看,这段代码可能有些迷惑,我仔细给大家分析:

老死机带你深入理解Laravel之验证器

首先is_null判断transformDependency返回的值是不是为空,如果不为空的话,$instanceCount值加1,这个值表示解析出的实例数,
$key表示当前的参数在参数中的位置,以DebugControllerargs方法为例:

老死机带你深入理解Laravel之验证器

因为第一个参数$requestDebugFormRequest类的对象,所以此时$key为0,因此$instanceCount的值为1。说到这里,有必要再说下spliceIntoParameters方法,这个方法很简单,但是在当前的分析中,很有必要解释哈:

老死机带你深入理解Laravel之验证器

方法array_splice会在$parameters$offset位置插入一个元素,插入的值是$value
我们看此时插入$request的时候,查看结果:

老死机带你深入理解Laravel之验证器

返回到resolveMethodDependencies接续迭代下一个参数,此时的参数为$name,关键的问题来了,代码会进入到elseif语句中,在分析它的代码前,我们来看哈,$values的值为:

老死机带你深入理解Laravel之验证器

控制台打印:

老死机带你深入理解Laravel之验证器

好了,我们进入到elseif语句中,! isset($values[$key - $instanceCount])这个判断语句是啥意思呢?前面我们说了$instanceCount表示解析出的实例数,而$key表示当前参数在控制器方法所有参数中的位置,这个地方表明Laravel会在所有的路由参数中依次取值,而不是按参数名进行取值,这就是我之前给大家说的Laravel坑,这就是为啥当我打印控制器参数的时候,$name值为2,$id参数为"DennisRitche"。如果! isset($values[$key - $instanceCount])的返回值为false,表示路由中无法解析出这个参数值,$parameter->isDefaultValueAvailable()检查参数是否存在默认值,所以总结下elseif的判断条件意思就是,当路由中不存在这个参数的时候,检查这个参数是否存在默认值,如果存在的话,$parameter->getDefaultValue()会获取默认值并把它存储到$parameters的相对应位置。

好了,resolveMethodDependencies这里关键的地方已经给大家解释了。

解析完所有的参数之后,我们回到Illuminate\Routing\ControllerDispatcherdispatch方法中:

老死机带你深入理解Laravel之验证器

1检测当前的控制器是否存在callAction方法,如果存在就调用它,如果不存在就直接调用对应的控制器方法。

总结

今天的这篇博文,我简要的给大家讲述了Laravel是如何解析控制器方法的参数的,这是下一篇给大家讲解Laravel验证器的基础条件,希望大家能理解,欢迎加入qq群:

本作品采用《CC 协议》,转载必须注明作者和本文链接

如果有不懂的地方,可以加我的qq:1174332406,或者是微信:itshardjs

本帖由系统于 4个月前 自动加精
Dennis_Ritchie
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 4

不错 清晰易懂 :relaxed: :+1:

4个月前 评论

先mark后看.

4个月前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!