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()方法 }
1、2兩步都是在構建例項,第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原始碼,有任何問題歡迎討論。