Laravel uses PHP's reflection API for several components. Of these, the inverson-of-control (IoC) dependency injection container and controller method injection are most visible to developers.
为了更清楚地说明反射的使用,下面是常规Laravel的IoC container class个使用的dramatically个简化版本,通过构造函数注入来建立对象的依赖关系:
function build($className)
{
$reflector = new ReflectionClass($className);
$constructor = $reflector->getConstructor();
foreach ($constructor->getParameters() as $dependency) {
$instances[] = build($dependency->getClass()->name);
}
return $reflector->newInstanceArgs($instances);
}
As we can see, the concept isn't too difficult to understand. The container uses PHP's ReflectionClass
to find the names of the classes in an object's constructor, and then loops through each of these names recursively to create instances of each object in the dependency tree. With these instances, build()
finally instantiates the original class and passes the dependencies as arguments to the constructor.
Controller method injection uses the same container functionality shown above to resolve instances of dependencies declared as method parameters, but there's a bit of extra logic needed to separate class dependencies from route parameters:
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);
}
同样,这个改编是精简的,以突出反射所起的作用,并依赖于我们前面显示的build()
功能.ControllerDispatcher
类使用PHP ReflectionMethod
的getParameters()
方法来确定控制器方法需要哪些参数,然后遍历这些参数以查找表示它可以从容器解析的依赖项的参数.然后,它将找到的每个依赖项拼接回路由参数数组中,并将这些参数传递回为该路由定义的控制器方法.有关详细信息,请参见RouteDependencyResolverTrait
.
如果我们忽略应用程序 bootstrap 过程,则当Laravel将请求映射到路由,然后确定将请求传递到哪个控制器时,通常会为请求启动此依赖项注入级联.Laravel首先从容器解析控制器的一个实例,这将构建任何构造函数注入的依赖项.然后,Laravel找到适当的控制器方法,并根据需要解析参数的任何更多依赖项.
As shown here, Laravel uses relatively simple techniques to implement these tools using reflection. However, unlike the examples shown in this answer, the framework adds a lot of additional code to make them as robust and flexible as they are today.