代码追踪
First.注册核心组件流程
1. require_once(SITEPARH . ‘/apple/yii/framework/yii.php’);
入口文件代码 Yii::createWebApplication($config)->run();
# yii.php继承于同级下YiiBase.php
public static function createWebApplication($config=null)
{
return self::createApplication('CWebApplication',$config);
}
public static function createApplication($class,$config=null)
{
return new $class($config); # new CWebApplication($config)
}
2. yii/web下CWebApplication.php继承于 yii/base下CApplication
# 调用 CApplication 的 __construct方法
class CWebApplication extends CApplication # CApplication 在yii/base下
# registerCoreComponents注册核心组件
abstract class CApplication extends CModule
{
public function __construct($config=null)
{
Yii::setApplication($this); # self::$_app 赋值
// set basePath at early as possible to avoid trouble
if(is_string($config))
$config=require($config);
if(isset($config['basePath']))
{
$this->setBasePath($config['basePath']);
unset($config['basePath']);
}
else
$this->setBasePath('protected');
Yii::setPathOfAlias('application',$this->getBasePath());
Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
$this->preinit();
$this->initSystemHandlers();
$this->registerCoreComponents();
$this->configure($config);
$this->attachBehaviors($this->behaviors);
$this->preloadComponents();
$this->init();
}
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);
}
}
3. yii/base下CModule
abstract class CModule extends CComponent{
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;
}
}
Second. 调用组件 例: Yii::app()->db
1. Yii::app()
# YiiBase.php
public static function app()
{
return self::$_app; # 上面 First.2 的Yii::setApplication($this); # self::$_app 赋值
}
2. ->db
# abstract class CModule extends CComponent
public function __get($name)
{
if($this->hasComponent($name))
return $this->getComponent($name);
else
return parent::__get($name);
}
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;
}
}
}
# YiiBase.php
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;
}
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)));
}
ps:
目的是对PHP设计模式有进一步认识。(个人理解,欢迎纠错。)
偷摸的把yii2的研究思路记在这。针对我当前遇见的,看官们可以忽略
1. yii\base\Application::run
2. yii\web\Application::handleRequest
3. yii\base\Module::runAction
4. yii\base\Controller::runAction
5. yii\base\InlineAction::runWithParams
6. yii\web\Controller::bindActionParams