zoukankan      html  css  js  c++  java
  • laravel kernel解析过程

    laravel kernel解析过程

    前面的两篇laravel文章过后,可以在bootstrap/app.php中拿到$app这个实例,

    app.php中 接下来通过singleton方法绑定了三个闭包(闭包代表未完成解析,需要在使用到的时候动态解析)到容器中。

    然后将$app返回到index.php中

    <?php
    
    $app = new IlluminateFoundationApplication(
        $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
    );
    
    $app->singleton(
        IlluminateContractsHttpKernel::class,
        AppHttpKernel::class
    );
    
    $app->singleton(
        IlluminateContractsConsoleKernel::class,
        AppConsoleKernel::class
    );
    
    $app->singleton(
        IlluminateContractsDebugExceptionHandler::class,
        AppExceptionsHandler::class
    );
    
    return $app;
    
    • 在index.php中可以看到尝试解析了IlluminateContractsHttpKernel::class
    $kernel = $app->make(IlluminateContractsHttpKernel::class);
    
    // 重走一遍解析时的路 之前介绍过 make方法调用的是resolve方法,最终通过build拿到闭包直接产生类或者通过php提供的反射api进行动态解析,最终返回需要的实例。
    
    Container中的resolve方法
    protected function resolve($abstract, $parameters = [], $raiseEvents = true)
    {	
        1 // 联系前文getAlias返回的还是$abstract本身,也就是IlluminateContractsHttpKernel
        $abstract = $this->getAlias($abstract);
    
        $needsContextualBuild = !empty($parameters) || !is_null(
            $this->getContextualConcrete($abstract)
        );
    
        if (isset($this->instances[$abstract]) && !$needsContextualBuild) {
            return $this->instances[$abstract];
        }
    
        $this->with[] = $parameters;
    	
        // 获取concrete实例 发现得到一个闭包 该闭包是app.php中通过singleton绑定生成的 其中调用了resolve方法,具体可以查看前文的singleton->bind->getClosure方法
        $concrete = $this->getConcrete($abstract);
    	
        // isBuildable只要concrete是闭包 恒真
        3 // 跳转回来isBuildable方法 此时$concrete == $abstract 同样调用build方法,再次跳转到build方法
        if ($this->isBuildable($concrete, $abstract)) {
            // 跳转到build方法
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }
        
        foreach ($this->getExtenders($abstract) as $extender) {
            $object = $extender($object, $this);
        }
    
        if ($this->isShared($abstract) && !$needsContextualBuild) {
            $this->instances[$abstract] = $object;
        }
    
        if ($raiseEvents) {
            $this->fireResolvingCallbacks($abstract, $object);
        }
    
        $this->resolved[$abstract] = true;
    
        array_pop($this->with);
    
        return $object;
    }
    
    
    // build方法
    public function build($concrete)
    {	
        2 // 此时传递进来的是 返回AppHttpKernel的闭包
        if ($concrete instanceof Closure) {
            // 走进这个分支 如果是闭包直接调用 前面说过这个闭包保存的是resolve方法 其中的concrete是AppHttpKernel类名,所以在此相当于调用$app->resolve(AppHttpKernel),又回到了resolve方法
            return $concrete($this, $this->getLastParameterOverride());
        }
    	
        4 // 解析AppHttpKernel 用到了大量的php反射api 请自行查阅手册
        try {
            $reflector = new ReflectionClass($concrete);
        } catch (ReflectionException $e) {
            throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
        }
    
        if (!$reflector->isInstantiable()) {
            return $this->notInstantiable($concrete);
        }
    
        $this->buildStack[] = $concrete;
    
        $constructor = $reflector->getConstructor();
    	
        // 若没有构造函数,表示不需要继续解析依赖了,直接返回了实例
        if (is_null($constructor)) {
            array_pop($this->buildStack);
            return new $concrete;
        }
    	
        // 获取依赖
        $dependencies = $constructor->getParameters();
    
        try {
            // 解析依赖 跳转到resolveDependencies方法
            $instances = $this->resolveDependencies($dependencies);
        } catch (BindingResolutionException $e) {
            array_pop($this->buildStack);
    
            throw $e;
        }
    
        array_pop($this->buildStack);
    	
        // 返回解析好依赖的实例
        return $reflector->newInstanceArgs($instances);
    }
    
    
    // 此方法循环解析要解析的依赖并返回,通过反射进行实例的返回,从而完成实例从容器中的解析
    protected function resolveDependencies(array $dependencies)
    {
        $results = [];
    	// $dependencies通过php原生反射机制得到的对应类的构造方法中的依赖,针对此AppHttpKernel得到应该如下
        // array (size=2)
        //   0 => 
        //     object(ReflectionParameter)[32]
        //     public 'name' => string 'app' (length=3)
        //  1 => 
        //     object(ReflectionParameter)[33]
        //     public 'name' => string 'router' (length=6)
        /**
         * AppHttpKernel的构造方法长这样
         *
         * @param  IlluminateContractsFoundationApplication  $app
         * @param  IlluminateRoutingRouter  $router
         * @return void
         */
        // public function __construct(Application $app, Router $router)
        // {
        //     $this->app = $app;
        //     $this->router = $router;
        //     $this->syncMiddlewareToRouter();
        // }
        
        foreach ($dependencies as $dependency) {
            if ($this->hasParameterOverride($dependency)) {
                $results[] = $this->getParameterOverride($dependency);
    
                continue;
            }
    		
            // 因为AppHttpKernel的构造方法中存在类型提示,所以getClass返回的不是null
            // 从而走到resolveClass方法中
            $results[] = is_null($dependency->getClass())
                ? $this->resolvePrimitive($dependency)
                // 跳转到resolveClass方法
                : $this->resolveClass($dependency);
        }
    
        return $results;
    }
    
    protected function resolveClass(ReflectionParameter $parameter)
    {
        try {
            // 通过getClass方法获取实例的类型约束。在此递归调用make方法,直到返回所有的依赖,从而通过newInstanceArgs(deps)获得从容器中解析的实例
            5 // 再次跳到make->resolve方法 针对AppHttpKernel 传递的两个依赖的type hint为
              // IlluminateContractsFoundationApplication
              // IlluminateRoutingRouter
              // Application解析返回的是 $app->instances['app'] 在registerBaseBindings绑定的
              // Router解析返回的是$app->bindings['router']闭包  在registerBaseServiceProvider中注册的
              // 前文都有提到
              // 至此解析除了AppHttpKernel类,得到了laravel传说中的‘黑盒子’
            return $this->make($parameter->getClass()->name);
        }
    
        // If we can not resolve the class instance, we will check to see if the value
        // is optional, and if it is we will return the optional parameter value as
        // the value of the dependency, similarly to how we do this with scalars.
        catch (BindingResolutionException $e) {
            if ($parameter->isOptional()) {
                return $parameter->getDefaultValue();
            }
    
            throw $e;
        }
    }
    

    本文和之前内容存在重复,通过laravel实际的解析例子再熟悉下解析流程,发现错误欢迎指点。

  • 相关阅读:
    43前端
    42 前端
    python 列表
    python 字符串方法
    python while语句
    zhy2_rehat6_mysql02
    zhy2_rehat6_mysql01
    bay——安装_Oracle 12C-RAC-Centos7.txt
    bay——RAC_ASM ORA-15001 diskgroup DATA does not exist or is not mounted.docx
    bay——Oracle RAC集群体系结构.docx
  • 原文地址:https://www.cnblogs.com/alwayslinger/p/13409177.html
Copyright © 2011-2022 走看看