1. 程式人生 > >Yii2應用的執行過程

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=yii\helpers\ArrayHelper::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' ); (newyii\web\Application($config))->run();

從換行上看,可以分為4個部分,第一部分表示是否開啟除錯模式和開發模式,一般在開發模式要這樣設定。第二部分引入第三方類載入器、Yii的類載入器,通用模組需要啟動的元件、以及前端模組需要啟動的元件,第三部分是合併通用、前端模組的主要、本地配置檔案,一般後者會覆蓋前者的配置,如果存在相同配置項的話。第四部分是啟動應用,這裡以匿名類的方式啟動了yii\web\Application,並且傳入了配置引數,然後呼叫它的run()方法來執行APP生命週期所定義的幾個方法,APP的 開始->初始化->請求之前事件->處理請求->請求之後事件->傳送響應->結束。好,這就是一個Yii應用執行的過程。

 

具體執行過程我們從run()方法入手來一點點剝洋蔥。

<?php

// yii\base\Application

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

// yii\base\Application

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

// yii\base\Application

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原始碼,有任何問題歡迎討論。