您的位置:首页 > 编程语言 > PHP开发

Yii 系统启动 trace源码

2016-05-10 23:20 525 查看
  摘要:学习使用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');
}


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

<完>

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  yii 框架 源码