zoukankan      html  css  js  c++  java
  • Yii2的整体结构概览

    Yii2的整体结构概览

    一、yii2内部中各类之间的关系

    1.  各类之间的关系

    yiiwebApplication->yiiaseApplication->yiiaseModule->yiidiServiceLocator->yiiaseComponent->yiiaseBaseObject->yiiaseConfigurable

    SiteController -> yiiwebController -> yiiaseController -> yiiaseComponent->yiiaseBaseObject->yiiaseConfigurable

    2.  目录以及文件

    yiiase目录:框架的底层类

    yiiaseModule:  子应用程序,debug、gii都是独立的module

    yiidiServiceLocator:  服务定位器,主要负责组件component的管理

    yiiaseComponent:  这个类非常重要,它所实现的属性、事件、行为功能贯穿yii2源码

    yiiaseBaseObject:  该类中没有事件行为机制,自己实现的类可以继承该类

    二、 预初始化

    从入口脚本index.php着手:

    我们看到这一段: (new yiiwebApplication($config))->run();  启动程序

    它实际指的是父类 yiiaseApplication::run 方法,是启动整个应用程序的“钥匙”。在执行yiiaseApplication::run之前, yiiaseApplication 的构造方法__construct会先被执行。

    下面截取 yiiaseApplication 中的一段代码:

     1 <?php
     2 namespace yiiase;
     3 
     4 use Yii;
     5 abstract class Application extends Module
     6 {
     7     const EVENT_BEFORE_REQUEST = 'beforeRequest';
     8     const EVENT_AFTER_REQUEST = 'afterRequest';
     9     const STATE_BEGIN = 0;
    10     const STATE_INIT = 1;
    11     const STATE_BEFORE_REQUEST = 2;
    12     const STATE_HANDLING_REQUEST = 3;
    13     const STATE_AFTER_REQUEST = 4;
    14     const STATE_SENDING_RESPONSE = 5;
    15     const STATE_END = 6;
    16     ...
    17     public function __construct($config = [])
    18     {
    19         Yii::$app = $this;
    20         static::setInstance($this);
    21 
    22         $this->state = self::STATE_BEGIN;
    23 
    24         $this->preInit($config); // 预初始化
    25 
    26         $this->registerErrorHandler($config);
    27 
    28         Component::__construct($config);
    29     }
    30 }

    Component::__construct实际指的是 yiiaseBaseObject::__construct,这个方法的作用是什么呢?

    顺便提下:

    1) Object是php7.2中的保留类名,不可以使用Object作为类的名称。

    2) 从 Yii2 2.0.13版本开始,已经弃用了 Object类,此前版本中的yiiaseObject使用 yiiaseBaseObject 进行代替

    接下来,我们来看下yiiaseBaseObject::__construct( )

     1 <?php
     2 namespace yiiase;
     3 
     4 use Yii;
     5 class BaseObject implements Configurable
     6 {
     7     ...
     8     // 该方法有两个重点含义:
     9     // 1.调用 yiiBaseYii::configure 方法,初始化 yiiwebApplication 类的属性
    10     // 2.调用 yiiwebApplication::init 方法完成初始化操作
    11     public function __construct($config = [])
    12     {
    13         if (!empty($config)) {
    14             // $this 自然指的是 yiiwebApplication,$config 指的是我们的配置数组。
    15             Yii::configure($this, $config);
    16         }
    17         $this->init();
    18     }  
    19 }

    说明:

        yiiaseBaseObject::__construct 方法内执行 $this->init(),也就是说,但凡是继承 yiiaseBaseObject 的类,init方法都是在 __construct 方法运行后调用。这在继承复杂的类时,很重要也很方便。前提是一旦重写父类的 __construct 方法,记得调用 parent::__construct哦。

        这里 $this->init 方法的调用,实际是调用 yiiaseApplication::init 方法。该方法则会带入我们进入下一阶段:应用的初始化

        从应用的角度来说,$this 指的就是 yiiwebApplication 类的实例 Yii::$app,这里调用的自然就是 yiiwebApplication 的init方法,但是在yiiwebApplication中并没有找到 init 方法,不过我们在其父类 yiiaseApplication 中找

    到对应的init方法,代码如下:

     1 <?php
     2 namespace yiiase;
     3 
     4 use Yii;
     5 abstract class Application extends Module
     6 {
     7     ...
     8     public function init()
     9     {
    10         $this->state = self::STATE_INIT;
    11         $this->bootstrap();
    12     }
    13 
    14 }

     yiiaseApplication::bootstrap 方法主要是初始化扩展和执行component 的 bootstrap方法,该方法主要用于一些component的启动工作。

    三、执行请求

    上面讲的内容其实都是在为运行应用而做的准备,应用的运行,可能要分为几个步骤,我们先从 yiiaseApplication::run 方法说起

     1 <?php
     2 namespace yiiase;
     3 
     4 use Yii;
     5 abstract class Application extends Module
     6 {
     7     ...
     8     public function run()
     9     {
    10         try {
    11             $this->state = self::STATE_BEFORE_REQUEST;
    12             $this->trigger(self::EVENT_BEFORE_REQUEST);
    13 
    14             $this->state = self::STATE_HANDLING_REQUEST;
    15             // 非常重要:捕获路由,处理路由以及根据路由规则调用对应的方法
    16             $response = $this->handleRequest($this->getRequest());
    17 
    18             $this->state = self::STATE_AFTER_REQUEST;
    19             $this->trigger(self::EVENT_AFTER_REQUEST);
    20 
    21             $this->state = self::STATE_SENDING_RESPONSE;
    22             $response->send();
    23 
    24             $this->state = self::STATE_END;
    25 
    26             return $response->exitStatus;
    27         } catch (ExitException $e) {
    28             $this->end($e->statusCode, isset($response) ? $response : null);
    29             return $e->statusCode;
    30         }
    31     }
    32     
    33     // 由yiiwebApplication实现了该抽象方法
    34     abstract public function handleRequest($request);
    35 
    36 }

    精华部分:

    yiiwebApplication::handleRequest 相关代码如下:

    主要分为三部分:

    1.  解析路由
    2.  运行路由指定的控制器操作
    3.  响应客户端请求

     1 <?php
     2 namespace yiiweb;
     3 
     4 use Yii;
     5 use yiiaseInvalidRouteException;
     6 use yiihelpersUrl;
     7 
     8 class Application extends yiiaseApplication
     9 {
    10     ...
    11     public function handleRequest($request)
    12     {
    13         // 解析路由
    14         if (empty($this->catchAll)) {
    15             try {
    16                 list($route, $params) = $request->resolve();
    17             } catch (UrlNormalizerRedirectException $e) {
    18                 $url = $e->url;
    19                 if (is_array($url)) {
    20                     if (isset($url[0])) {
    21                         // ensure the route is absolute
    22                         $url[0] = '/' . ltrim($url[0], '/');
    23                     }
    24                     $url += $request->getQueryParams();
    25                 }
    26 
    27                 return $this->getResponse()->redirect(Url::to($url, $e->scheme), $e->statusCode);
    28             }
    29         } else {
    30             $route = $this->catchAll[0];
    31             $params = $this->catchAll;
    32             unset($params[0]);
    33         }
    34         try {
    35             // 运行路由指定的控制器操作
    36             Yii::debug("Route requested: '$route'", __METHOD__);
    37             $this->requestedRoute = $route;
    38             $result = $this->runAction($route, $params);
    39 
    40             // 响应客户端请求
    41             if ($result instanceof Response) {
    42                 return $result;
    43             }
    44 
    45             $response = $this->getResponse();
    46             if ($result !== null) {
    47                 $response->data = $result;
    48             }
    49 
    50             return $response;
    51         } catch (InvalidRouteException $e) {
    52             throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'), $e->getCode(), $e);
    53         }
    54     }
    55 }

    重点放在第二部分:

    yiiaseModule::runAction

    分为两部分:
    1.   创建controller实例:yiiaseModule::createController
    1) 注意里面有一个controllerMap优先的原则
    2) yiiaseModule::createControllerByID
    3) gii/debug没有被解析到giiController/debugController的原因
    4) 控制器必须是SiteController的原因
    5) controller都得是yiiaseController的子类

    yiiaseModule中相关代码如下:

     1 <?php
     2 namespace yiiase;
     3 
     4 use Yii;
     5 use yiidiServiceLocator;
     6 
     7 class Module extends ServiceLocator
     8 {
     9     const EVENT_BEFORE_ACTION = 'beforeAction';
    10     const EVENT_AFTER_ACTION = 'afterAction';
    11     ...
    12     public function runAction($route, $params = [])
    13     {
    14         // 1. 创建controller实例
    15         $parts = $this->createController($route);
    16         if (is_array($parts)) {
    17             /* @var $controller Controller */
    18             list($controller, $actionID) = $parts;
    19             $oldController = Yii::$app->controller;
    20             Yii::$app->controller = $controller;
    21             // 运行controller的action
    22             $result = $controller->runAction($actionID, $params);
    23             if ($oldController !== null) {
    24                 Yii::$app->controller = $oldController;
    25             }
    26 
    27             return $result;
    28         }
    29 
    30         $id = $this->getUniqueId();
    31         throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');
    32     }
    33 
    34     ....
    35     public function createController($route)
    36     {
    37         // 访问省略路由的时候,默认就是site
    38         if ($route === '') {
    39             $route = $this->defaultRoute;
    40         }
    41         ...  
    42 
    43         // module and controller map take precedence(controllerMap优先的原则)  
    44         if (isset($this->controllerMap[$id])) {
    45             $controller = Yii::createObject($this->controllerMap[$id], [$id, $this]);
    46             return [$controller, $route];
    47         }
    48 
    49         // 这段代码可以用来解释:在地址栏输入 index.php?r=gii,这个路由就被解析到gii模块了,而不是giiController
    50         // 不过像gii这种的module,它既然也是一个独立的module,所以势必最终还是会重新走createController方法,即我们还是可以通过controllerMap映射阻断他的这种解析。
    51         $module = $this->getModule($id);
    52         if ($module !== null) {
    53             return $module->createController($route);
    54         }
    55 
    56         if (($pos = strrpos($route, '/')) !== false) {
    57             $id .= '/' . substr($route, 0, $pos);
    58             $route = substr($route, $pos + 1);
    59         }
    60 
    61         $controller = $this->createControllerByID($id);
    62         if ($controller === null && $route !== '') {
    63             $controller = $this->createControllerByID($id . '/' . $route);
    64             $route = '';
    65         }
    66        
    67         // 返回yiiaseController的实例和操作ID
    68         return $controller === null ? false : [$controller, $route];
    69     }
    70 
    71     public function createControllerByID($id)
    72     {
    73         ...
    74         
    75         // 这段代码可以解释:类文件不是class Site而是class SiteController
    76         $className = preg_replace_callback('%-([a-z0-9_])%i', function ($matches) {
    77                 return ucfirst($matches[1]);
    78             }, ucfirst($className)) . 'Controller';
    79         $className = ltrim($this->controllerNamespace . '\' . str_replace('/', '\', $prefix) . $className, '\');
    80         if (strpos($className, '-') !== false || !class_exists($className)) {
    81             return null;
    82         }
    83         
    84         // 必须是yiiaseController的子类
    85         if (is_subclass_of($className, 'yiiaseController')) {
    86             $controller = Yii::createObject($className, [$id, $this]);
    87             return get_class($controller) === $className ? $controller : null;
    88         } elseif (YII_DEBUG) {
    89             throw new InvalidConfigException('Controller class must extend from \yii\base\Controller.');
    90         }
    91 
    92         return null;
    93     }
    94 }

    2.  运行controller的action:yiiaseController::runAction

    下面是yiiaseController的相关部分代码:

     1 <?php
     2 namespace yiiase;
     3 
     4 use Yii;
     5 use yiidiInstance;
     6 use yiidiNotInstantiableException;
     7 
     8 class Controller extends Component implements ViewContextInterface
     9 {
    10     const EVENT_BEFORE_ACTION = 'beforeAction';
    11     const EVENT_AFTER_ACTION = 'afterAction';
    12     ...
    13     public function runAction($id, $params = [])
    14     {
    15         $action = $this->createAction($id);
    16         ...
    17 
    18         // call beforeAction on modules
    19         // yii::$app就是一个独立的module,由于这里的 $module 指的是 yiiwebApplication,所以 yiiwebApplication::beforeAction先被调起
    20         // 其含义指的是先发起对应用级别的 beforeAction 事件调用。yiiwebApplication::beforeAction 这里指的是 yiiaseModule::beforeAction
    21         foreach ($this->getModules() as $module) {
    22             if ($module->beforeAction($action)) {
    23                 array_unshift($modules, $module);
    24             } else {
    25                 $runAction = false;
    26                 break;
    27             }
    28         }
    29 
    30         $result = null;
    31         // 确认runAction为true(应用级别的beforeAction事件返回结果都是true)
    32         // 再调用 controller 级别的 beforeAction 事件。这里指的是 yiiwebApplication::beforeAction方法
    33         if ($runAction && $this->beforeAction($action)) {
    34             // run the action
    35             $result = $action->runWithParams($params);
    36             
    37             // 将返回结果交给afterAction处理,这是预留的另外一个事件,方便大家使用  
    38             $result = $this->afterAction($action, $result);
    39 
    40             // call afterAction on modules
    41             // 继续调用 module的afterAction,处理一些应用级别的afterAction事件
    42             foreach ($modules as $module) {
    43                 /* @var $module Module */
    44                 $result = $module->afterAction($action, $result);
    45             }
    46         }
    47         ...
    48     }
    49 
    50     public function createAction($id)
    51     {
    52         // 判断actionID,如果是空,则使用默认的action
    53         if ($id === '') {
    54             $id = $this->defaultAction;
    55         }
    56         
    57         // action的映射关系,跟controller有点像,如果actions方法中有配置,则执行actions方法配置的优先策略
    58         $actionMap = $this->actions();
    59         if (isset($actionMap[$id])) {
    60             return Yii::createObject($actionMap[$id], [$id, $this]);
    61         }
    62 
    63         if (preg_match('/^(?:[a-z0-9_]+-)*[a-z0-9_]+$/', $id)) {
    64             $methodName = 'action' . str_replace(' ', '', ucwords(str_replace('-', ' ', $id)));
    65             if (method_exists($this, $methodName)) {
    66                 // 通过反射,严格要求此方法是public可访问的
    67                 $method = new ReflectionMethod($this, $methodName);
    68                 if ($method->isPublic() && $method->getName() === $methodName) {
    69                     //  yiiaseInlineAction 的实例
    70                     return new InlineAction($id, $this, $methodName);
    71                 }
    72             }
    73         }
    74 
    75         return null;
    76     }
    77 }

    总结:

    1) yiiaseController::createAction: 返回yiiaseInlineAction 的实例
    2) 同创建controller实例类似,注意里面有一个controllerMap优先的原则
    3) yiiaseModule::beforeAction
    4)yiiaseInlineAction::runWithParams 代码如下:

     1 <?php
     2 namespace yiiase;
     3 
     4 use Yii;
     5 
     6 class InlineAction extends Action
     7 {
     8     ...
     9     public function runWithParams($params)
    10     {
    11         $args = $this->controller->bindActionParams($this, $params);
    12         Yii::debug('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
    13         if (Yii::$app->requestedParams === null) {
    14             Yii::$app->requestedParams = $args;
    15         }
    16         // 调用 controller::action方法
    17         return call_user_func_array([$this->controller, $this->actionMethod], $args);
    18     }
    19 }

     action执行完了之后,这一切的结果,交由yiiaseModule::runAction方法处理,当然,这个方法也只是一个过程,这个方法的调用,源于最初的运行应用的方法 yiiwebApplication::handleRequest 方法。

    参考链接:

    http://www.manks.top/yii2-analysis-prev-init.html

    http://www.manks.top/yii2-analysis-run-action.html

  • 相关阅读:
    MySQL:批量修改表的排序规则
    Python黑客编程3网络数据监听和过滤
    springboot redis 项目实战 完整篇
    C#读取U盘序列号
    AS3,ReferenceError: Error #1056: 无法为 ×× 创建属性 ×××。
    知道挖掘机如何从货车上下来吗?
    一次进销存软件架构的实践(二)——业务外观层设计
    重构你的软件企业
    遇到了火狐扩展全部丢失的问题
    分布式系统设计原理与方案
  • 原文地址:https://www.cnblogs.com/hld123/p/14677105.html
Copyright © 2011-2022 走看看