Yii2.0框架源码阅读,从请求发起,到结束的运行步骤

其实最初阅读是从yii\web\UrlManager这个类开始看起,不断的寻找这个类中方法的调用者,最终回到了yii\web\Application,那就从头开始看。

1、Nginx

nginx作为web服务器,时刻监听着80端口,等待接收用户请求,并转发给php进行处理,Yii2.0框架使用了统一的入口脚本:index.php

所以nginx中有如下的配置:

location / {
try_files $uri $uri/ /index.php?$args;
}

首先nginx会检索$uri$uri/这个路径下的文件,如果没有找到,那就将请求交给index.php

2、创建Yii Aapplication 实例

index.php文件可以看出加载了各个文件夹下的配置项,然后new了一个application,这个构造方法是在yii\web\Application的父类yii\base\Applicaton中,主要根据配置项初始化:

public function __construct($config = [])
{
Yii::$app = $this;
static::setInstance($this); $this->state = self::STATE_BEGIN; $this->preInit($config); $this->registerErrorHandler($config); Component::__construct($config);
}

3、Application run

可以看到创建实例之后调用了run方法$applicaton->run();run方法位于yii\base\Application中,总的来说就是执行:

  • before request 处理请求前的操作
  • handle request 真正的处理这次HTTP请求
  • after request 请求处理完成之后的操作
  • send response 将响应信息发送给客户端
public function run()
{
//代码简化 try catch去掉
$this->state = self::STATE_BEFORE_REQUEST;
$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;
}

4、Get Request Object

请求的处理从 $this->handleRequest($this->getRequest());开始,首先看handleRequest的参数是getRequest的返回值,getRequest方法位于yii\web\Application中:

public function getRequest()
{
return $this->get('request');
}

get方法,根据层层的继承关系找到其位于yii\base\Application的父类yii\base\Module的父类yii\di\ServiceLocator中:

public function get($id, $throwException = true)
{
if (isset($this->_components[$id])) {
return $this->_components[$id];
} if (isset($this->_definitions[$id])) {
$definition = $this->_definitions[$id];
if (is_object($definition) && !$definition instanceof Closure) {
return $this->_components[$id] = $definition;
} else {
return $this->_components[$id] = Yii::createObject($definition);
}
} elseif ($throwException) {
throw new InvalidConfigException("Unknown component ID: $id");
} else {
return null;
}
}

这里我们先不考虑$_definitions $_components这些变量初始化的位置,打印$id='request'是获取到的$definition为:

array(2) {
["cookieValidationKey"]=>
string(32) "0drrX5wQ2wZ8Hli2Ql48ss8efcE-W11m"
["class"]=>
string(15) "yii\web\Request"
}

说一下这个结果的由来,事实上这个就是我们在config/main-local.php中的配置项:

$config = [
'components' => [
'request' => [
'cookieValidationKey' => '0drrX5wQ2wZ8Hli2Ql48ss8efcE-W11m',
],
],
];

而我们获取到信息中有class=>yii\web\Request,这个class的添加是在new Application的时候,也就在构造方法中执行了preInit(),preInit中对核心的组件进行了初始化,添加了class信息,然后更新到了config数组中。

//preInit
//coreComponents 在yii\web\Application 与 yii\base\Application中
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'];
}
}

回到上面ServiceLocator的get方法中,我们获取到了$definition,现在是一个数组并非一个对象,所以执行的是:return $this->_components[$id] = Yii::createObject($definition); return回来的就是Request对象: object(yii\web\Request)

这个createObject方法,看注释中的描述就是可以理解为一个高级版本的new,因为可以根据字符串(类名),数组(含class信息),匿名函数来创建一个对象,然后返回。

5、Handle Request

run中调用的handleRequest()位于yii\web\Application中,这里主要的操作就是:

  • 从Request中获取用户请求路由
  • 调用这个路由对应的action
