Yii源码阅读笔记(二十一)——请求处理流程
Yii2请求处理流程:
首先:项目路径/web/index.php
(new yii\web\Application($config))->run();//根据配置文件创建App实例,先实例化yii\web\Application(),然后调用run()方法
该语句可分为两步:
$application = new yii\web\Application($config);//实例化app $application->run();//调用run()方法
$config 为配置文件,通过 require引入:
$config = require(__DIR__ . '/../config/web.php');//引用配置文件
首先看实例化的过程:
$application = new yii\web\Application($config);//分析完成
顺着命名空间可以发现,实例化的类为yii\web下的Application()类:
class Application extends \yii\base\Application//继承了yiii\base\Application
yii\web\Application.php中没有使用$config 构造函数,所以我们在它的父类也就是\yii\base\Application查找,在父类中找到了构造函数,如下:
abstract class Application extends Module
/**
* Constructor.
* 实例化应用的构造函数
* @param array $config name-value pairs that will be used to initialize the object properties.
* Note that the configuration must contain both [[id]] and [[basePath]].
* @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
*/
public function __construct($config = [])
{
Yii::$app = $this;//将\yii\base\Application里所有的公共方法都交给了,Yii::$app,其实Yii大部分信息都在Yii::$app变量中
//当然也包括它的父类如:\yii\base\Module \yii\di\ServiceLocator \yii\base\Component \yii\base\Object
$this->setInstance($this);//这一句是指向\yii\base\Module,将\yii\base\Application中的所有的属性和方法交给Yii::$app->loadedModules数组中
$this->state = self::STATE_BEGIN;//标记状态
$this->preInit($config);//加载配置文件的框架信息 如:设置别名,设置框架路径等等 最为重要的是给加载默认组件
$this->registerErrorHandler($config);//加载配置文件中的异常处理组件
Component::__construct($config);//将配置文件中的所有信息赋值给Object,也就是Yii::$app->配置文件参数可以直接调用配置文件的内容 如:Yii::$app->vendorPath//输出框架路径 Yii::$app->components['redis']//输出redis配置信息
}
下面分析源代码:
Yii::$app = $this;
这一句的意思是,将\yii\base\Application里所有的公共方法都交给了,Yii::$app,其实Yii大部分信息都在Yii::$app变量中,当然也包括它的父类如:\yii\base\Module \yii\di\ServiceLocator \yii\base\Component \yii\base\Object
$this->setInstance($this);//该句调用的是module中的方法
这一句调用的是\yii\base\Module中的方法:
class Module extends ServiceLocator
/**
* Sets the currently requested instance of this module class.
* 将当前类名和存储类的对象变量加入Yii::$app->loadedModules['yii\web\Application']数组中
* 这样直接通过Yii::$app->loadedModules['yii\web\Application']就可以直接调用这个类
* @param Module|null $instance the currently requested instance of this module class.
* If it is null, the instance of the calling class will be removed, if any.
*/
public static function setInstance($instance)//module模块里会用到,为getInstance提供数据
{
if ($instance === null) {
unset(Yii::$app->loadedModules[get_called_class()]);//如果没有传入参数,说明是删除,直接unset
} else {
Yii::$app->loadedModules[get_class($instance)] = $instance;//否则将该类和类的实例存入loadedModules数组中
}
}
这句意思是:将当前类名和存储类的对象变量加入Yii::$app->loadedModules['yii\web\Application']数组中这样直接通过Yii::$app->loadedModules['yii\web\Application']就可以直接调用这个类
$this->state = self::STATE_BEGIN;//标记状态
这一句用于标记状态
$this->preInit($config);//加载配置文件的框架信息 如:设置别名,设置框架路径等等 最为重要的是给加载默认组件
这一句是将配置文件中的一些变量设置别名,主要是针对路径、URL之类的
$this->registerErrorHandler($config);//加载配置文件中的异常处理组件
这一句是用于加载异常处理程序的
Component::__construct($config);//将配置文件中的所有信息赋值给Object,也就是Yii::$app->配置文件参数可以直接调用配置文件的内容 如:Yii::$app->vendorPath//输出框架路径 Yii::$app->components['redis']//输出redis配置信息
这一句通过多次继承,最终指向了Object
/**
* Constructor.
* The default implementation does two things:
* 构造方法实现了接口Configurable,通过传入的配置初始化对象,调用init()方法
* - Initializes the object with the given configuration `$config`.
* - Call [[init()]].
*
* If this method is overridden in a child class, it is recommended that
* 如果构造函数在子类中重写,必须调用父类的方法,且最后一个参数为配置数组
* - the last parameter of the constructor is a configuration array, like `$config` here.
* - call the parent implementation at the end of the constructor.
*
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($config = [])
{
// 根据 $config 内容初始化该对象
if (!empty($config)) {
Yii::configure($this, $config);//将配置文件里面的所有配置信息赋值给Object,由于Object是大部分类的基类,实际上也就是交给了yii\web\Application
// 可以Yii::$app->配置参数来访问配置文件中的内容
}
// 调用 init() 方法,继承该类的类可以重写 init 方法,用于初始化
$this->init();
}
该方法的作用就是把当前的配置文件$config变量中内容交给Object 类,Object类是基础类,所以绝大部分类都能直接调用配置文件中配置内容
然后执行
$this->init();
在请求过程中,这句实际上执行的是yii\base\Module.php中的init()
/**
* Initializes the module.
* 初始化模块,取出控制器的命名空间,也可以理解为路径 注:第一次加载的时候(即controllerNamespace为null时)才会执行
* This method is called after the module is created and initialized with property values
* given in configuration. The default implementation will initialize [[controllerNamespace]]
* if it is not set.
*
* If you override this method, please make sure you call the parent implementation.
*/
public function init()
{
if ($this->controllerNamespace === null) {//判断controllerNamespace属性是否被赋值,没有赋值才执行
$class = get_class($this);//获取类名
if (($pos = strrpos($class, '\\')) !== false) {
$this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';//取得命名空间
}
}
}
初始化模块,取出控制器的命名空间,也可以理解为路径 *注:第一次加载的时候才会执行
接着看调用run()的过程:
$application->run();//调用run()方法
该语句指向的是\yii\base\Application.php中的run()方法
/**
* Runs the application.
* 运行应用,该方法是应用的主入口
* This is the main entrance of an application.
* @return integer the exit status (0 means normal, non-zero values mean abnormal)
*/
public function run()
{
try {
$this->state = self::STATE_BEFORE_REQUEST;
$this->trigger(self::EVENT_BEFORE_REQUEST);//加载事件函数beforRequest函数
$this->state = self::STATE_HANDLING_REQUEST;
$response = $this->handleRequest($this->getRequest());//加载控制器 //$this->getRequest() //获取Request对象
$this->state = self::STATE_AFTER_REQUEST;
$this->trigger(self::EVENT_AFTER_REQUEST);//加载afterRequest事件函数
$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;
}
}
分析该方法中的关键部分:
$this->getRequest()
/**
* Returns the request component.
* 返回请求的组件对象
* @return \yii\web\Request|\yii\console\Request the request component.
*/
public function getRequest()
{
return $this->get('request');
}
该语句调用内部的方法,获取Request对象
$response = $this->handleRequest($this->getRequest());//加载控制器 //$this->getRequest() //获取Request对象
通过调用抽象方法,指向\yii\web\Application.php
/**
* Handles the specified request.
* 处理指定的请求
* @param Request $request the request to be handled
* @return Response the resulting response
* @throws NotFoundHttpException if the requested route is invalid
*/
public function handleRequest($request)
{
if (empty($this->catchAll)) {
list ($route, $params) = $request->resolve();//取出路由及参数
} else {
$route = $this->catchAll[0];
$params = $this->catchAll;
unset($params[0]);
}
try {
Yii::trace("Route requested: '$route'", __METHOD__);
$this->requestedRoute = $route;
$result = $this->runAction($route, $params);//运行模块中的runAcition方法,实际是指向控制器中的Action
if ($result instanceof Response) {
return $result;
} else {
/**
*这个是加载yii\base\Response类,在外部可以Yii::$app->get('response')、Yii::$app->getResponse()、Yii::$app->response 等等方式来加载response类
*主要用来加载http状态,及头信息,如301,302,404,ajax头等等的获取
*/
$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);
}
}
$result = $this->runAction($route, $params);//运行模块中的runAcition方法,实际是指向控制器中的Action
该语句指向\yii\base\Module.php中
/**
* Runs a controller action specified by a route.
* 运行路由中指定的控制器方法
* This method parses the specified route and creates the corresponding child module(s), controller and action
* instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.
* If the route is empty, the method will use [[defaultRoute]].
* @param string $route the route that specifies the action.
* @param array $params the parameters to be passed to the action
* @return mixed the result of the action.
* @throws InvalidRouteException if the requested route cannot be resolved into an action successfully
*/
public function runAction($route, $params = [])
{
$parts = $this->createController($route);//根据路由创建控制器对象
if (is_array($parts)) {
/* @var $controller Controller */
list($controller, $actionID) = $parts;//获得$actionId和$controller
$oldController = Yii::$app->controller;
Yii::$app->controller = $controller;
$result = $controller->runAction($actionID, $params);//运行使用控制器加载 action方法
Yii::$app->controller = $oldController;//将对象交给Yii::$app->controller 这里面起的作用应该是运行控制器,最后释放控制器的对象变量
return $result;
} else {
$id = $this->getUniqueId();
throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');
}
}
其中
$parts = $this->createController($route);//根据路由创建控制器对象
创建了控制器对象
$result = $controller->runAction($actionID, $params);//运行使用控制器加载 action方法
用来调用runAction()方法加载Action方法
该句指向了\yii\base\Controller.php
/**
* Runs an action within this controller with the specified action ID and parameters.
* If the action ID is empty, the method will use [[defaultAction]].
* $id 为action的id,也就是操作的名称,如定义的actionIndex,那么id就为Index。
* 如果没有定义 action ID,就会调用默认的操作,例如常用的index
*
* @param string $id 操作id,也就是操作名
* @param array $params the parameters (name-value pairs) to be passed to the action.
* @return mixed the result of the action.
* @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
* @see createAction()
*/
public function runAction($id, $params = [])
{
//创建action
$action = $this->createAction($id);
if ($action === null) {
//创建action失败,抛出异常
throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
}
//写入trace信息
Yii::trace('Route to run: ' . $action->getUniqueId(), __METHOD__);
if (Yii::$app->requestedAction === null) {
Yii::$app->requestedAction = $action;//不知道这个是干嘛用的0.0
}
$oldAction = $this->action;//将action中的信息保存到oldAction
$this->action = $action; //将当前的action写入action属性中
//用来保存当前控制器的所有父模块,顺序为由子模块到父模块
$modules = [];
$runAction = true;
/*
* 获取当前控制器的所有的模块,并执行每个模块的beforeAction来检查当前的action是否可以执行,
* 注意:getModules返回的数组顺序为:从父模块到子模块,
* 所以在执行beforeAction的时候,先检查最外层的父模块,然后检查子模块。
*
* 然而在执行afterAction的时候,顺序就反过来了,先执行子模块,最后执行父模块。
* 加载默认模块如:Application log等。再调用模块内的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)) {//判断当前控制器中beforeAction,执行beforeAction
// 由生成的action对象来执行runWithParams方法
$result = $action->runWithParams($params);//执行控制器里的action
//执行完后,再执行afterAction方法
$result = $this->afterAction($action, $result);
//执行所有父模块的afterAction
foreach ($modules as $module) {
/* @var $module Module */
$result = $module->afterAction($action, $result);
}
}
$this->action = $oldAction; //有什么用呢?,看完后面的在回头看吧
return $result;
}
这一句才是真正执行action的地方
$result = $action->runWithParams($params);//执行控制器里的action
该语句指向yii\base\InlineAction
/**
* Runs this action with the specified parameters.
* 使用指定的参数运行action
* This method is mainly invoked by the controller.
* @param array $params action parameters
* @return mixed the result of the action
*/
public function runWithParams($params)
{
$args = $this->controller->bindActionParams($this, $params);//对action的参数进行分析,并且赋值给控制器
Yii::trace('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
if (Yii::$app->requestedParams === null) {
Yii::$app->requestedParams = $args;
}
//用控制器类去执行action方法,并且带上参数
return call_user_func_array([$this->controller, $this->actionMethod], $args);
}
Yii源码阅读笔记(二十一)——请求处理流程的更多相关文章
- Yii源码阅读笔记(十一)
controller类的render部分,用于渲染视图和布局文件: /** * Returns all ancestor modules of this controller. * 获取当前控制器所有 ...
- Caddy源码阅读(二)启动流程与 Event 事件通知
Caddy源码阅读(二)启动流程与 Event 事件通知 Preface Caddy 是 Go 语言构建的轻量配置化服务器.https://github.com/caddyserver/caddy C ...
- Yii源码阅读笔记(一)
今天开始阅读yii2的源码,想深入了解一下yii框架的工作原理,同时学习一下优秀的编码规范和风格.在此记录一下阅读中的小心得. 每个框架都有一个入口文件,首先从入口文件开始,yii2的入口文件位于we ...
- werkzeug源码阅读笔记(二) 下
wsgi.py----第二部分 pop_path_info()函数 先测试一下这个函数的作用: >>> from werkzeug.wsgi import pop_path_info ...
- werkzeug源码阅读笔记(二) 上
因为第一部分是关于初始化的部分的,我就没有发布出来~ wsgi.py----第一部分 在分析这个模块之前, 需要了解一下WSGI, 大致了解了之后再继续~ get_current_url()函数 很明 ...
- Detectron2源码阅读笔记-(二)Registry&build_*方法
Trainer解析 我们继续Detectron2代码阅读笔记-(一)中的内容. 上图画出了detectron2文件夹中的三个子文件夹(tools,config,engine)之间的关系.那么剩下的 ...
- Yii源码阅读笔记(二)
接下来阅读BaseYii.php vendor/yiisoft/yii2/BaseYii.php—— namespace yii; use yii\base\InvalidConfigExceptio ...
- Yii源码阅读笔记(三十二)
web/Application类的注释,继承base/Application类,针对web应用的一些处理: namespace yii\web; use Yii; use yii\base\Inval ...
- Yii源码阅读笔记(三十一)
Widget类中开始,获取视图对象,获取widget ID,渲染视图,获取路径方法注释: private $_id; /** * Returns the ID of the widget. * 返回插 ...
随机推荐
- codeforces 519E A and B and Lecture Rooms LCA倍增
Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Submit Status Prac ...
- 将MapReduce的结果输出至Mysql数据库
package com.sun.mysql;import java.io.DataInput;import java.io.DataOutput;import java.io.IOException; ...
- 优化WPF 3D性能
Maximize WPF 3D Performance .NET Framework 4.5 As you use the Windows Presentation Foundation (WPF ...
- ural 1252. Sorting the Tombstones
1252. Sorting the Tombstones Time limit: 1.0 secondMemory limit: 64 MB There is time to throw stones ...
- ural 1142. Relations
1142. Relations Time limit: 1.0 secondMemory limit: 64 MB Background Consider a specific set of comp ...
- iOS之04-方法的声明和实现
本次重点学习和理解OC对象方法的声明和定义 代码: /* 计算器类 方法: 1> 返回 π 2> 计算某个整数的平方 3> 计算两个整数的和 */ #import <Found ...
- 【Cocos2d-x游戏开发】浅谈游戏中的坐标系
无论是开发2D还是开发3D游戏,首先必须弄清楚坐标系的概念.在Cocos2d-x中,需要了解的有OpenGL坐标系.世界坐标系和节点坐标系. 1.UI坐标系 IOS/Android/Windows ...
- unity
static function Instantiate (original : Object, position : Vector3, rotation : Quaternion) : Object ...
- 《DON'T MAKE ME THINK》/《点石成金访客至上的网页设计秘笈》 读书笔记
1.web页面要尽可能简单,让用户不用思考就能知道页面的功能,如果要进行一些崭新的.开拓性的或者非常复杂的页面设计时, 此时要利用页面元素的外观.精心选择的名称.页面布局以及少量仔细斟酌过的文字,使页 ...
- BZOJ3636: 教义问答手册
Description “汉中沃野如关中,四五百里烟蒙蒙.黄云连天夏麦熟,水稻漠漠吹秋风.”——摘自 黄裳<汉中行>“泉岭精神不朽,汉中诸球永生.”——摘自<泉岭精神创立者语录> ...