思考并回答以下问题:
前言
经过前面一系列中间件的工作,现在请求终于要达到了正确的控制器方法了。本篇文章主要讲Laravel如何调用控制器方法,并且为控制器方法依赖注入构建参数的过程。
路由控制器的调用
我们前面已经解析过中间件的搜集与排序、pipeline的原理,接下来就要进行路由的run运行函数:
1 |
|
路由的run函数主要负责路由控制器方法与路由闭包函数的运行:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24namespace Illuminate\Routing;
class Route
{
/**
* Run the route action and return the response.
*
* @return mixed
*/
public function run()
{
$this->container = $this->container ?: new Container;
try {
if ($this->isControllerAction()) {
return $this->runController();
}
return $this->runCallable();
} catch (HttpResponseException $e) {
return $e->getResponse();
}
}
}
路由的运行主要靠ControllerDispatcher这个类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Route
{
/**
* Checks whether the route's action is a controller.
*
* @return bool
*/
protected function isControllerAction()
{
return is_string($this->action['uses']);
}
/**
* Run the route action and return the response.
*
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function runController()
{
return $this->controllerDispatcher()->dispatch(
$this, $this->getController(), $this->getControllerMethod()
);
}
}
1 | namespace Illuminate\Routing; |
上面可以很清晰地看出,控制器的运行分为两步:解析函数参数、调用callAction
解析控制器方法参数
解析参数的功能主要由ControllerDispatcher类的RouteDependencyResolverTrait这一trait负责:
1 |
|
控制器方法函数参数构造难点在于,参数来源有三种:
- 路由参数赋值
- IoC容器自动注入
- 函数自带默认值
在IoC容器自动注入的时候,要保证路由的现有参数中没有相应的类,防止依赖注入覆盖路由绑定的参数:
1 | /** |
由IoC容器构造出的参数需要插入到原有的路由参数数组中:
1 | if (! is_null($instance)) |
当路由的参数数组与IoC容器构造的参数数量不足以覆盖控制器参数个数时,就要去判断控制器是否具有默认参数:
1 | elseif (! isset($values[$key - $instanceCount]) && $parameter->isDefaultValueAvailable()) |
调用控制器方法callAction
所有的控制器并非是直接调用相应方法的,而是通过callAction函数再分配,如果实在没有相应方法还会调用魔术方法__call() :
1 | namespace Illuminate\Routing; |
路由闭包函数的调用
路由闭包函数的调用与控制器方法一样,仍然需要依赖注入,参数构造:
1 |
|