Yii2应用的运行过程

每一个框架都有一个入口脚本,Yii2也不例外。一般来说,对于Web应用的入口脚本是YiiBasePath/frontend/web目录下的index.php

先观察这个文件:

<?php

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require__DIR__.'/../../vendor/autoload.php'; require__DIR__.'/../../vendor/yiisoft/yii2/Yii.php'; require__DIR__.'/../../common/config/bootstrap.php'; require__DIR__.'/../config/bootstrap.php'; $config=yiihelpersArrayHelper::merge( require__DIR__.'/../../common/config/main.php', require__DIR__.'/../../common/config/main-local.php', require__DIR__.'/../config/main.php', require__DIR__.'/../config/main-local.php' ); (newyiiwebApplication($config))->run();

从换行上看,可以分为4个部分,第一部分表示是否开启调试模式和开发模式,一般在开发模式要这样设置。第二部分引入第三方类加载器、Yii的类加载器,通用模组需要启动的组件、以及前端模组需要启动的组件,第三部分是合并通用、前端模组的主要、本地配置文件,一般后者会覆盖前者的配置,如果存在相同配置项的话。第四部分是启动应用,这里以匿名类的方式启动了yiiwebApplication,并且传入了配置参数,然后调用它的run()方法来执行APP生命周期所定义的几个方法,APP的 开始->初始化->请求之前事件->处理请求->请求之后事件->发送响应->结束。好,这就是一个Yii应用执行的过程。

具体执行过程我们从run()方法入手来一点点剥洋葱。

<?php

// yiiaseApplication

public

function run()
{
    try {
        $this->state = self::STATE_BEFORE_REQUEST; // 1.应用开始,设置为开始状态
        $this->trigger(self::EVENT_BEFORE_REQUEST); // 2.触发请求之前的事件,当请求发过来后就会触发
$this->state = self::STATE_HANDLING_REQUEST; // 3.设置为正在处理请求的状态 $response = $this->handleRequest($this->getRequest()); // 3.处理请求,得到一个响应(结果)
$this->state = self::STATE_AFTER_REQUEST; // 4.设置为请求之后的状态 $this->trigger(self::EVENT_AFTER_REQUEST); // 4.触发请求之后的事件
$this->state = self::STATE_SENDING_RESPONSE; // 5.设置为正在发送响应的状态 $response->send(); // 5.发送响应
$this->state = self::STATE_END; // 6.应用结束,设置为结束状态
return $response->exitStatus; // 返回应用推出状态码 } catch(ExitException $e) { $this->end($e->statusCode, isset($response) ? $response : null); return $e->statusCode; } }

通过解析发现少了一个步骤,关于应用的初始化,那是因为在构建Application对象的时候还执行了以下语句:

<?php

// yiiaseApplication

publicfunction__construct($config = []) {
    Yii::$app = $this; // 1.Yii的静态成员$app指向Application对象
    static ::setInstance($this); // 2.设置当前请求的Application实例
    $this->state = self::STATE_BEGIN; // 3.设置Application的状态为开始
    $this->preInit($config); // 4.执行预备初始化
    $this->registerErrorHandler($config); // 5.注册一个错误处理组件
    Component::__construct($config); // 6.调用BaseObject的构造方法,Component继承自BaseObject,它会设置配置参数以及调用APP的init()方法
}

12两步都是在构建实例,第4步是APP的预备初始化,第6步是调用祖先类的初始化方法,它也包括init()方法的初始化。好,我们重点关注第4步,预备初始化。

<?php

// yiiaseApplication

public
function preInit(&$config) //这是一个引用类型的形参,不用复制$config数据

{
    if (!isset($config['id'])) { // 1.id配置项是必须的
        throw new InvalidConfigException('The "id" configuration for the Applicationis required.');
    }

    if (isset($config['basePath'])) { // 2.如果存在basePath参数,这个参数也是必须的
        $this->setBasePath($config['basePath']); //则设置这个参数
        unset($config['basePath']); //并删除$config中的那个参数
    }
    else {
        throw new InvalidConfigException('The "basePath" configuration for the Applicationis required.');
    }

    if (isset($config['vendorPath'])) { // 3.如果存在第三方组件目录
        $this->setVendorPath($config['vendorPath']); //设置它
        unset($config['vendorPath']); //从$config中删除它
    }
    else {

        // set"@vendor"

        $this->getVendorPath(); //否则设置一个默认vendor目录,一般在basePath下面
    }

    if (isset($config['runtimePath'])) { // 4.如果存在一个runtime目录
        $this->setRuntimePath($config['runtimePath']); //设置它到这个APP
        unset($config['runtimePath']); //然后从$config中删除它
    }
    else {

        // set"@runtime"

        $this->getRuntimePath(); //否则设置一个默认runtime目录,一般在basePath目录下面
    }

    if (isset($config['timeZone'])) { // 5.是否有时区字段
        $this->setTimeZone($config['timeZone']); //如果有则设置它
        unset($config['timeZone']); //然后从$config中删除它
    }
    elseif (!ini_get('date.timezone')) { //检查php.ini配置文件中是否设置了时区项
        $this->setTimeZone('UTC'); //如果没有设置则初始化为国际标准时区
    }

    if (isset($config['container'])) { // 6.检查是否有服务容器字段
        $this->setContainer($config['container']); //如果有则设置它
        unset($config['container']); //然后从$config中删除它
    }

    // merge core components with custom components

    foreach($this->coreComponents() as $id => $component) { // 7.合并核心组件与自定义组件
        if (!isset($config['components'][$id])) { //如果核心组件在自定义组件中不存在(去重)
            $config['components'][$id] = $component; //把核心组件加入到$config中
        }
        elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {
            $config['components'][$id]['class'] = $component['class']; //把核心组件的类加入到自定义组件中
        }
    }
}

不难发现,它总共设置了7个属性,分别是应用ID,应用basePah,三方组件目录,runtime目录,时区,服务容器,自定义组件合并(组件)。

回到Application::__construct()方法的第6步,这里主要是调用了祖先类BaseObject的构造方法,它主要做了两个操作,一个是Yii::configure($this, $config),一个是执行init()方法,简单说就是后初始化。

关于后初始化的内容介绍在我的另一篇博文,《Yii2应用的初始化》。

初次阅读Yii2源代码,有任何问题欢迎讨论。

  

原文地址:https://www.cnblogs.com/ranwuer/p/9818032.html