【问题标题】:How does Reflection in Laravel work?Laravel 中的反射是如何工作的?
【发布时间】:2017-11-09 11:20:01
【问题描述】:

Laravel 中的反射实际上是如何工作的?

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

但我发现它很难,而且看甚至理解 50% 的内容都非常复杂。从一个班级跳到另一个班级,我真的看不到它。试了几次调试,结果理解不高。

这一点和反思给我留下了深刻的印象,Laravel 使用它的方式让我心潮澎湃——真是太美了。我希望完全理解——整个过程——大体上,一步一步地。

从到达路线开始到最终拥有dd($x),其中$x 来自方法参数并且是TestClass,它具有TestClass2 的另一个依赖项,应该通过以下方式构造:@987654326 @

我认为这些都是美妙的力学和架构,理解这一点是我非常想要的。

我的问题是:Laravel 中的反射实际上是如何工作的?


这不是关于dd 伙计们...假设没有dd。正如我之前所说的——当我们从class method 实例化这个对象时。这不是要倾倒它,而是要从 method injectionreflection 获得它。

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

【问题讨论】:

  • 您在这里提出了一个非常广泛的问题,而这不是解决 How do this work... 风格问题的最佳论坛。 dd 本身使用反射来输出有关对象的私有信息。依赖注入层使用它递归地检查目标类的依赖关系。这些都不是 Laravel 特有的——大多数用于转储变量或 DI 的包都会做类似的事情。
  • @iainn 编辑了我的问题
  • Laravel 不仅仅是反射。引擎盖下发生了很多事情。从阅读这些文章开始:alanstorm.com/category/laravel/#container
  • @Hamoud 我知道。这就是为什么这个问题也很宝贵
  • @CyRossignol 可能是关于“将类依赖项与路由参数分开所需的一些额外逻辑”

标签: php laravel reflection inversion-of-control


【解决方案1】:

Laravel 将 PHP 的 reflection API 用于多个组件。其中,控制反转 (IoC) dependency injection container控制器 method injection 对开发人员最为可见。

为了更清楚地说明反射的使用,这是 Laravel 的 IoC container class 用于通过构造函数注入构建对象依赖关系的例程的戏剧性简化版本:

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

同样,这种改编被精简以突出反射所扮演的角色,并依赖于我们之前显示的build() 函数。 ControllerDispatcher 类使用 PHP 的 ReflectionMethodgetParameters() 方法来确定控制器方法需要哪些参数,然后遍历这些参数以查找表示它可以从容器解析的依赖项的参数。然后,它将找到的每个依赖项拼接回路由参数数组,然后传递回为路由定义的控制器方法。详情请见RouteDependencyResolverTrait

如果我们忽略应用程序的引导过程,这种依赖注入级联通常会在 Laravel 将请求映射到路由时为请求启动,然后确定将请求传递给哪个控制器。 Laravel 首先从容器中解析控制器的一个实例,该实例构建出任何构造函数注入的依赖项。然后,Laravel 找到合适的控制器方法,并根据需要解析参数的更多依赖关系。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-05
    • 2015-07-25
    • 2018-07-18
    • 1970-01-01
    • 2015-08-10
    • 1970-01-01
    相关资源
    最近更新 更多