调用 registerCoreComponents 函数
/**
* Registers the core application components.
* @see setComponents
*/
protected function registerCoreComponents()
{
$components=array(
'coreMessages'=>array(
'class'=>'CPhpMessageSource',
'language'=>'en_us',
'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages',
),
'db'=>array(
'class'=>'CDbConnection',
),
'messages'=>array(
'class'=>'CPhpMessageSource',
),
'errorHandler'=>array(
'class'=>'CErrorHandler',
),
'securityManager'=>array(
'class'=>'CSecurityManager',
),
'statePersister'=>array(
'class'=>'CStatePersister',
),
'urlManager'=>array(
'class'=>'CUrlManager',
),
'request'=>array(
'class'=>'CHttpRequest',
),
'format'=>array(
'class'=>'CFormatter',
),
);
$this->setComponents($components);
}
首先一个数组,调用setComponents,将该数组传递进去。
setComponents如下:
public function setComponents($components,$merge=true)
{
foreach($components as $id=>$component){
$this->setComponent($id,$component,$merge);
}
public function setComponent($id,$component,$merge=true)
{
if($component===null)
{
unset($this->_components[$id]);
return;
}
elseif($component instanceof IApplicationComponent)
{
$this->_components[$id]=$component;
if(!$component->getIsInitialized())
$component->init();
return;
}
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);
}
else{
$this->_componentConfig[$id]=$component;
}
}
如果$component 为null,注销掉$this->_components里面对应的key
如果$component实现了IApplicationComponent接口,很显然,该$component是数组
如果$this->_components的对应的key存在,即$this->_components[$id]存在,呢么
判断$component['class'] 存在,并且 通过get_class 获取 $this->_components[$id] 的类名,判断是否和$component['class']相等。如果都为true,注销掉$this->_components[$id],将$this->_componentConfig[$id]=$component,返回,
判断$component['class']不存在,或者$this->_components[$id]的类名和$component['class']不等,呢么
foreach 循环$component, key value
循环体里面,如果key不等于字符串'class',$this->_components[$id]->$key=$value;
如果$this->_componentConfig[$id]存在,$merge为true
类CMap调用其静态方法mergeArray,方法如下:
public static function mergeArray($a,$b)
{
$args=func_get_args();
$res=array_shift($args);
while(!empty($args))
{
$next=array_shift($args);
foreach($next as $k => $v)
{
if(is_integer($k))
isset($res[$k]) ? $res[]=$v : $res[$k]=$v;
elseif(is_array($v) && isset($res[$k]) && is_array($res[$k]))
$res[$k]=self::mergeArray($res[$k],$v);
else
$res[$k]=$v;
}
}
return $res;
}
将合并后的对象赋值给$this->_componentConfig[$id]
最后$this->_componentConfig[$id]=$component;
返回到构造函数。
调用配置方法configure(),将配置数组传递进去。
public function configure($config)
{
if(is_array($config))
{
foreach($config as $key=>$value){
$this->$key=$value;
}
}
}
该方法很简单,就是判断是数组,循环该数组,
循环体中将$value的值赋值个$this->$key.
$config的数组体是:
array(
'name'=>'Yii Blog Demo',
// preloading 'log' component
'preload'=>array('log'),
// autoloading model and component classes
'import'=>array(
'application.models.*',
'application.components.*',
),
'defaultController'=>'post',
‘components’=>array(
),
'params'=>'.../'
);
$this 指的是CWebApplication,因为其父类的父类的父类有魔术方法__set,__get,所以在给其属性设置值得时候,会自动调用CComponent的__set方法,
public function __set($name,$value)
{
$setter='set'.$name;
if(method_exists($this,$setter))
return $this->$setter($value);
elseif(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
{
// duplicating getEventHandlers() here for performance
$name=strtolower($name);
if(!isset($this->_e[$name]))
$this->_e[$name]=new CList;
return $this->_e[$name]->add($value);
}
elseif(is_array($this->_m))
{
foreach($this->_m as $object)
{
if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))
return $object->$name=$value;
}
}
if(method_exists($this,'get'.$name))
throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
array('{class}'=>get_class($this), '{property}'=>$name)));
else
throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
array('{class}'=>get_class($this), '{property}'=>$name)));
}
2个参数,第一个是属性,第二个是值。
参数分别是:‘name’,‘preload’,‘import’,‘defaultController’,‘components’,‘params’
$setter = 'setname' ,‘setpreload’,'setimport','setdefaultController','setcomponents','setparams'
判断这些方法如果存在,调用这些方法,由于属性name,preload,defaultController存在,故直接赋值,而import,components,params不存在,调用setImport,setComponents,setparams方法:
public function setImport($aliases)
{
foreach($aliases as $alias)
Yii::import($alias);
}
循环$aliases,即配置里面配置的内容。
调用Yii的import方法,将值传入。方法如下:
public static function import($alias,$forceInclude=false)
{
if(isset(self::$_imports[$alias])) // previously imported
return self::$_imports[$alias];
if(class_exists($alias,false) || interface_exists($alias,false))
return self::$_imports[$alias]=$alias;
if(($pos=strrpos($alias,'\\'))!==false) // a class name in PHP 5.3 namespace format
{
$namespace=str_replace('\\','.',ltrim(substr($alias,0,$pos),'\\'));
if(($path=self::getPathOfAlias($namespace))!==false)
{
$classFile=$path.DIRECTORY_SEPARATOR.substr($alias,$pos+1).'.php';
if($forceInclude)
{
if(is_file($classFile))
require($classFile);
else
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.',array('{alias}'=>$alias)));
self::$_imports[$alias]=$alias;
}
else
self::$classMap[$alias]=$classFile;
return $alias;
}
else
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory.',
array('{alias}'=>$namespace)));
}
if(($pos=strrpos($alias,'.'))===false) // a simple class name
{
if($forceInclude && self::autoload($alias))
self::$_imports[$alias]=$alias;
return $alias;
}
$className=(string)substr($alias,$pos+1);
$isClass=$className!=='*';
if($isClass && (class_exists($className,false) || interface_exists($className,false)))
return self::$_imports[$alias]=$className;
if(($path=self::getPathOfAlias($alias))!==false)
{
if($isClass)
{
if($forceInclude)
{
if(is_file($path.'.php'))
require($path.'.php');
else
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.',array('{alias}'=>$alias)));
self::$_imports[$alias]=$className;
}
else
self::$classMap[$className]=$path.'.php';
return $className;
}
else // a directory
{
if(self::$_includePaths===null)
{
self::$_includePaths=array_unique(explode(PATH_SEPARATOR,get_include_path()));
if(($pos=array_search('.',self::$_includePaths,true))!==false)
unset(self::$_includePaths[$pos]);
}
array_unshift(self::$_includePaths,$path);
if(self::$enableIncludePath && set_include_path('.'.PATH_SEPARATOR.implode(PATH_SEPARATOR,self::$_includePaths))===false)
self::$enableIncludePath=false;
return self::$_imports[$alias]=$path;
}
}
else
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory or file.',
array('{alias}'=>$alias)));
}
按下不表。
setparams方法:
/**
* Sets user-defined parameters.
* @param array $value user-defined parameters. This should be in name-value pairs.
*/
public function setParams($value)
{
$params=$this->getParams();
foreach($value as $k=>$v)
$params->add($k,$v);
}
先调用getParams方法,获取该方法返回信息
/**
* Returns user-defined parameters.
* @return CAttributeCollection the list of user-defined parameters
*/
public function getParams()
{
if($this->_params!==null)
return $this->_params;
else
{
$this->_params=new CAttributeCollection;
$this->_params->caseSensitive=true;
return $this->_params;
}
}
显然$this->_params为null,故
新实例化一个CAttributeCollection对象。由于该类继承CMap,并且CMap继承CComponent,且实现IteratorAggregate,ArrayAccess,Countable3个接口。
CMap类包含2个私有属性,private $_d = array();private $_r = false;
$_r是否只读。
由于CAttributeCollection类没有构造函数,所以调用其父类CMap的构造函数。
CMap的构造函数如下:
public function __construct($data=null,$readOnly=false)
{
if($data!==null)
$this->copyFrom($data);
$this->setReadOnly($readOnly);
}
默认参数。$data是null,调用$this->setReadOnly,设置$_r为false
调用对象的caseSensitive = true
将该对象返回。
回到setParams方法里面。
循环$value,这里的$value是配置参数里面对应的,
array(
// this is displayed in the header section
'title'=>'My Yii Blog',
// this is used in error pages
'adminEmail'=>'[email protected]',
// number of posts displayed per page
'postsPerPage'=>10,
// maximum number of comments that can be displayed in recent comments portlet
'recentCommentCount'=>10,
// maximum number of tags that can be displayed in tag cloud portlet
'tagCloudCount'=>20,
// whether post comments need to be approved before published
'commentNeedApproval'=>true,
// the copyright information displayed in the footer section
'copyrightInfo'=>'Copyright © 2009 by My Company.',
)
循环调用刚获取的CAttributeCollects对象的add方法, 参数分别是key,value
add方法如下:
public function add($key,$value)
{
if($this->caseSensitive)
parent::add($key,$value);
else
parent::add(strtolower($key),$value);
}
由于改对象caseSensitive为true,调用其父类的方法add,
方法如下:
public function add($key,$value)
{
if(!$this->_r)
{
if($key===null)
$this->_d[]=$value;
else
$this->_d[$key]=$value;
}
else
throw new CException(Yii::t('yii','The map is read only.'));
}
将key,value,放入$_d中。
返回到构造函数。
调用attachBehaviors方法,空的。
调用preloadComponents方法。如下:
/**
* Loads static application components.
*/
protected function preloadComponents()
{
foreach($this->preload as $id)
$this->getComponent($id);
}
循环$this->preload,这个值来源于配置参数,在构造函数调用$this->configre($confige)赋值的。
array('log');
在循环体里面调用getComponent,将‘log’传递进去。
方法如下:
public function getComponent($id,$createIfNull=true)
{
if(isset($this->_components[$id]))
return $this->_components[$id];
elseif(isset($this->_componentConfig[$id]) && $createIfNull)
{
$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;
}
}
}
知道$this->_components为空。
而且$this->_componentConfig里面的log对应的数组有2个元素。
$config = array(
Class =>CLogRouter
Routes =>array(
Class =>CFileLogRoute
Levels =>error,warning
)
)
调用Yii::trace(xxxxxxx);
调用
Yii的
createComponent
方法,将
$configure传入。
public static function createComponent($config)
{
if(is_string($config))
{
$type=$config;
$config=array();
}
elseif(isset($config['class']))
{
$type=$config['class'];
unset($config['class']);
}
else
throw new CException(Yii::t('yii','Object configuration must be an array containing a "class" element.'));
if(!class_exists($type,false))
$type=Yii::import($type,true);
if(($n=func_num_args())>1)
{
$args=func_get_args();
if($n===2)
$object=new $type($args[1]);
elseif($n===3)
$object=new $type($args[1],$args[2]);
elseif($n===4)
$object=new $type($args[1],$args[2],$args[3]);
else
{
unset($args[0]);
$class=new ReflectionClass($type);
// Note: ReflectionClass::newInstanceArgs() is available for PHP 5.1.3+
// $object=$class->newInstanceArgs($args);
$object=call_user_func_array(array($class,'newInstance'),$args);
}
}
else
$object=new $type;
foreach($config as $key=>$value)
$object->$key=$value;
return $object;
}
参数的结构是$config = array(
Class =>CLogRouter
Routes =>array(
Class =>CFileLogRoute
Levels =>error,warning
)
)
满足条件,将$config[‘Class’]赋值给$type,即$type=CLogRouter
注销掉key class,
调用class_exists判断$type类是否存在,第二个参数是false,不自动导入。如果不存在,调用Yii 的import方法导入
调用func_num_args,获取参数个数。参数等于1,实例化一个$type。
Foreach循环 $config key=>val
此时
$config = array(
Routes =>array(
Class =>CFileLogRoute
Levels =>error,warning
)
)
循环体内部调用$type(即CLogRouter)的属性,
由于没有该属性,自动调用其父类的魔术方法__set。如下
判断setroutes方法存在
Foreach 循环 配置参数
$this->_routes 键值填充。
将返回的对象赋值给$component(CLogRouter 继承CApplicationComponent )
调用其init()方法
public function init()
{
parent::init();
foreach($this->_routes as $name=>$route)
{
$route=Yii::createComponent($route);
$route->init();
$this->_routes[$name]=$route;
}
Yii::getLogger()->attachEventHandler('onFlush',array($this,'collectLogs'));
Yii::app()->attachEventHandler('onEndRequest',array($this,'processLogs'));
}
按下不表。
返回构造函数
调用初始化方法init()
protected function init()
{
parent::init();
// preload 'request' so that it has chance to respond to onBeginRequest event.
$this->getRequest();
}
调用父类的初始化方法空的。
调用getRequest()方法,获取组件
public function getRequest()
{
return $this->getComponent('request');
}
调用getComponent方法,将request字符串传递进去。
//至此,初始化完毕。