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源码阅读笔记(二十一)——请求处理流程的更多相关文章

  1. Yii源码阅读笔记(十一)

    controller类的render部分,用于渲染视图和布局文件: /** * Returns all ancestor modules of this controller. * 获取当前控制器所有 ...

  2. Caddy源码阅读(二)启动流程与 Event 事件通知

    Caddy源码阅读(二)启动流程与 Event 事件通知 Preface Caddy 是 Go 语言构建的轻量配置化服务器.https://github.com/caddyserver/caddy C ...

  3. Yii源码阅读笔记(一)

    今天开始阅读yii2的源码,想深入了解一下yii框架的工作原理,同时学习一下优秀的编码规范和风格.在此记录一下阅读中的小心得. 每个框架都有一个入口文件,首先从入口文件开始,yii2的入口文件位于we ...

  4. werkzeug源码阅读笔记(二) 下

    wsgi.py----第二部分 pop_path_info()函数 先测试一下这个函数的作用: >>> from werkzeug.wsgi import pop_path_info ...

  5. werkzeug源码阅读笔记(二) 上

    因为第一部分是关于初始化的部分的,我就没有发布出来~ wsgi.py----第一部分 在分析这个模块之前, 需要了解一下WSGI, 大致了解了之后再继续~ get_current_url()函数 很明 ...

  6. Detectron2源码阅读笔记-(二)Registry&build_*方法

    ​ Trainer解析 我们继续Detectron2代码阅读笔记-(一)中的内容. 上图画出了detectron2文件夹中的三个子文件夹(tools,config,engine)之间的关系.那么剩下的 ...

  7. Yii源码阅读笔记(二)

    接下来阅读BaseYii.php vendor/yiisoft/yii2/BaseYii.php—— namespace yii; use yii\base\InvalidConfigExceptio ...

  8. Yii源码阅读笔记(三十二)

    web/Application类的注释,继承base/Application类,针对web应用的一些处理: namespace yii\web; use Yii; use yii\base\Inval ...

  9. Yii源码阅读笔记(三十一)

    Widget类中开始,获取视图对象,获取widget ID,渲染视图,获取路径方法注释: private $_id; /** * Returns the ID of the widget. * 返回插 ...

随机推荐

  1. js jQuery笔记

    jQuery 1.几种获取子元素的方法及区别 children方法获得的仅仅是元素一下级的子元素,即:immediate children. find方法获得所有下级元素,即:descendants ...

  2. C# 格式化字符串,日期,字符串操作汇总

    时间格式化 有时候我们要对时间进行转换,达到不同的显示效果 默认格式为:2005-6-6 14:33:34 如果要换成成200506,06-2005,2005-6-6或更多的该怎么办呢 我们要用到:D ...

  3. Oracle存储过程中临时表的使用技巧

    一.Oracle临时表知识 在Oracle中,临时表分为SESSION(会话级).TRANSACTION(事务级)两种,SESSION级的临时表数据在整个SESSION都存在,直到结束此次SESSIO ...

  4. 拓展企业VR培训业务,这家VR训练公司StriVR完成500万美元融资!

    虚拟现实初创公司StriVR最近发布了新的企业VR训练产品项目,并宣布在刚刚结束的首轮融资中获得500万美元投资.由Signia Venture Partners领投,宝马i Venturesi.Ad ...

  5. UVa 11388 & 丝帛

    一直在想丝帛题要不要贴呢...后来觉得还是贴了吧...反正没人看...blog是开给自己看的...偶尔无聊打打blog也显得生活非常充实... 题意: 给一个gcd和lcm求满足啊他们的最小的a和b. ...

  6. 洛谷 P1262 间谍网络 Label: Kosarajn强联通

    题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报 ...

  7. [题解+总结]NOIP2015模拟题2

    // 此博文为迁移而来,写于2015年7月22日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w72i.html 1.总 ...

  8. jQuery自定义滚动条样式插件mCustomScrollbar

    如果你构建一个很有特色和创意的网页,那么肯定希望定义网页中的滚动条样式,这方面的 jQuery 插件比较不错的,有两个:jScrollPane 和 mCustomScrollbar. 关于 jScro ...

  9. 2015 CTSC & APIO滚粗记

    o诶人太弱..... 记一发滚粗记以便治疗我的健忘症= = //文章会不定时修改,添加一些内容什么的...因此最好看一下刷新一下(因为有可能你正在看= =我正在写... 5.2 早上9点坐上长达11小 ...

  10. 【noiOJ】p1776

    t1776:木材加工 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头,需要得到的小段的数目是给 ...