热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Yii系统启动trace源码

  摘要:学习使用Yii框架,总觉得使用起来不顺手,趁这几天工作不忙,就trace下框架源码吧。这篇先来trace从入口文件到控制器启动的过程。1.入口文件index.php$yi

  摘要:学习使用Yii框架,总觉得使用起来不顺手,趁这几天工作不忙,就trace下框架源码吧。这篇先来trace从入口文件到控制器启动的过程。

1. 入口文件index.php

$yii=dirname(__FILE__).'/../../lib/yii-1.1.16/yii.php';
require_once($yii);
Yii::createWebApplication($config)->run();

  

2. system.YiiBase

public static function createWebApplication($config=null) {
    return self::createApplication('CWebApplication',$config);
}
public static function createApplication($class,$config=null) {
    return new $class($config);
}


  在这里new出了第一个对象,CWebApplication
  继承关系如下:

class CWebApplication » CApplication » CModule » CComponent

  new的时候,调用了构造函数__construct(),这个构造函数是在CApplication这个类里面声明的。
  就是在这个构造函数里面实现了系统初始化工作。

3. system.web.CApplication

  这里只trace部分代码(component相关)

public function __construct($config=null) {
    //tag
    Yii::setApplication($this);
    //作者特意在这里写了注释:设置basePath要尽可能的早,以避免麻烦,估计这个路径是后面很多地方都用到的基础的路径。
    if(isset($config['basePath']))
    {
        $this->setBasePath($config['basePath']);
        unset($config['basePath']);
    }
    //tag2
    $this->registerCoreComponents();
    $this->configure($config);
    $this->preloadComponents();
    $this->init();
}


//tag
这句代码很关键,我们在任何地方调用Yii::app()这个对象就是这就代码实现的
它将创建的CWebApplication实例传入YiiBase::setApplication($app);
代码如下:
public static function setApplication($app)
{
    if(self::$_app===null || $app===null)
        self::$_app=$app;
    else
        throw new CException(Yii::t('yii','Yii application can only be created once.'));
}

//tag2
$this->registerCoreComponents();
   这个方法用来注册核心组件。
   这个函数虽然是在CApplication的构造函数里面声明的,但是根据继承关系,当new CWebApplication的时候,这个构造函数实际上是在CWebApplication里面执行的,而registerCoreComponents()方法,在CWebApplication和CApplication里面都有声明,并且在CWebApplication->registerCoreComponents()里面有调用parent::registerCoreComponents();所以是这样的:
   在CWebApplication里面注册的核心组件有:session,assetManager,user,themeManager,authManager,clientScript,widgetFactory
   在CApplication里面注册的核心组件有:coreMessages,db,messages,errorHandler,securityManager,statePersister,urlManager,request,format
   在这个方法最后调用了$this->setComponents($components);

下面trace 这个方法,有玄机

3.1 system.base.CModule.setComponents()

public function setComponents($components,$merge=true) {
    //这里的$components就是上面的配置数组
    foreach($components as $id=>$component)
        $this->setComponent($id,$component,$merge);
}
public function setComponent($id,$component,$merge=true) {
    //这里当设置某个component的配置项是null,那么就相当于从这里删除掉了
    if($component===null)
    {
        unset($this->_components[$id]);
        return;
    }
    //这里的$component显然是数组,并不是这个接口类的实例,所以不会进入这个if,它出现的意义暂不深究
    elseif($component instanceof IApplicationComponent)
    {
        $this->_components[$id]=$component;

        if(!$component->getIsInitialized())
            $component->init();

        return;
    }
    //在一个请求到来,系统初始化的时候,$this->_components这个数组肯定是空的,下面这个if的意义在于在系统初始化之后(执行了CWepApplication和CApplication里面的registerCoreComponents()方法之后),当在protected/config/main.php配置数组里面配置有组件的数组,调用第三步构造函数里面的$this->configure($config);的时候,会使用魔术方法调用该方法,那么就会进入到这个if里面,最这个已经存在的component再进行配置修改。
    elseif(isset($this->_components[$id]))
    {
        if(isset($component['class']) && get_class($this->_components[$id])!==$component['class'])
        {
            unset($this->_components[$id]);
            $this->_componentConfig[$id]=$component; //we should ignore merge here
            return;
        }

        foreach($component as $key=>$value)
        {
            if($key!=='class')
                $this->_components[$id]->$key=$value;
        }
    }
    elseif(isset($this->_componentConfig[$id]['class'],$component['class'])
        && $this->_componentConfig[$id]['class']!==$component['class'])
    {
        $this->_componentConfig[$id]=$component; //we should ignore merge here
        return;
    }
    //在这里处理合并配置项
    if(isset($this->_componentConfig[$id]) && $merge)
        $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);
    //其实在系统初始化的时候,就是执行下面这一句话的,把registerCoreComponents()里面的配置数组,复制进CWepApplication的属性里面。
    else
        $this->_componentConfig[$id]=$component;
}

  至于为什么在系统初始化的时候,先把核心组件的配置信息(其实就是组件类名)先写进CWepApplication的私有属性$_componentConfig里面,然后才将配置文件里面的组件配置信息更新进来。
  我想原因应该是:自己在配置文件里面是不用关心系统启动需要的组件的,在框架里面先把框架启动需要的组件类先加进来,然后自己在配置文件里面的配置只要合并进来就好了。

