每一个框架都有一个入口脚本,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源代码,有任何问题欢迎讨论。

  

Yii2应用的运行过程的更多相关文章

  1. 江太公:javascript count(a)(b)(c)(d)运行过程思考

    昨天,我弟抛给我一个js的题,使用类似标题那样的调用方法计算a*b*c*d以致无穷的实现方法.思考了半天,终于理清了它的运行过程,记录于下: 函数体: <!DOCTYPE html> &l ...

  2. JAVA - JAVA编译运行过程

    Java编译原理 *.java→*.class→机器码 java编译器 (编译) → 虚拟机(解释执行) →  解释器(翻译) → 机器码 1.Java编译过程与c/c++编译过程不同 Java编译程 ...

  3. 孙鑫MFC学习笔记3:MFC程序运行过程

    1.MFC中WinMain函数的位置在APPMODUL.cpp APPMODUL.cpp中是_tWinMain,其实_tWinMain是一个宏#define _tWinMain WinMain 2.全 ...

  4. HOWTO - Basic MSI安装包在安装运行过程中如何获取完整源路径

    有朋友问到如何在一个Windows Installer安装包中获取安装包源路径,就是在安装包运行过程中动态获取*.msi所在完整路径. 这个问题分两类,如果我们的安装包只是一个*.msi安装文件,那么 ...

  5. DirectShow程序运行过程简析

    这段时间一直在学习陆其明老师的<DirectShow开发指南>一书,书中对DirectShow的很多细节讲解清晰,但是却容易让人缺少对全局的把握.在学习过程中,整理了关于DirectSho ...

  6. Java Executor并发框架(二)剖析ThreadPoolExecutor运行过程

    上一篇从整体上介绍了Executor接口,从上一篇我们知道了Executor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newScheduledThrea ...

  7. 基础知识《零》---Java程序运行机制及运行过程

    Java运行机制 Java虚拟机(Java Virtual Machine):Java虚拟机可以理解成一个以字节码为机器指令的CPU:对于不同的运行平台,有不同的虚拟机:Java虚拟机机制屏蔽了底层运 ...

  8. .net学习之.net和C#关系、运行过程、数据类型、类型转换、值类型和引用类型、数组以及方法参数等

    1..net 和 C# 的关系.net 是一个平台,C#是种语言,C#语言可以通过.net平台来编写.部署.运行.net应用程序,C#通过.net平台开发.net应用程序2..net平台的重要组成FC ...

  9. ionic 运行过程中动态切换API服务器地址

    ionic 运行过程中动态切换API服务器地址 keywords: ionic,phonegap,cordova,网络制式,动态切换,变更,API,服务器地址,$resource,localstora ...

随机推荐

  1. Linux常用命令汇总(未完,待补充)

    由于工作中经常要和Linux服务器打交道,故整理了一些常用的Linux命令,方便新入职的同学或实习生参考学习. 1. 查看Linux内核版本 #uname -a 2. ctrl+insert:复制选中 ...

  2. crm项目整理概要

    一.开发背景 由于公司人员的增多,原来通过excel表格存取方式过于繁琐,而且对于公司人员的调配和绩效考核等不能做到精确处理,所以开发crm系统,开始开发只是针对销售人员和客户,后面陆续加上一些操作, ...

  3. C 语言实例 - 阶乘

    C 语言实例 - 阶乘 一个正整数的阶乘(英语:factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1.自然数n的阶乘写作n!. n!=×××...×n.阶乘亦可以递归方式定义:!=, ...

  4. Java - 一道关于Arrays.asList的题目

    题目 有这样一道有趣的题目: final int[] test = new int[]{1,2,3,4}; final Integer[] test2 = new Integer[]{1,2,3,4} ...

  5. 关于idea的快捷键提示

    IntelliJ Idea 常用快捷键列表 Ctrl+Shift + Enter,语句完成“!”,否定完成,输入表达式时按 “!”键Ctrl+E,最近的文件Ctrl+Shift+E,最近更改的文件Sh ...

  6. [Android基础]Android四大组件之Activity总结

    1.Activity简介 Activity是Android一个非常重要的用户接口(四大组件之一),是可见的,主要是用户和应用程序之间进行交互的接口.在每个Activity中都可以放很多控件,所以也可以 ...

  7. [Android]《Android艺术开发探索》第一章读书笔记

    1. 典型情况下生命周期分析 (1)一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart方法就会被调用. (2)当用户打开新的Activity或者切换到桌面的时候,回调如下 ...

  8. Win10+VirtualBox+Openstack Mitaka

    首先VirtualBox安装的话,没有什么可演示的,去官网(https://www.virtualbox.org/wiki/Downloads)下载,或者可以去(https://www.virtual ...

  9. 网页抓取解析,使用JQuery选择器进行网页解析

    最近开发一个小功能,数据库中一个基础表的数据从另一个网站采集. 因为网站的数据不定时更新,需要更新后自动采集最新的内容. 怎么判断更新数据没有? 好在网站有一个更新日志提示的地方,只需要对比本地保留的 ...

  10. SSAS 部署之创建部署脚本

    1.获取多维数据库的结构脚本: 当你的SSAS项目完成后,在Bin目录下会有一个SSAS.asdatabase文件. 2.打开“开始” ->Microsoft SQL Server 2008 R ...