2019独角兽企业重金招聘Python工程师标准>>>

以 yii 2.0.14 高级版的 frontend 为例,从 frontend/web/index.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 内的逻辑

/*** Yii::autoload 内执行过程* 1、先查看类是否在 Yii::$classMap 中存在,存在直接调用 getAlias 生成类文件物理地址* 2、如果 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 配置实现事件绑定

一、new yii\web\Application 时,会调用构造方法 yii\base\Application::__construct

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 内代码* 1、调用 $this->set('errorHandler', $config['components']['errorHandler']) * 将 errorHandler 配置放到 ServiceLocator (_definitions 数组中,这时还没实例化)* 2、调用 $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);
}

二、yii\base\Application::init 代码

public function init()
{$this->state = self::STATE_INIT;$this->bootstrap();
}

三、yii\web\Application::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();
}

四、yii\base\Application::bootstrap 代码太多,不展示源码了,大致总结为

1、是否在配置文件中配置了 extensions 参数,如果没有配置,直接加载扩展清单文件 @vendor/yiisoft/extensions.php,否则使用配置的 extensions。然后在 extensions 文件返回的数组中,可有含有 alias 和 bootstrap 参数,根据 alias 中的参数定义别名,根据 bootstrap 中的参数,使用 createObject 实例化对象(创建并运行各个扩展声明的 引导组件 )
2、根据配置文件配置的 bootstrap 参数,使用 createObject 实例化对象(创建并运行各个 应用组件 以及在应用的 bootstrap 属性中声明的各个 模块组件 )
3、注意:extensions 文件中配置的 bootstrap 和 配置文件中配置的 bootstrap,如果实现了 BootstrapInterface 接口,还会执行实例化后的 bootstrap 方法
4、注意:bootstrap 会直接将配置的类实例化,而不是在第一次使用的时候实例化,所以为了性能考虑 bootstrap 中的配置应该尽量少,而且只配置一些全局使用的类

五、yii\base\Application::run 代码

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;}
}

六、yii\web\Application::handleRequest 代码

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);}
}

七、yii\base\Module::runAction 代码

public function runAction($route, $params = [])
{/*** yii\base\Module::createController 代码也不贴了,可以追进去看,思路是* 1、如果 route 是空(直接通过域名访问应用 www.aaa.com)* 使用配置中的 defaultRoute 属性* 2、route 不为空,查看配置文件中是否有 controllerMap 的配置* 直接使用配置创建* controllerMap 配置如* [*     'controllerMap' => [*         // 用类名申明 "account" 控制器*         'account' => 'app\controllers\UserController',*         // 用配置数组申明 "article" 控制器*         'article' => [*             'class' => 'app\controllers\PostController',*             'enableCsrfValidation' => false,*         ]*     ]* ]* * 3、调用 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) . '".');
}

八、说明一下 yii/base/Module::getModule 这个很有意思

1、先看一下配置文件时 modules 配置后的赋值过程
我们使用 modules 时,需要在配置文件中配置 modules,比如

'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) ,然后方法中执行

if (!empty($config)) {Yii::configure($this, $config);
}

再调用 yii\base\Component::setter 方法 (yii\base\Module::setModules),将 $_modules 赋值
2、如果 module 套着 module,需要这么这么设置

'modules' => ['v1' => ['class'      => 'frontend\modules\v1\Module','modules'   => ['v2'  => 'frontend\modules\v2\Module'],],
],

九、yii\base\Controller::runAction 代码

public function runAction($id, $params = [])
{/*** yii\base\Controller::createAction 代码也不贴了,可以追进去看,思路是* 1、如果 action id 是空(访问 www.aaa.com/controller)* 使用 yii\base\Controller 中的 defaultAction 属性* * 2、id 不为空,查看 Controller::actions 方法中是否有配置* 如果有,直接使用配置创建,actions 配置如* * public function actions()* {*     return [*         'error' => [*             'class' => 'yii\web\ErrorAction',*         ],*         'captcha' => [*             'class' => 'yii\captcha\CaptchaAction',*             'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,*         ],*     ];* }* * 3、利用反射(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-applications

G
M
T

Detect languageAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBengaliBosnianBulgarianCatalanCebuanoChichewaChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEsperantoEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekGujaratiHaitian CreoleHausaHebrewHindiHmongHungarianIcelandicIgboIndonesianIrishItalianJapaneseJavaneseKannadaKazakhKhmerKoreanLaoLatinLatvianLithuanianMacedonianMalagasyMalayMalayalamMalteseMaoriMarathiMongolianMyanmar (Burmese)NepaliNorwegianPersianPolishPortuguesePunjabiRomanianRussianSerbianSesothoSinhalaSlovakSlovenianSomaliSpanishSundaneseSwahiliSwedishTajikTamilTeluguThaiTurkishUkrainianUrduUzbekVietnameseWelshYiddishYorubaZulu AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBengaliBosnianBulgarianCatalanCebuanoChichewaChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEsperantoEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekGujaratiHaitian CreoleHausaHebrewHindiHmongHungarianIcelandicIgboIndonesianIrishItalianJapaneseJavaneseKannadaKazakhKhmerKoreanLaoLatinLatvianLithuanianMacedonianMalagasyMalayMalayalamMalteseMaoriMarathiMongolianMyanmar (Burmese)NepaliNorwegianPersianPolishPortuguesePunjabiRomanianRussianSerbianSesothoSinhalaSlovakSlovenianSomaliSpanishSundaneseSwahiliSwedishTajikTamilTeluguThaiTurkishUkrainianUrduUzbekVietnameseWelshYiddishYorubaZulu

Text-to-speech function is limited to 200 characters
Options : History : Feedback : Donate Close

转载于:https://my.oschina.net/botkenni/blog/1806106

Yii2 源码分析 - 入口文件执行流程相关推荐

  1. 【Linux 内核 内存管理】mmap 系统调用源码分析 ④ ( do_mmap 函数执行流程 | do_mmap 函数源码 )

    文章目录 一.do_mmap 函数执行流程 二.do_mmap 函数源码 调用 mmap 系统调用 , 先检查 " 偏移 " 是否是 " 内存页大小 " 的 & ...

  2. MyBatis 源码分析 - SQL 的执行过程

    本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...

  3. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  4. Fuchsia源码分析--系统调用流程

    Fuchsia源码分析--系统调用流程 以zx_channel_create为例 Fuchsia系统调用的定义 Fuchsia系统调用定义文件的编译 Fuchsia系统调用用户空间的调用流程 zx_c ...

  5. HDFS源码分析DataXceiver之整体流程

    在<HDFS源码分析之DataXceiverServer>一文中,我们了解到在DataNode中,有一个后台工作的线程DataXceiverServer.它被用于接收来自客户端或其他数据节 ...

  6. 源码分析Dubbo服务提供者启动流程-上篇

    本节将详细分析Dubbo服务提供者的启动流程,请带着如下几个疑问进行本节的阅读,因为这几个问题将是接下来几篇文章分析的重点内容.  1.什么时候建立与注册中心的连接.  2.服务提供者什么时候向注册中 ...

  7. F2FS源码分析-5.2 [数据恢复流程] 后滚恢复和Checkpoint的作用与实现

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 三.文件与目录的创建以及删除(未完成) 四.垃圾回收机制 五.数据恢复机制 数据恢复的原理以及方式 后滚恢 ...

  8. [Abp vNext 源码分析] - 1. 框架启动流程分析

    一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...

  9. 源码分析Dubbo服务提供者启动流程-下篇

    本文继续上文Dubbo服务提供者启动流程,在上篇文章中详细梳理了从dubbo spring文件开始,Dubbo是如何加载配置文件,服务提供者dubbo:service标签服务暴露全流程,本节重点关注R ...

最新文章

  1. lambda表达式python_Python中的Lambda表达式
  2. 腾讯、百度、阿里、微软面试题精选(不断更新)
  3. 基于AI的超分辨技术在RTC领域的技术难点与挑战
  4. SAP Spartacus和product相关的标准normalizer
  5. 技术动态 | 大规模中文概念图谱CN-Probase正式发布
  6. linux vim debugger,Vim 调试:termdebug 入门
  7. 软件测试 学习之路 linux 基础命令 (三)
  8. 微信开发php插件下载图片,微信开发之微信jsapi选择图片,上传图片,预览和下载图片方法...
  9. C语言百叶窗动画效果算法,用vb实现“百叶窗”的图形特效_visualbasic教程
  10. Swift - 05 - 数值型字面量
  11. 洛谷P1978 集合 [2017年6月计划 数论08]
  12. linux 安装Curl
  13. 近12年的计算机考研408真题及答案解析分享
  14. 零门槛修改微信步数(基于虚拟xposed)
  15. linux c蜂鸣器驱动程序,〖Linux〗OK6410a蜂鸣器的驱动程序编写全程实录
  16. 【2022最新Java面试宝典】—— Memcache面试题(23道含答案)
  17. win10睡眠模式 屏幕熄灭主机仍然运行
  18. 网络安全毕业设计选题题目大全
  19. 【正点原子FPGA连载】第十六章Petalinux设计流程实战摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南
  20. FCPX插件:Stupid Raisins Title Pop (78个动画标题+2个额外背景)

热门文章

  1. Vue.set()详解
  2. [jQuery原理] jQuery事件操作相关方法
  3. 教你轻松搞定javascript中的正则
  4. 详细了解文档对象模型(DOM)
  5. JavaScript算法(实例九)整数的置换 / 求s=a+aa+aaa+aaaa+aa...a的值 / 自守数
  6. 7-293 鸡兔同笼 (10 分)
  7. ghost网络克隆功能实现【批量】计算机操作【系统的安装】,网络学习(三十)通过ghost的网络克隆功能实现操作系统的分发...
  8. sklearn查看数据
  9. 编程实现newton插值c++_数据体操:数据处理和IDW地理插值算法
  10. 新员工入职自动加入所在部门的邮件组。