yii源代码之旅(4)

 调用 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::tracexxxxxxx;

<!--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-->

 

将返回的对象赋值给$componentCLogRouter 继承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字符串传递进去。 //至此,初始化完毕。    

猜你喜欢

转载自miaojihao.iteye.com/blog/1853383
今日推荐