laravel5.8生命周期与源码分析

laravel的http请求入口是public目录下的index.php。首先从这个文件进行分析,代码流程走完以后,会出一个逻辑流程图:<?php//定义LARAVEL_START常量define('LARAVEL_START', microtime(true));//使用composer的自动加载机制(后续分析自动加载是如何实现的)require __DIR__.'...
摘要由CSDN通过智能技术生成

laravel的http请求入口是public目录下的index.php。

首先从这个文件进行分析,代码流程走完以后,会出一个逻辑流程图:

<?php


//定义LARAVEL_START常量
define('LARAVEL_START', microtime(true));

//使用composer的自动加载机制(后续分析自动加载是如何实现的)
require __DIR__.'/../vendor/autoload.php';

//创建laravel应用
$app = require_once __DIR__.'/../bootstrap/app.php';

//获取Http内核
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//处理请求
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
//将请求结果返回给客户端
$response->send();
//调用http内核的terminate方法,销毁资源
$kernel->terminate($request, $response);

进入当前目录下的bootstrap/app.php文件中,代码段如下:

<?php

//实例化Application,以base_path作为参入传递给构造函数
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);


//绑定三种接口的实现 ,在http服务中,只会用到第一种接口的实现
//第二种和第三种是命令行模式和调试模式的接口实现。
//为什么要绑定,因为laravel使用的应用容器模式实现类的依赖注入,为了降低代码的耦合,
//依赖类常常使用接口类或者抽象类作为参数,通过绑定接口类(抽象类)和具体接口的实现,当从容器中
//获取该接口的实现时,通过类型提示即可获取该接口的具体实现,降低了代码的耦合。

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
//
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

return $app;

找到 Illuminate\Foundation\Application,该类继承自Container(laravel最重要的容器类)

也就是说,一个laravel应用就是一个laravel容器

其构造函数代码如下:

    public function __construct($basePath = null)
    {
        //设置应用基础目录
        if ($basePath) {
            $this->setBasePath($basePath);
        }
        //绑定基础类
        $this->registerBaseBindings();
        //注册基础服务提供者。
        $this->registerBaseServiceProviders();
        //设置框架核心类的短名。
        $this->registerCoreContainerAliases();
    }
    
    public function setBasePath($basePath)
    {
        $this->basePath = rtrim($basePath, '\/');

        //绑定路径到容器中。
        $this->bindPathsInContainer();

        return $this;
    }

    protected function bindPathsInContainer()
    {
        //$this->path(),$this->basePath()等都是返回框架设定的目录,以全路径形式返回。
        $this->instance('path', $this->path());
        $this->instance('path.base', $this->basePath());
        $this->instance('path.lang', $this->langPath());
        $this->instance('path.config', $this->configPath());
        $this->instance('path.public', $this->publicPath());
        $this->instance('path.storage', $this->storagePath());
        $this->instance('path.database', $this->databasePath());
        $this->instance('path.resources', $this->resourcePath());
        $this->instance('path.bootstrap', $this->bootstrapPath());
    }

当前实例的instance方法,代码段如下

    public function instance($abstract, $instance)
    {

        /*removeAbstractAlias  方法
        if (! isset($this->aliases[$searched])) {
            return;
        }

        foreach ($this->abstractAliases as $abstract => $aliases) {
            foreach ($aliases as $index => $alias) {
                if ($alias == $searched) {
                    unset($this->abstractAliases[$abstract][$index]);
                }
            }
        }

        */
        //removeAbstractAlias从方法名命名来解释:移除抽象别名
        //上面的实现为:删除当前容器的abstractAliases数组中保存的所有以$abstract为
        //键名的所有成员,保证注册的唯一性。
        $this->removeAbstractAlias($abstract);
         /*bound 方法
               return isset($this->bindings[$abstract]) ||
               isset($this->instances[$abstract]) ||
               $this->isAlias($abstract);
        */
        //在bindings/instances/aliases中,如果有一个存在,则该返回值为真。
        $isBound = $this->bound($abstract);

        //删除aliases中保存的当前abstract对应的值。
        unset($this->aliases[$abstract]);

        //保存进instances数组,(instances:实例,实体)。
        $this->instances[$abstract] = $instance;

        if ($isBound) { 
            /*rebound
           $instance = $this->make($abstract);
           foreach ($this->getReboundCallbacks($abstract) as $callback) {
              call_user_func($callback, $this, $instance);
            }
            */
            //若$isBound为真,也就是曾经向容器中保存过该$abstract
            //则需要重新make(实际调用的是resolve方法)一个实体出来,
            //然后调用rebound注册的回调函数
            //重新触发或者通知相关依赖类
            $this->rebound($abstract);
        }
        
        return $instance;
    }
