文档章节

yii2底层源码分析 [ 2.0 版本 ]

botkenni
 botkenni
发布于 2017/09/03 22:52
字数 2058
阅读 10
收藏 0
点赞 0
评论 0

Yii2底层分析

我是从入口处分析的。
$mysiteRoot/frontend
首先:$mysiteRoot/frontend/index.php

$application = new yii\web\Application($config);//先从这入手
$application->run();//先不急,后面会提到

从上面注释的位置入口

$config为配置文件,这里我们来看看是如何加载配置文件内容的。
顺着application我们能找到:yii\web\Application.php

class Application extends \yii\base\Application

yii\web\Application.php中没有构造函数,所以我们顺理成章的找找
它的父类也就是\yii\base\Application,看看父类里面是否有构造函数
\yii\base\Application没有让我们失望,
构造方法如下:

abstract class Application extends Module{

.....

public function __construct($config = [])
{
    Yii::$app = $this;
    $this->setInstance($this);//将\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里会用到,为getInstance提供

这一句是指向\yii\base\Module

public static function setInstance($instance)//module模块里会用到,为getInstance提供
{
    if ($instance === null) {
        unset(Yii::$app->loadedModules[get_called_class()]);
    } else {
        Yii::$app->loadedModules[get_class($instance)] = $instance;
    }
}

这句意思是:将当前类名和存储类的对象变量加入Yii::$app->loadedModules['yii\web\Application']数组中
这样直接通过Yii::$app->loadedModules['yii\web\Application']就可以直接调用这个类
重要的用处在于后面的使用如:
在Module里,也就是module使用的时候,可以通过self::getInstance()获取App对象,类似于Yii::$app。这个研究的比较浅,以后再深入,有疑问的童鞋可以深入

Yii::$app = $this;
$this->setInstance($this);

这两句做的操作是一样的,其实是有所不同的。

Yii::$app = $this;

指的是通过Yii::$app可以调用yii\web\Application及其父类所有的方法

Yii::$app->loadedModules['yii\web\Application']//也能同样做到

loadedModules是一个数组,存放成员类的。它除了能调用当前,还能调用其它许许多多的类....

$this->preInit($config);

这一句是将配置文件中的一些变量设置别名,主要是针对路径、URL之类的

$this->registerErrorHandler($config);

加载异常处理,这块比较深就先不研究了,觉得比较浅的童鞋可以接着补充哈

Component::__construct($config);

这一句指向Object

public function __construct($config = [])
{
    if (!empty($config)) {
        Yii::configure($this, $config);//将配置文件里面的所有配置信息赋值给Object,由于Object是大部分类的基类,实际上也就是交给了yii\web\Application 您可以Yii::$app->配置参数来访问配置文件中的内容
    }
    $this->init();//下面会细分析
}
foreach ($this->coreComponents() as $id => $component) {//加载默认组件components
    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'];
    }
}

这个就是把当前的配置文件config变量中内容交给Object 再就是讲components默认需要加载的组件类,赋到config配置文件变量中。
Object是基础类,所以绝大部分类都能直接调用配置文件中配置内容
如:
var_dump(Yii::$app->name);
实际上config文件的数组中有name属性

return [
    'id' => 'app-frontend',
    'name' => '环球在线',
......

再回到Object

public function __construct($config = [])
{
    if (!empty($config)) {
        Yii::configure($this, $config);
    }// 这以上已经执行完了
    $this->init();//接下来分析这一句
}
$this->init();

这句实际上执行的是yii\base\Module.php


/*
  取出控制器的命名空间,您也可以理解为路径(* 注:第一次加载它的时候。)
  表面看起来没有太多的意义,实则不然,yii2的大部分组件都是以Object为基类的,
  所以init函数很重要,控制器、模型、模块module,自定义组件等都可以去实现init方法。
  比如说默认的控制器SiteController吧。在里面写一个init方法,当你访问site控制器下任意的$route路径,
  都会先执行init方法。作用大不?其它组件同样如此。
*/
public function init()
{
    if ($this->controllerNamespace === null) {
        $class = get_class($this);
        if (($pos = strrpos($class, '\\')) !== false) {
            $this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';
        }
    }
}

至此第一部分执行完了,再看第二部分吧

$application = new yii\web\Application($config);//分析完成
$application->run();//加载主要组件,运行默认控制器

接下拆分 $application->run吧
用ide指向直接到了\yii\base\Application.php

public function run()
{
    try {

        $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;

    } catch (ExitException $e) {

        $this->end($e->statusCode, isset($response) ? $response : null);
        return $e->statusCode;

    }
}
        $response = $this->handleRequest($this->getRequest());

摘出来的这句:

$this->getRequest()//获取Request对象

这个没啥可说的,获取Request对象

$this->handleRequest($this->getRequest());

通过指向\yii\web\Application.php

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);//运行控制器中的Acition,下面有详细介绍
        if ($result instanceof Response) {
            return $result;
        } else {
            $response = $this->getResponse();

/*这个是加载yii\base\Response类,在外部可以Yii::$app->get('response')、Yii::$app->getResponse()、Yii::$app->response 等等方式来加载response类,主要用来加载http状态,及头信息,如301,302,404,ajax头等等的获取*/

            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);//运行控制器

我们再看看这句指向:\yii\base\Module.php

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) . '".');
    }
}

