以 yii 2.0.14 高級版的 frontend 爲例,從 frontend/web/index.php 開始php
//引用 yii2 composer 的 autoload,調用 getLoader require __DIR__ . '/../../vendor/autoload.php'; //引用 yii.php require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php'; //引用 bootstrap.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' ); (new yii\web\Application($config))->run();
入口文件看着就這麼幾行,簡單的很,那他是怎麼經過這幾行來運行應用的呢?先看 Yii.php 內的邏輯html
/** * Yii::autoload 內執行過程 * 一、先查看類是否在 Yii::$classMap 中存在,存在直接調用 getAlias 生成類文件物理地址 * 二、若是 Yii::$classMap 中不存在,將命名空間轉爲實際路徑調用 getAlias 生成類文件物理地址 */ spl_autoload_register(['Yii', 'autoload'], true, true); //yii2 核心類的類名和物理文件地址映射的 hash 數組 Yii::$classMap = require __DIR__ . '/classes.php'; /** * 實例化 依賴注入(Dependency Injection,DI)容器 * 依賴注入容器知道怎樣初始化並配置對象及其依賴的全部對象 * 在Yii中使用DI解耦,有2種注入方式:構造函數注入、屬性注入 * yii\di\Container 繼承了 * yii\base\Component * yii\base\BaseObject * BaseObject 實現了 Configurable * DI容器只支持 yii\base\Object 類 * 若是你的類想放在DI容器裏,那麼必須繼承自 yii\base\Object 類 * 參考地址: * http://www.digpage.com/di.html * https://www.cnblogs.com/minirice/p/yii2_configurations.html */ Yii::$container = new yii\di\Container();
接下來,就是重頭戲,yii\web\Application,它繼承了
yii\base\Application
yii\base\Module
yii\di\ServiceLocator(服務定位器)
yii\base\Component
yii\base\BaseObject, BaseObject 實現 Configurable
PS:繼承 Component 的都有 on event 和 as behavior 配置實現事件綁定web
public function __construct($config = []) { Yii::$app = $this; //application 對象放到註冊樹中 static::setInstance($this); $this->state = self::STATE_BEGIN; /** * 初始化 application 中應用屬性的一些值,配置一些高優先級的應用屬性 * 還會初始化 components 中,log、user、urlManager 對應的類文件 * foreach ($this->coreComponents() as $id => $component) { * if (!isset($config['components'][$id])) { * $config['components'][$id] = $component; * } elseif ( * is_array($config['components'][$id]) * && !isset($config['components'][$id]['class']) * ) { * $config['components'][$id]['class'] = $component['class']; * } * } * * yii\web\Application 中,coreComponents 的代碼 * public function coreComponents() * { * return array_merge(parent::coreComponents(), [ * 'request' => ['class' => 'yii\web\Request'], * 'response' => ['class' => 'yii\web\Response'], * 'session' => ['class' => 'yii\web\Session'], * 'user' => ['class' => 'yii\web\User'], * 'errorHandler' => ['class' => 'yii\web\ErrorHandler'], * ]); * } * * yii\base\Application 中,coreComponents 的代碼 * public function coreComponents() * { * return [ * 'log' => ['class' => 'yii\log\Dispatcher'], * 'view' => ['class' => 'yii\web\View'], * 'formatter' => ['class' => 'yii\i18n\Formatter'], * 'i18n' => ['class' => 'yii\i18n\I18N'], * 'mailer' => ['class' => 'yii\swiftmailer\Mailer'], * 'urlManager' => ['class' => 'yii\web\UrlManager'], * 'assetManager' => ['class' => 'yii\web\AssetManager'], * 'security' => ['class' => 'yii\base\Security'], * ]; * } * * 從2.0.11 開始,配置支持使用 container 屬性來配置依賴注入容器 * 'container' => [ * 'definitions' => [ * 'yii\widgets\LinkPager' => ['maxButtonCount' => 5] * ], * 'singletons' => [ * // 依賴注入容器單例配置 * ] * ] * * */ $this->preInit($config); /** * registerErrorHandler 內代碼 * 一、調用 $this->set('errorHandler', $config['components']['errorHandler']) * 將 errorHandler 配置放到 ServiceLocator (_definitions 數組中,這時還沒實例化) * 二、調用 $this->getErrorHandler()->register() * 調用 getErrorHandler,使用 createObject 調用 Container 依賴注入容器實例化對象 * 調用 yii\web\ErrorHandler::register,初始化錯誤異常顯示和拋出 */ $this->registerErrorHandler($config); /** * 在多層繼承中,調用上級某一層的構造函數,而不是單純的父類構造函數 * 上級某一層的構造函數中若是調用了某個方法 * 而且這個方法被下層類重寫過,那麼會直接執行重寫以後的方法 * 因此執行 Component::__construct,__construct 中調用 init() * 會執行 yii\base\Application 的 init * 若是上級調用下級重寫的 靜態方法 時 * 要使用延時靜態綁定(上級靜態調用 self::a() 改成 static::a()) */ Component::__construct($config); }
public function init() { $this->state = self::STATE_INIT; $this->bootstrap(); }
protected function bootstrap() { /** * 經過 Application::get('request') * 使用 createObject 實現調用 Container 依賴注入容器實例化對象 */ $request = $this->getRequest(); //定義別名 Yii::setAlias('@webroot', dirname($request->getScriptFile())); Yii::setAlias('@web', $request->getBaseUrl()); //調用 yii\base\Application::bootstrap 代碼 parent::bootstrap(); }
一、是否在配置文件中配置了 extensions 參數,若是沒有配置,直接加載擴展清單文件 @vendor/yiisoft/extensions.php,不然使用配置的 extensions。而後在 extensions 文件返回的數組中,可有含有 alias 和 bootstrap 參數,根據 alias 中的參數定義別名,根據 bootstrap 中的參數,使用 createObject 實例化對象(建立並運行各個擴展聲明的 引導組件 )
二、根據配置文件配置的 bootstrap 參數,使用 createObject 實例化對象(建立並運行各個 應用組件 以及在應用的 bootstrap 屬性中聲明的各個 模塊組件 )
三、注意:extensions 文件中配置的 bootstrap 和 配置文件中配置的 bootstrap,若是實現了 BootstrapInterface 接口,還會執行實例化後的 bootstrap 方法
四、注意:bootstrap 會直接將配置的類實例化,而不是在第一次使用的時候實例化,因此爲了性能考慮 bootstrap 中的配置應該儘可能少,並且只配置一些全局使用的類bootstrap
public function run() { try { $this->state = self::STATE_BEFORE_REQUEST; /** * trigger 觸發通知,將此事件通知給綁定到這個事件的觀察者,綁定事件的方法: * yii\base\Component 或者其子類::on("事件名稱","方法") */ $this->trigger(self::EVENT_BEFORE_REQUEST); $this->state = self::STATE_HANDLING_REQUEST; $response = $this->handleRequest($this->getRequest()); $this->state = self::STATE_AFTER_REQUEST; $this->trigger(self::EVENT_AFTER_REQUEST); $this->state = self::STATE_SENDING_RESPONSE; $response->send(); $this->state = self::STATE_END; return $response->exitStatus; } catch (ExitException $e) { $this->end($e->statusCode, isset($response) ? $response : null); return $e->statusCode; } }
public function handleRequest($request) { if (empty($this->catchAll)) { try { //resolve 方法調用 urlManager 對 url 進行解析 list($route, $params) = $request->resolve(); } catch (UrlNormalizerRedirectException $e) { $url = $e->url; if (is_array($url)) { if (isset($url[0])) { $url[0] = '/' . ltrim($url[0], '/'); } $url += $request->getQueryParams(); } return $this->getResponse()->redirect(Url::to($url, $e->scheme), $e->statusCode); } } else { /** * 若是設置了 catchAll 變量, 那麼全部請求都會跳轉到這裏 * 示例: * 假設網站維護, 須要將網站重定向到一個設置好的頁面上 * 能夠在配置文件中添加 * 'catchAll' => ['offline/index'] * 這樣, 全部的訪問都跳轉到 offline/index 頁面了 */ $route = $this->catchAll[0]; $params = $this->catchAll; unset($params[0]); } try { Yii::debug("Route requested: '$route'", __METHOD__); $this->requestedRoute = $route; //根據 route 訪問對應的 module/controller/action $result = $this->runAction($route, $params); if ($result instanceof Response) { return $result; } $response = $this->getResponse(); if ($result !== null) { $response->data = $result; } return $response; } catch (InvalidRouteException $e) { throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'), $e->getCode(), $e); } }
public function runAction($route, $params = []) { /** * yii\base\Module::createController 代碼也不貼了,能夠追進去看,思路是 * 一、若是 route 是空(直接經過域名訪問應用 www.aaa.com) * 使用配置中的 defaultRoute 屬性 * 二、route 不爲空,查看配置文件中是否有 controllerMap 的配置 * 直接使用配置建立 * controllerMap 配置如 * [ * 'controllerMap' => [ * // 用類名申明 "account" 控制器 * 'account' => 'app\controllers\UserController', * // 用配置數組申明 "article" 控制器 * 'article' => [ * 'class' => 'app\controllers\PostController', * 'enableCsrfValidation' => false, * ] * ] * ] * * 三、調用 yii/base/Module::getModule 查看 route 中是否有 module 存在 * 若是直接調用yii/base/Module::createController 方法 * 不然調用 yii/base/Module::createControllerByID * 經過 createControllerByID 實例化的 Controller 類,必須繼承 yii\base\Controller * createController 和 createControllerByID 都使用 Yii::createObject 實例化 */ $parts = $this->createController($route); if (is_array($parts)) { list($controller, $actionID) = $parts; $oldController = Yii::$app->controller; Yii::$app->controller = $controller; $result = $controller->runAction($actionID, $params); if ($oldController !== null) { Yii::$app->controller = $oldController; } return $result; } $id = $this->getUniqueId(); throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".'); }
一、先看一下配置文件時 modules 配置後的賦值過程
咱們使用 modules 時,須要在配置文件中配置 modules,好比swift
'modules' => [ 'v1' => [ 'class' => 'frontend\modules\v1\Module', ], ],
或者像 main-local.php 中那樣,新建一個 $config,配置完之後 return $config,$config 中配置數組
$config['modules']['gii'] = [ 'class' => 'yii\gii\Module', ];
這個 modules 的屬性,在 Application 及其父類中,都是不存在的
只有私有屬性 $_modules,存在於 yii\base\Module 類中
當 new yii\web\Application 執行 yii\base\Application::construct 方法時
方法中執行了 Component::construct($config) (不清楚的往上看,上邊有這塊代碼)
而後 Component::construct($config) 實際執行的是
BaseObject::construct($config) ,而後方法中執行yii2
if (!empty($config)) { Yii::configure($this, $config); }
再調用 yii\base\Component::setter 方法 (yii\base\Module::setModules),將 $_modules 賦值
二、若是 module 套着 module,須要這麼這麼設置session
'modules' => [ 'v1' => [ 'class' => 'frontend\modules\v1\Module', 'modules' => [ 'v2' => 'frontend\modules\v2\Module' ], ], ],
public function runAction($id, $params = []) { /** * yii\base\Controller::createAction 代碼也不貼了,能夠追進去看,思路是 * 一、若是 action id 是空(訪問 www.aaa.com/controller) * 使用 yii\base\Controller 中的 defaultAction 屬性 * * 二、id 不爲空,查看 Controller::actions 方法中是否有配置 * 若是有,直接使用配置建立,actions 配置如 * * public function actions() * { * return [ * 'error' => [ * 'class' => 'yii\web\ErrorAction', * ], * 'captcha' => [ * 'class' => 'yii\captcha\CaptchaAction', * 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, * ], * ]; * } * * 三、利用反射(ReflectionMethod)查看調用方法是否存在,是不是公共方法 * 若是是,返回 yii\base\InlineAction 的實例 */ $action = $this->createAction($id); if ($action === null) { throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id); } Yii::debug('Route to run: ' . $action->getUniqueId(), __METHOD__); if (Yii::$app->requestedAction === null) { Yii::$app->requestedAction = $action; } $oldAction = $this->action; $this->action = $action; $modules = []; $runAction = true; //調用全部加載模塊中的 beforeAction 方法 foreach ($this->getModules() as $module) { if ($module->beforeAction($action)) { array_unshift($modules, $module); } else { $runAction = false; break; } } $result = null; if ($runAction && $this->beforeAction($action)) { $result = $action->runWithParams($params); $result = $this->afterAction($action, $result); //調用全部加載模塊中的 afterAction 方法 foreach ($modules as $module) { $result = $module->afterAction($action, $result); } } if ($oldAction !== null) { $this->action = $oldAction; } return $result; }
最後,附個圖,源自
http://www.yiichina.com/doc/guide/2.0/structure-applicationsapp
Detect languageAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBengaliBosnianBulgarianCatalanCebuanoChichewaChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEsperantoEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekGujaratiHaitian CreoleHausaHebrewHindiHmongHungarianIcelandicIgboIndonesianIrishItalianJapaneseJavaneseKannadaKazakhKhmerKoreanLaoLatinLatvianLithuanianMacedonianMalagasyMalayMalayalamMalteseMaoriMarathiMongolianMyanmar (Burmese)NepaliNorwegianPersianPolishPortuguesePunjabiRomanianRussianSerbianSesothoSinhalaSlovakSlovenianSomaliSpanishSundaneseSwahiliSwedishTajikTamilTeluguThaiTurkishUkrainianUrduUzbekVietnameseWelshYiddishYorubaZulu |
|
AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBengaliBosnianBulgarianCatalanCebuanoChichewaChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEsperantoEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekGujaratiHaitian CreoleHausaHebrewHindiHmongHungarianIcelandicIgboIndonesianIrishItalianJapaneseJavaneseKannadaKazakhKhmerKoreanLaoLatinLatvianLithuanianMacedonianMalagasyMalayMalayalamMalteseMaoriMarathiMongolianMyanmar (Burmese)NepaliNorwegianPersianPolishPortuguesePunjabiRomanianRussianSerbianSesothoSinhalaSlovakSlovenianSomaliSpanishSundaneseSwahiliSwedishTajikTamilTeluguThaiTurkishUkrainianUrduUzbekVietnameseWelshYiddishYorubaZulu |
|
|
|
|
|