// @param $request yii\web\Request
public function handleRequest($request)
{
if (empty($this->catchAll)) {
list ($route, $params) = $request->resolve();
} else {
$route = $this->catchAll[0];
$params = $this->catchAll;
unset($params[0]);
}
//下面暂时省略
}

catchAll用于系统维护的时候,将所有的请求转发到一处进行处理,默认值为空,所以这里的条件判断进入了list ($route, $params) = $request->resolve(); resolve肯定是在yii\web\Request中了:

public function resolve()
{
$result = Yii::$app->getUrlManager()->parseRequest($this);
if ($result !== false) {
list ($route, $params) = $result;
if ($this->_queryParams === null) {
$_GET = $params + $_GET; // preserve numeric keys
} else {
$this->_queryParams = $params + $this->_queryParams;
}
return [$route, $this->getQueryParams()];
} else {
throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
}
}

这里通过调用yii\web\UrlManager来解析请求路由,parseRequest方法的参数为Request对象,它的主要任务是:

  • 通过Request对象获取path info
  • 检查是否有跟我们配置的url rule匹配的,有则返回
  • 没有则直接按照默认的方式进行解析

进入UrlManager:

public function parseRequest($request)
{
//在enable pretty url的前提下
$pathInfo = $request->getPathInfo();
//如果在rules中匹配到了 request 直接返回转换后的路由
foreach ($this->rules as $rule) {
if (($result = $rule->parseRequest($this, $request)) !== false) {
return $result;
}
}
//没有则使用默认方式,同时对路由进行检查,判断是否有多与一个的斜线
//判断是否使用了.html后缀 //代码省略,这里做的就是比较和截取的操作 return [$pathInfo, []];
}

getPathInfo()先不展开讨论,主要就是从当前请求的http header中获取path info。

回到yii\web\Application的handleRequest中:

public function handleRequest($request)
{
//代码简化一下,完整版请查看yii\web\Application类
list ($route, $params) = $request->resolve();
$this->requestedRoute = $route;
//主要操作
$result = $this->runAction($route, $params);
if ($result instanceof Response) {
return $result;
} else {
$response = $this->getResponse();
if ($result !== null) {
$response->data = $result;
}
return $response;
}
}

在获取到属于Yii中的路由(route)信息之后,接下来的主要操作就是执行对应的controller中的action,或者是对应模块(module)中的controller,controller中对应的action。runAction($route,$params)先不展开讨论,主要知道执行完成之后我们获取的是一个yii\web\Response对象就行了。

6、回到run方法

将response信息发送给客户端

// yii\base\Application run()
$response = $this->handleRequest($this->getRequest());
$response->send();
return $response->exitStatus;

至此一次完整的请求完成。

