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);
    }
上一篇:01-08-03【Nhibernate (版本3.3.1.4000) 出入江湖】二级缓存:NHibernate自带的HashtableProvider之缓存管理


下一篇:java 通过网络 ntp 获取网络时间