调用 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);
<!--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')); }
按下不表。 返回构造函数 调用初始化方法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字符串传递进去。 //至此,初始化完毕。