调用 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
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]
public function configure($config)
{
if(is_array($config))
{
foreach($config as $key=>$value){
$this->$key=$value;
}
}
}
该方法很简单,就是判断是数组,循环该数组,
'name'=>'Yii Blog Demo',
'preload'=>array('log'),
'import'=>array(
'application.models.*',
'application.components.*',
),
‘components’=>array(
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个参数,第一个是属性,第二个是值。
public function setImport($aliases)
{
foreach($aliases as $alias)
Yii::import($alias);
}
循环$aliases,即配置里面配置的内容。
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)));
}
按下不表。
/**
* 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,故
public function __construct($data=null,$readOnly=false)
{
if($data!==null)
$this->copyFrom($data);
$this->setReadOnly($readOnly);
}
默认参数。$data是null,调用$this->setReadOnly,设置$_r为false
array(
// this is displayed in the header section
'title'=>'My Yii Blog',
// this is used in error pages
'adminEmail'=>'webmaster@example.com',
// 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.',
)
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中。
/**
* Loads static application components.
*/
protected function preloadComponents()
{
foreach($this->preload as $id)
$this->getComponent($id);
}
循环$this->preload,这个值来源于配置参数,在构造函数调用$this->configre($confige)赋值的。
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);
<!--EndFragment--> 调用 Yii的 createComponent 方法,将 $configure传入。<!--EndFragment-->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。如下
<!--EndFragment-->判断setroutes方法存在
Foreach 循环 配置参数
$this->_routes 键值填充。
<!--EndFragment-->
将返回的对象赋值给$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'));
}
按下不表。
protected function init()
{
parent::init();
// preload 'request' so that it has chance to respond to onBeginRequest event.
$this->getRequest();
}
调用父类的初始化方法空的。
public function getRequest()
{
return $this->getComponent('request');
}
调用getComponent方法,将request字符串传递进去。