Yii2.0源码阅读-一次请求的完整过程的更多相关文章

  1. Yii2.0源码阅读-从路由到控制器

    之前的文章弄清了一次请求的开始到结束.主要讲了Yii Applicaton实例的创建.初始化,UrlManager如何返回Yii中的路由信息,到runAction,最后将Response发送给客户端. ...

  2. Yii2.0源码阅读-视图(View)渲染过程

    之前的文章我们根据源码的分析,弄清了Yii如何处理一次请求,以及根据解析的路由如何调用控制器中的action,那接下来好奇的可能就是,我在控制器action中执行了return $this->r ...

  3. Yii2.0源码阅读-behavior的实现原理

    Yii2.0中的一个思想就是组件化的思想,所以.大多数的类都直接或间接的继承自yii\base\Component,而组件的三大功能:属性.事件.行为. 行为的目的是为了方便的扩展一个类的功能,而不需 ...

  4. Yii2.0源码阅读-PHP如何与redis通信?

    PHP与Redis可以通过socket进行通信,前提是PHP需要实现Redis的协议 RESP协议描述: 字符串 \r\n : 表示一个正确的状态信息,具体信息是'+'后面的字符(Simple Str ...

  5. Yii2.0源码分析之——控制器文件分析(Controller.php)创建动作、执行动作

    在Yii中,当请求一个Url的时候,首先在application中获取request信息,然后由request通过urlManager解析出route,再在Module中根据route来创建contr ...

  6. gin 源码阅读(2) - http请求是如何流入gin的?

    推荐阅读: gin 源码阅读(1) - gin 与 net/http 的关系 本篇文章是 gin 源码分析系列的第二篇,这篇文章我们主要弄清一个问题:一个请求通过 net/http 的 socket ...

  7. Vue2.0源码阅读笔记(四):nextTick

      在阅读 nextTick 的源码之前,要先弄明白 JS 执行环境运行机制,介绍 JS 执行环境的事件循环机制的文章很多,大部分都阐述的比较笼统,甚至有些文章说的是错误的,以下为个人理解,如有错误, ...

  8. Vue2.0源码阅读笔记(一):选项合并

      Vue本质是上来说是一个函数,在其通过new关键字构造调用时,会完成一系列初始化过程.通过Vue框架进行开发,基本上是通过向Vue函数中传入不同的参数选项来完成的.参数选项往往需要加以合并,主要有 ...

  9. Vue2.0源码阅读笔记--生命周期

    一.Vue2.0的生命周期 Vue2.0的整个生命周期有八个:分别是 1.beforeCreate,2.created,3.beforeMount,4.mounted,5.beforeUpdate,6 ...

随机推荐

  1. java-生成任意格式的json数据

    最近研究java的东西.之前靠着自己的摸索,实现了把java对象转成json格式的数据的功能,返回给前端.当时使用的是 JSONObject.fromObject(object) 方法把java对象换 ...

  2. linux下用split命令将一个大的文件拆分成若干小文件

    命令 split -l 50 wlan_date.txt wlan 说明:按50行给文件进行拆分,如果没有最后面的参数,命名将会是xaa,xab等.

  3. 【java】打印一个对象即打印出该对象toString()返回值

    public class TestToString { public static void main(String[] args){ Node node1=new Node("东邪&quo ...

  4. Solr集群搭建详细教程(二)

    注:欢迎大家转载,非商业用途请在醒目位置注明本文链接和作者名dijia478,商业用途请联系本人dijia478@163.com. 之前步骤:Solr集群搭建详细教程(一) 三.solr集群搭建 注意 ...

  5. ASP.NET Core 系列视频完结,新项目实战课程发布。

    今天把MVC的章节完成了,给大家从头到尾做了一个登录注册的示例,带前后端Model验证,算是完整的示例.同时借助于eShopOnContainers的示例也做了一个DBContextSeed的包装器来 ...

  6. ES6之Promise

    Promise是一个对象,用来传递异步操作的消息,他有两个特点:第一对象的状态不受外界的影响,第二一旦状态改变就不会在变,任何时候都可以得到这个结果,他有两个参数分别是resolve(他的作用是将Pr ...

  7. JAVA中的设计模式二(工厂模式)

    工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类. 工厂模式主要有: 简单工厂模式,工厂方法,抽象工厂: 简单工厂: 又叫静态工厂,是工厂模式三中状态中结构最为简单的.主 ...

  8. 大赛获奖选手专访 | 冷燕冰:最佳设计奖TIMING里的时机和时序

    Mockplus三周年原型设计大赛,从筹备到11月21日完美落幕,50余天的时光,已成为过去.这场近千人参赛的原型设计大赛,我想,无论是于主办方,于参赛选手,于专家评委,还是于每一个关注和参与的人,都 ...

  9. Python数据分析工具

    1.Numpy 安装:pip install numpy [root@kvm work]# cat numpy_test.py #!/usr/bin/env python #coding:utf-8 ...

  10. Linux(CentOS6.5)修改默认yum源为国内的阿里云、网易yum源

    官方的yum源在国内访问效果不佳. 需要改为国内比较好的阿里云或者网易的yum源 修改方式: echo 备份当前的yum源 mv /etc/yum.repos.d /etc/yum.repos.d.b ...