Module里有一段:

$controller = Yii::createObject($this->controllerMap[$id], [$id, $this]);

其实在

$this->createController($route)

这个时候创建了控制器对象

下面看看如何加载action的。

$result = $controller->runAction($actionID, $params);//运行使用控制器加载 action方法

上面这句指向:

public function runAction($id, $params = [])
{
    $action = $this->createAction($id);//创建action
    if ($action === null) {
        throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
    }

    Yii::trace("Route to run: " . $action->getUniqueId(), __METHOD__);

    if (Yii::$app->requestedAction === null) {
        Yii::$app->requestedAction = $action;
    }

    $oldAction = $this->action;
    $this->action = $action;

    $modules = [];
    $runAction = true;

    // 加载默认模块如: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
        // run the action
        $result = $action->runWithParams($params);//执行控制器里的action

        $result = $this->afterAction($action, $result);//执行beforeAction


        // call afterAction on modules
        foreach ($modules as $module) {
            /* @var $module Module */
            $result = $module->afterAction($action, $result);
        }
    }

    $this->action = $oldAction;

    return $result;
}
        $result = $action->runWithParams($params);//执行控制器里的action

这里才是真正执行action的地方

首先弄清楚$action是什么类?这里可以var_dump一下就清楚了,刚开始我也被编辑器迷糊了找半天
$action类是yii\base\InlineAction

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;
    }

    return call_user_func_array([$this->controller, $this->actionMethod], $args);//用控制器类去执行action方法,并且带上参数。
}

本人使用的ide是phpstorm。顺着index.php的入口一步步读代码,可能有些地方分析不太完整,以后会慢慢补充的。

© 著作权归作者所有

共有 人打赏支持
botkenni
粉丝 15
博文 377
码字总数 378586
作品 0
西城
程序员
yii2源码分析之执行基本流程

用yii2框架用了将近2年,一直都没有去看过它底层源码, 马上快不用了,最近对其源码研究一番,哈哈 废话少说,上代码, 入口文件是web/index.php

china_lx1
04/22
0
0
Yii2 RC 版本发布下载,三年磨一剑!

今天YII2.0 RC版本已经发布,此版本现已提供下载: https://github.com/yiisoft/yii2/releases/tag/2.0.0-rc Yii 当前有两个主要版本:1.1 和 2.0。 1.1 版是上代的老版本,现在处于维护状态...

chinaphp
2014/09/28
4.4K
48
著名 PHP 框架 Yii2 将使用 Ruby 重写?

大名鼎鼎的PHP框架Yii官网首页昨天出现了一则重磅消息,声称将使用Ruby代替PHP语言来重写下一个大版本Yii2。 原文内容翻译如下: 我们很高兴地宣布我们关于Yii2的发展决定,从2013年4月1日,...

oschina
2013/04/02
7.1K
33
Yii2 源码分析 - 入口文件执行流程

以 yii 2.0.14 高级版的 frontend 为例,从 frontend/web/index.php 开始 入口文件看着就这么几行,简单的很,那他是怎么通过这几行来运行应用的呢?先看 Yii.php 内的逻辑 接下来,就是重头...

botkenni
05/03
0
0
Yii框架学习笔记(一)引入Yii框架

Yii Framework是一个基于组件、用于开发大型 Web 应用的高性能 PHP 框架。Yii提供了今日Web 2.0应用开发所需要的几乎一切功能。Yii是最有效率的PHP框架之一。Yii是创始人薛强的心血结晶,于2...

flute小行
2014/04/11
0
0
安装指南: 带有 RBAC 的 Yii2 高级模板

在这里我介绍一种快速简单的方法来在 Yii2 高级模版中安装 RBAC(Role Based Access Control) 系统 安装: Yii2 高级模板 在这里使用 安装 yii2 高级模板。假如你的机器中没有 请下载 它的最新...