3.2 system.base.CModule.configure()

  这个函数里面也有玄机
  先上代码:

public function configure($config) {
    if(is_array($config))
    {
        foreach($config as $key=>$value)
            $this->$key=$value;
    }
}

  乍看很普通,就是把$config里面的值,按照键值对,复制进对象里面的属性和值。
  容易忽略一点是,在继承关系的最顶层是CComponent,里面有魔术方法__set(),看看这个魔术方法是什么样子(只取出关键代码):

public function __set($name,$value) {
    $setter='set'.$name;
    if(method_exists($this,$setter))
        return $this->$setter($value);
    //下面的代码先省略
}

  比如在/config/main.php里面配置了组件的配置信息,像这样:

'components'=>array(

    'user'=>array(
        // enable COOKIE-based authentication
        'allowAutoLogin'=>true,
    ),
)

  那么就相当于在configure()方法里面执行:$this->compOnents= array();
  因为类里面并没有声明components这个属性,所以对这个属性赋值的时候会调用魔术方法__set()
  在魔术方法里面,先检查setcomponents()这个方法是否存在,当然是存在的,这个方法被声明在system.base.CModule里面;
  所以,我们在/config/main.php里面对组件(components)进行配置的时候,那么配置项并不是简单的复制进CWepApplication的属性里面,而是调用了setComponents()方法。

3.3 system.base.CModule.preloadComponents()

  预加载组件的加载
  在config/main.php里面的一个配置项:’preload’=>array(‘log’),

protected function preloadComponents() {
    //因为在__construct()里面之前已经调用了$this->configure($config);
    //所以这个$this->preload = array('log');
    foreach($this->preload as $id)
        $this->getComponent($id);
}
public function getComponent($id,$createIfNull=true) {
    if(isset($this->_components[$id]))
        return $this->_components[$id];
    elseif(isset($this->_componentConfig[$id]) && $createIfNull)
    {
        //肯定是进到这里面
        //$this->_componentConfig是在__construct的$this->registerCoreComponents();里面初始化赋值的,log组件的初始化配置项只有一个class
        $config=$this->_componentConfig[$id];
        if(!isset($config['enabled']) || $config['enabled'])
        {
            Yii::trace("Loading \"$id\" application component",'system.CModule');
            unset($config['enabled']);
            $component=Yii::createComponent($config);
            $component->init();
            return $this->_components[$id]=$component;
        }
    }
}

3.4 system.web.CWepApplication.init()

  在__construct里面最后一个函数是init()方法;

//system.web.CWepApplication
protected function init() {
    //父类的init()方法是空
    parent::init();
    // preload 'request' so that it has chance to respond to onBeginRequest event.
    $this->getRequest();
}
//system.base.CApplication
public function getRequest() {
    //这个方法前面也见过了,就是初始化一个组件,供一次请求的后续使用
    return $this->getComponent('request');
}

  至此一个请求的初始化就完成了。

<完>


推荐阅读
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • PHP中的单例模式与静态变量的区别及使用方法
    本文介绍了PHP中的单例模式与静态变量的区别及使用方法。在PHP中,静态变量的存活周期仅仅是每次PHP的会话周期,与Java、C++不同。静态变量在PHP中的作用域仅限于当前文件内,在函数或类中可以传递变量。本文还通过示例代码解释了静态变量在函数和类中的使用方法,并说明了静态变量的生命周期与结构体的生命周期相关联。同时,本文还介绍了静态变量在类中的使用方法,并通过示例代码展示了如何在类中使用静态变量。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • RouterOS 5.16软路由安装图解教程
    本文介绍了如何安装RouterOS 5.16软路由系统,包括系统要求、安装步骤和登录方式。同时提供了详细的图解教程,方便读者进行操作。 ... [详细]
  • 如何在php文件中添加图片?
    本文详细解答了如何在php文件中添加图片的问题,包括插入图片的代码、使用PHPword在载入模板中插入图片的方法,以及使用gd库生成不同类型的图像文件的示例。同时还介绍了如何生成一个正方形文件的步骤。希望对大家有所帮助。 ... [详细]
  • 本文介绍了一个Magento模块,其主要功能是实现前台用户利用表单给管理员发送邮件。通过阅读该模块的代码,可以了解到一些有关Magento的细节,例如如何获取系统标签id、如何使用Magento默认的提示信息以及如何使用smtp服务等。文章还提到了安装SMTP Pro插件的方法,并给出了前台页面的代码示例。 ... [详细]
  • 指示|厂家_UDS协议
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了UDS协议相关的知识,希望对你有一定的参考价值。UDS的第二类诊断服务,数据传输 ... [详细]
  • PHP输出缓冲控制Output Control系列函数详解【PHP】
    后端开发|php教程PHP,输出缓冲,Output,Control后端开发-php教程概述全景网页源码,vscode如何打开c,ubuntu强制解锁,sts启动tomcat慢,sq ... [详细]
  • 在单位的一台4cpu的服务器上部署了esxserver,挂载了6个虚拟机,目前运行正常。在安装部署过程中,得到了cnvz.net论坛精华区 ... [详细]
  • Yii framwork 应用小窍门
    Yiiframework应用小窍门1.YiiFramework]如何获取当前controller的名称?下面语句就可以获取当前控制器的名称了!Php代码 ... [详细]
author-avatar
快乐xin_yi
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有