resolve方法代码段:
 protected function resolve($abstract, $parameters = [], $raiseEvents = true)
    {
        //getAlias方法是一个递归调用的方法,用于获取已经保存在
        //aliases数组中该别名的最终对应abstract。
        $abstract = $this->getAlias($abstract);
        
        //从上下文关系中可以推断出,$needsContextualBuild的值为空
        $needsContextualBuild = (! empty($parameters) || ! is_null(
            $this->getContextualConcrete($abstract)
        ));
        //如果在单例管理数组中查找到该abstract的对应绑定,且不需要重新构建上下文,则
        //直接返回该instance(实例)
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
            return $this->instances[$abstract];
        }
        //当前迭代层参数入栈。
        $this->with[] = $parameters;
        //获取该abstract的具体实现
        //从bindings/contextual中获取该abstract的具体实现,如果未获取到,则返回其自身
        $concrete = $this->getConcrete($abstract);
        //判断该concrete 是否可以build,
        //$concrete与$abstract相等,$concrete 是一个闭包(匿名函数),二者满足其一即可。
        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            //否则调用make(递归调用resolve),非闭包,且是一个接口的实现。
            $object = $this->make($concrete);
        }
        //对已经生成的object调用extend中已经注册的函数,扩展作用于该object之上
        //可以改变其object的属性
        foreach ($this->getExtenders($abstract) as $extender) {
            $object = $extender($object, $this);
        }
        //该abstract的shared属性是否为真,且不需要重新构建上下文,如果都为真,
        //则将该object保存进
        //容器管理的单例集中去。
        if ($this->isShared($abstract) && ! $needsContextualBuild) {
            $this->instances[$abstract] = $object;
        }
        //该参数默认为true,调用resolve所注册的callback
        if ($raiseEvents) {
            $this->fireResolvingCallbacks($abstract, $object);
        }
        //将当前abstract的resolved属性设置为true
        $this->resolved[$abstract] = true;
        //当前迭代层的参数出栈
        array_pop($this->with);
        //返回该object
        return $object;
    }

getContextualConcrete方法代码段: 

    protected function getContextualConcrete($abstract)
    {
        //若有直接找到该abstract的binding,则直接诶返回
        if (! is_null($binding = $this->findInContextualBindings($abstract))) {
            return $binding;
        }
        //检查在abstractAliases中是否有该abstract的相关绑定,若无,直接返回
        if (empty($this->abstractAliases[$abstract])) {
            return;
        }
        //遍历abstractAliases中该abstract所有的alias,调用findInContextualBindings
        foreach ($this->abstractAliases[$abstract] as $alias) {
            if (! is_null($binding = $this->findInContextualBindings($alias))) {
                return $binding;
            }
        }
    }

    /**
     * Find the concrete binding for the given abstract in the contextual binding array.
     * 在上下文绑定数组中找到给定抽象的具体绑定。
     * @param  string  $abstract
     * @return \Closure|string|null
     */
    protected function findInContextualBindings($abstract)
    {
        //end:输出数组中的最后一个元素的值:
        //buildStack是当前容器的虚拟栈,用于在递归调用make或者build
        //时的函数参数信息上下文。
        if (isset($this->contextual[end($this->buildStack)][$abstract])) {
            return $this->contextual[end($this->buildStack)][$abstract];
        }
    }

从上面代码实现可以看出,resolve最终都是要调用build生成object的,先看build方法,后面在分析在什么情况下会递归调用make(resolve)进行实例生成。

build方法代码段:

    public function build($concrete)
    {
        //如果传参试一个闭包(匿名函数),则直接调用该闭包,并返回
        if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }

        //新建反射类 ,反射类可以获取该类的一些基本参数。
        $reflector = new ReflectionClass($concrete);
        //如果类不可以实例化,则给上层抛出一个异常。
        if (! $reflector->isInst
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值