首席烤地瓜
2016/11/10
17
0
Yii2 使用 RESTful 写API接口 实例

Yii2使用RESTful?其实Yii2框架本身就对RESTful是友好支持的,具体可以看官方文档(http://www.yiichina.com/doc/guide/2.0/rest-quick-start),或者去看源码,都是可以的注意:我们使用resTf...

小田天
2016/11/19
28
0
liufee/yii2-swoole

yii2 swoole 让yii2运行在swoole上。如果您在使用中遇到问题或者想学习yii2结合swoole可以加qq群258780872一起讨论 性能 运行在swoole上的yii2是运行在php-fpm上yii2的5倍以上,而且一句代码...

liufee
2017/12/26
0
0
yii2源码分析之组件实例化流程

读本篇文章,建议先看看我之前的文章php依赖注入 到此,现在我们正式开始分析yii2框架组件构造流程 我们先从yiidiServiceLocator(服务定位器)入手吧!!让我们先看个实例: use yiidiServiceLoc...

china_lx1
06/27
0
0
composer yii2 mailer redis 包制作

1.github 创建项目 ,项目名称:yii2mailerqueue 2.制作并上传包 a.首先clone yii2_mailerqueue 项目 b.打开yii2mailerqueue文件,把开发测试完成的源码文件夹 src 复制黏贴到yii2mailerqueue根...

罗培海
2017/10/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

idea tomcat 远程调试

tomcat 配置 编辑文件${tomcat_home}/bin/catalina.sh,在文件开头添加如下代码。    CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7829" Idea端配......

qwfys
今天
1
0
遍历目录下的文件每250M打包一个文件

#!/usr/bin/env python # -*- utf-8 -*- # @Time : 2018/7/20 0020 下午 10:16 # @Author : 陈元 # @Email : abcmeabc@163.com # @file : tarFile.py import os import tarfile import thr......

寻爱的小草
今天
1
0
expect同步文件&expect指定host和要同步的文件&构建文件分发系统&批量远程执行命令

20.31 expect脚本同步文件 expect通过与rsync结合,可以在一台机器上把文件自动同步到多台机器上 编写脚本 [root@linux-5 ~]# cd /usr/local/sbin[root@linux-5 sbin]# vim 4.expect#!/...

影夜Linux
今天
1
0
SpringBoot | 第九章:Mybatis-plus的集成和使用

前言 本章节开始介绍数据访问方面的相关知识点。对于后端开发者而言,和数据库打交道是每天都在进行的,所以一个好用的ORM框架是很有必要的。目前,绝大部分公司都选择MyBatis框架作为底层数...

oKong
今天
12
0
win10 上安装解压版mysql

1.效果 2. 下载MySQL 压缩版 下载地址: https://downloads.mysql.com/archives/community/ 3. 配置 3.1 将下载的文件解压到合适的位置 我最终将myql文件 放在:D:\develop\mysql 最终放的位...

Lucky_Me
今天
2
0
linux服务器修改mtu值优化cpu

一、jumbo frames 相关 1、什么是jumbo frames Jumbo frames 是指比标准Ethernet Frames长的frame,即比1518/1522 bit大的frames,Jumbo frame的大小是每个设备厂商规定的,不属于IEEE标准;...

问题终结者
今天
1
0
expect脚本同步文件expect脚本指定host和要同步的文件 构建文件分发系统批量远程执行命令

expect脚本同步文件 在一台机器上把文件同步到多台机器上 自动同步文件 vim 4.expect [root@yong-01 sbin]# vim 4.expect#!/usr/bin/expectset passwd "20655739"spawn rsync -av ro...

lyy549745
今天
1
0
36.rsync下 日志 screen

10.32/10.33 rsync通过服务同步 10.34 linux系统日志 10.35 screen工具 10.32/10.33 rsync通过服务同步: rsync还可以通过服务的方式同步。那需要开启一个服务,他的架构是cs架构,客户端服务...

王鑫linux
今天
1
0
matplotlib 保存图片时的参数

简单绘图 import matplotlib.pyplot as pltplt.plot(range(10)) 保存为csv格式,放大后依然很清晰 plt.savefig('t1.svg') 普通保存放大后会有点模糊文件大小20多k plt.savefig('t5.p...

阿豪boy
今天
3
0
java 8 复合Lambda 表达式

comparator 比较器复合 //排序Comparator.comparing(Apple::getWeight);List<Apple> list = Stream.of(new Apple(1, "a"), new Apple(2, "b"), new Apple(3, "c")) .collect(......

Canaan_
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部