Laravel将PHP的反射API用于多个组件。其中,inverson-of control (IoC) 依赖注入容器和控制器方法注入对开发人员来说最为明显。
为了更清楚地说明反射的用法,下面是一个大大简化的例程 Laravel 的 IoC 容器类版本,用于通过构造函数注入来构建对象的依赖项:
function build($className)
{
$reflector = new ReflectionClass($className);
$constructor = $reflector->getConstructor();
foreach ($constructor->getParameters() as $dependency) {
$instances[] = build($dependency->getClass()->name);
}
return $reflector->newInstanceArgs($instances);
}
正如我们所看到的,这个概念并不难理解。容器使用 PHP 的 ReflectionClass
在对象的构造函数中查找类的名称,然后以递归方式遍历其中每个名称,以创建依赖关系树中每个对象的实例。使用这些实例,最后实例化原始类并将依赖项作为参数传递给构造函数。build()
控制器方法注入使用上面显示的相同容器功能来解析声明为方法参数的依赖项的实例,但是需要一些额外的逻辑来将类依赖项与路由参数分开:
function dispatch(Route $route, Controller $controller, $methodName)
{
$routeParameters = $route->parametersWithoutNulls();
$method = new ReflectionMethod($controller, $methodName);
foreach ($method->getParameters() as $index => $parameter) {
$class = $parameter->getClass();
if ($class !== null) {
$instance = build($class->name);
array_splice($routeParameters, $index, 0, [ $instance ]);
}
}
$controller->callAction($methodName, $routeParameters);
}
同样,这种适应被精简,以突出反射所扮演的角色,并依赖于我们前面显示的功能。ControllerDispatcher
类使用 PHP 的 ReflectionMethod
的 getParameters()
方法来确定控制器方法需要哪些参数,然后遍历这些参数以查找表示它可以从容器中解析的依赖项的参数。然后,它将找到的每个依赖项拼接回路由参数数组中,并将这些参数传递回为路由定义的控制器方法。有关详细信息,请参阅 RouteDependencyResolverTrait
。build()
如果我们忽略应用程序引导过程,则当 Laravel 将请求映射到路由时,通常会为请求启动此依赖关系注入级联,然后确定要将请求传递给哪个控制器。Laravel首先从容器中解析控制器的实例,该实例构建出任何构造函数注入的依赖项。然后,Laravel 找到合适的控制器方法,并根据需要解析参数的任何更多依赖关系。
如图所示,Laravel使用相对简单的技术来使用反射来实现这些工具。但是,与本答案中显示的示例不同,该框架添加了大量附加代码,以使它们像今天一样健壮和灵活。