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实际的解析例子再熟悉下解析流程,发现错误欢迎指点。

原文地址:https://www.cnblogs.com/alwayslinger/p/13409177.html