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. * 返回插 ...
随机推荐
- js jQuery笔记
jQuery 1.几种获取子元素的方法及区别 children方法获得的仅仅是元素一下级的子元素,即:immediate children. find方法获得所有下级元素,即:descendants ...
- C# 格式化字符串,日期,字符串操作汇总
时间格式化 有时候我们要对时间进行转换,达到不同的显示效果 默认格式为:2005-6-6 14:33:34 如果要换成成200506,06-2005,2005-6-6或更多的该怎么办呢 我们要用到:D ...
- Oracle存储过程中临时表的使用技巧
一.Oracle临时表知识 在Oracle中,临时表分为SESSION(会话级).TRANSACTION(事务级)两种,SESSION级的临时表数据在整个SESSION都存在,直到结束此次SESSIO ...
- 拓展企业VR培训业务,这家VR训练公司StriVR完成500万美元融资!
虚拟现实初创公司StriVR最近发布了新的企业VR训练产品项目,并宣布在刚刚结束的首轮融资中获得500万美元投资.由Signia Venture Partners领投,宝马i Venturesi.Ad ...
- UVa 11388 & 丝帛
一直在想丝帛题要不要贴呢...后来觉得还是贴了吧...反正没人看...blog是开给自己看的...偶尔无聊打打blog也显得生活非常充实... 题意: 给一个gcd和lcm求满足啊他们的最小的a和b. ...
- 洛谷 P1262 间谍网络 Label: Kosarajn强联通
题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报 ...
- [题解+总结]NOIP2015模拟题2
// 此博文为迁移而来,写于2015年7月22日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w72i.html 1.总 ...
- jQuery自定义滚动条样式插件mCustomScrollbar
如果你构建一个很有特色和创意的网页,那么肯定希望定义网页中的滚动条样式,这方面的 jQuery 插件比较不错的,有两个:jScrollPane 和 mCustomScrollbar. 关于 jScro ...
- 2015 CTSC & APIO滚粗记
o诶人太弱..... 记一发滚粗记以便治疗我的健忘症= = //文章会不定时修改,添加一些内容什么的...因此最好看一下刷新一下(因为有可能你正在看= =我正在写... 5.2 早上9点坐上长达11小 ...
- 【noiOJ】p1776
t1776:木材加工 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65536kB 描述 木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头,需要得到的小段的数目是给 ...