Laravel中的反思如何运作?

时间:2021-03-07 16:24:00

How does reflection in Laravel actually work?

Laravel中的反射如何实际起作用?

I tried to debug it to see how Laravel uses reflection in a controller's constructor or methods to resolve their dependencies and sub-dependencies and then and give it back to us.

我试图调试它,看看Laravel如何在控制器的构造函数或方法中使用反射来解析它们的依赖关系和子依赖关系,然后将它还给我们。

But I found it hard, and it's very complicated to see and to even understand 50% of. Jumping from class to class, I can't really see it. I tried a few times by debugging it with low results of understanding.

但我觉得很难,而且看到甚至理解50%的人都很复杂。从一个班级跳到另一个班级,我真的看不到它。我尝试了几次,理解结果很差。

I am very impressed by this and by reflection, and the way Laravel uses it makes my heart burn—it's just beautiful. And I wish to fully understand that—the whole process—in general, and step by step.

我对这一点和反思印象深刻,而Laravel使用它的方式让我的心脏燃烧 - 它只是美丽的。我希望完全理解 - 整个过程 - 一般而言,一步一步。

Beginning from hitting the route to finally having, let's say, dd($x), where $x is from a method argument and is a TestClass that has another dependency of TestClass2 that should be constructed through: $x = new TestClass(new TestClass2());

从命中路径开始到最终拥有,比方说,dd($ x),其中$ x来自方法参数,是一个TestClass,它具有另一个应该通过以下构造的TestClass2依赖:$ x = new TestClass(new TestClass2 ());

I think those are beautiful mechanics and architecture, and understanding this is something I want so badly.

我认为那些是美丽的机制和建筑,理解这是我非常想要的东西。

So again, my question is: how does reflection in Laravel actually work?

再说一次,我的问题是:Laravel的反思实际上是如何运作的?


It's not about dd guys... Let's say without dd. Just as I said earlier - when we have this object instantiated from the class method. It's not about dumping it, it's just about having it from method injection by reflection.

这不是关于dd家伙......让我们说没有dd。正如我之前所说 - 当我们从类方法实例化此对象时。这不是关于倾销它,而是通过反射方法注入它。

The dd was only an example. It can even be die(var_dump()); and it will work

dd只是一个例子。它甚至可以死(var_dump());它会起作用

1 个解决方案

#1


33  

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使用PHP的反射API来处理几个组件。其中,开发人员最容易看到控制反转(IoC)依赖注入容器和控制器方法注入。

To more clearly illustrate the use of reflection, here's a dramatically simplified version of the routine Laravel's IoC container class uses to build up an object's dependencies through constructor injection:

为了更清楚地说明反射的使用,这里是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);
}

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.

我们可以看到,这个概念并不难理解。容器使用PHP的ReflectionClass在对象的构造函数中查找类的名称,然后递归遍历每个名​​称以创建依赖关系树中每个对象的实例。使用这些实例,build()最终实例化原始类,并将依赖项作为参数传递给构造函数。

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);
}

Again, this adaptation is slimmed-down to highlight the role reflection plays and relies on our build() function shown previously. The ControllerDispatcher class uses the getParameters() method of PHP's ReflectionMethod to determine which parameters a controller method expects, and then loops through these to find parameters that represent dependencies that it can resolve from the container. Then, it splices each dependency it finds back into the array of route parameters that it passes back to the controller method defined for the route. See RouteDependencyResolverTrait for details.

同样,这种改编是精简的,以突出反射角色扮演的角色,并依赖于我们之前显示的build()函数。 ControllerDispatcher类使用PHP的ReflectionMethod的getParameters()方法来确定控制器方法所期望的参数,然后循环遍历这些参数以查找表示它可以从容器中解析的依赖项的参数。然后,它将它找到的每个依赖项拼接回路由参数数组,并将其传递回为路由定义的控制器方法。有关详细信息,请参阅RouteDependencyResolverTrait。

If we ignore the application bootstrapping process, this dependency injection cascade typically starts for a request when Laravel maps a request to a route, and then determines which controller to pass the request to. Laravel first resolves an instance of the controller from the container, which builds out any constructor-injected dependencies. Then, Laravel finds the appropriate controller method and resolves any more dependencies for the arguments as needed.

如果我们忽略应用程序引导过程,当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.

如此处所示,Laravel使用相对简单的技术来使用反射来实现这些工具。但是,与本答案中显示的示例不同,该框架添加了许多额外的代码,使它们像现在一样强大和灵活。

#1


33  

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使用PHP的反射API来处理几个组件。其中,开发人员最容易看到控制反转(IoC)依赖注入容器和控制器方法注入。

To more clearly illustrate the use of reflection, here's a dramatically simplified version of the routine Laravel's IoC container class uses to build up an object's dependencies through constructor injection:

为了更清楚地说明反射的使用,这里是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);
}

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.

我们可以看到,这个概念并不难理解。容器使用PHP的ReflectionClass在对象的构造函数中查找类的名称,然后递归遍历每个名​​称以创建依赖关系树中每个对象的实例。使用这些实例,build()最终实例化原始类,并将依赖项作为参数传递给构造函数。

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);
}

Again, this adaptation is slimmed-down to highlight the role reflection plays and relies on our build() function shown previously. The ControllerDispatcher class uses the getParameters() method of PHP's ReflectionMethod to determine which parameters a controller method expects, and then loops through these to find parameters that represent dependencies that it can resolve from the container. Then, it splices each dependency it finds back into the array of route parameters that it passes back to the controller method defined for the route. See RouteDependencyResolverTrait for details.

同样,这种改编是精简的,以突出反射角色扮演的角色,并依赖于我们之前显示的build()函数。 ControllerDispatcher类使用PHP的ReflectionMethod的getParameters()方法来确定控制器方法所期望的参数,然后循环遍历这些参数以查找表示它可以从容器中解析的依赖项的参数。然后,它将它找到的每个依赖项拼接回路由参数数组,并将其传递回为路由定义的控制器方法。有关详细信息,请参阅RouteDependencyResolverTrait。

If we ignore the application bootstrapping process, this dependency injection cascade typically starts for a request when Laravel maps a request to a route, and then determines which controller to pass the request to. Laravel first resolves an instance of the controller from the container, which builds out any constructor-injected dependencies. Then, Laravel finds the appropriate controller method and resolves any more dependencies for the arguments as needed.

如果我们忽略应用程序引导过程,当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.

如此处所示,Laravel使用相对简单的技术来使用反射来实现这些工具。但是,与本答案中显示的示例不同,该框架添加了许多额外的代码,使它们像现在一样强大和灵活。