通常的默认启动脚本:
<code>
Yii::createWebApplication(Ugc::loadConfig('base'))->run();
</code>
其执行以及CWebApplication::run()方法源码如下:
CWebApplication::run();
<code>
/**
* Runs the application.
* This method loads static application components. Derived classes usually overrides this
* method to do more application-specific tasks.
* Remember to call the parent implementation so that static application components are loaded.
*/
public function run()
{
if($this->hasEventHandler('onBeginRequest'))
$this->onBeginRequest(new CEvent($this));
$this->processRequest();
if($this->hasEventHandler('onEndRequest'))
$this->onEndRequest(new CEvent($this));
}
</code>
代码分析:
依据以上源码,该方法执行逻辑依次为
"onBeginRequest" 事件控制器 -> "processRequest()"方法 -> "onEndRequest" 事件控制器
现在分析processRequest()方法执行流程:
由于run();方法是在 CWebApplication 对象的父级对象 CApplication对象中存在,所以在run();方法中, $this 默认指向 “CWebApplication”,
因此我们分析CWebApplication::processRequest();方法,源码如下:
<code>
public function processRequest()
{
if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0]))
{
$route=$this->catchAllRequest[0];
foreach(array_splice($this->catchAllRequest,1) as $name=>$value)
$_GET[$name]=$value;
}
else
$route=$this->getUrlManager()->parseUrl($this->getRequest());
$this->runController($route);
}
</code>
在该方法中,最终由runController(); 方法处理用户请求。
继续分析CWebApplication::runController();方法,源码如下:
<code>
public function runController($route)
{
if(($ca=$this->createController($route))!==null)
{
list($controller,$actionID)=$ca;
$oldController=$this->_controller;
$this->_controller=$controller;
$controller->init();
$controller->run($actionID);
$this->_controller=$oldController;
}
else
throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',
array('{route}'=>$route===''?$this->defaultController:$route)));
}
</code>
在该方法中,最终由"$controller->run($actionID);" 代码片段执行用户请求。根据上下文以及CWebApplication::createController();方法
源码得知,该方法返回数组,结构如“list($controller,$actionID)=$ca;” 由此根据$controller在YII框架中的继承关系得知 $controller对象
继承自“CController”,所以这里所执行的"$controller->run($actionID);" 中的run();方法也就是CController中的“run();“方法。
CController::run(); 方法源码如下:
<code>
public function run($actionID)
{
if(($action=$this->createAction($actionID))!==null)
{
if(($parent=$this->getModule())===null)
$parent=Yii::app();
if($parent->beforeControllerAction($this,$action))
{
$this->runActionWithFilters($action,$this->filters());
$parent->afterControllerAction($this,$action);
}
}
else
$this->missingAction($actionID);
}
</code>
到此为止,已经分析到具体action执行阶段,其中"$this->runActionWithFilters($action,$this->filters());"负责具体执行action,同时根据
filters参数对输出过滤。
那么由此可以得出,在我们开发的Controller中,action的render方法也就是CController::render();
“CController::render()”方法源码如下:
<code>
public function render($view,$data=null,$return=false)
{
if($this->beforeRender($view))
{
$output=$this->renderPartial($view,$data,true);
if(($layoutFile=$this->getLayoutFile($this->layout))!==false)
$output=$this->renderFile($layoutFile,array('content'=>$output),true);
$this->afterRender($view,$output);
$output=$this->processOutput($output);
if($return)
return $output;
else
echo $output;
}
}
</code>
由此,我们可以看出,这里对页面的输出是直接echo, 并且此时整个layout已经组合完成。那么从这里倒回到我们最初的地方:
CWebApplication::run();
<code>
/**
* Runs the application.
* This method loads static application components. Derived classes usually overrides this
* method to do more application-specific tasks.
* Remember to call the parent implementation so that static application components are loaded.
*/
public function run()
{
if($this->hasEventHandler('onBeginRequest'))
$this->onBeginRequest(new CEvent($this));
$this->processRequest();
if($this->hasEventHandler('onEndRequest'))
$this->onEndRequest(new CEvent($this));
}
</code>
分析页面执行一次操作进行的处理流程:
在capplication的__construct中执行
$this->registerCoreComponents();//加载核心组件
在这里实际上会首先 调用cwebapplication的registerCoreComponents()函数,在函数中会执行parent::registerCoreComponents();
即执行capplication的registerCoreComponents(),
注册核心组件,然后注册cwebapplication里面的核心组件。
在这两个函数中都会调用cmodule的setComponents(); //2次
下面还有1次
$this->configure($config); //设置配置去除了bashPath的config数组成为当前类的属性
这里注意会用到ccomponent.php的魔术方法__set($name,$value)
在config文件里面的数组注册为类的属性的时候会调用这个魔术函数,因为类文件本身没有声明config中的key代表的属性,即不存在属性所以会调用__set
public function __set($name,$value)
{
$setter='set'.$name;
if(method_exists($this,$setter))
return $this->$setter($value);
如果存在set.$name这个方法,设置config数组成属性的时候,就要用到这个。
比如在config中有component数组
'components'=>array(
'user'=>array(
// enable cookie-based authentication
'allowAutoLogin'=>true,
),
/*
'db'=>array(
'connectionString' => 'sqlite:protected/data/blog.db',
'tablePrefix' => 'tbl_',
),
*/
// uncomment the following to use a MySQL database
'db'=>array(
'connectionString' => 'mysql:host=localhost;dbname=testdrive',
'emulatePrepare' => true,
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => 'tbl_',
),
'errorHandler'=>array(
// use 'site/error' action to display errors
'errorAction'=>'site/error',
),
/*
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'post/<id:\d+>/<title:.*?>'=>'post/view',
'posts/<tag:.*?>'=>'post/index',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),*/
'log'=>array(
'class'=>'CLogRouter',
'routes'=>array(
array(
'class'=>'CFileLogRoute',
'levels'=>'error, warning',
),
// uncomment the following to show log messages on web pages
/*
array(
'class'=>'CWebLogRoute',
),
*/
),
),
)
在构造函数中执行configure
public function configure($config)
{
if(is_array($config))
{
foreach($config as $key=>$value)
$this->$key=$value;
}
}
相当于$this->components=array()//config配置文件中的components对应的数组集合;实际上执行ccomponent.php的魔术方法__set
所以这里会调用一次'set'.$name;//$name=components ,即第3次调用setComponents()
public function setComponents($components,$merge=true)
{
// phptest2($components,__FILE__.__METHOD__.__line__);
//debug_print_backtrace();
foreach($components as $id=>$component)
{
// echo "id:".$id."; component:".$component."<br/>";
if($component instanceof IApplicationComponent) //暂时测试没遇到这种情况
{
$this->setComponent($id,$component);
//phptest2("if",__FILE__.__METHOD__.__line__);
}
else if(isset($this->_componentConfig[$id]) && $merge) //如果已经存在key,则合并之前的
{
$this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);
//phptest2("else if zhoujian $id",__FILE__.__METHOD__.__line__);
}
else //新建
{
$this->_componentConfig[$id]=$component;
//phptest2("else",__FILE__.__METHOD__.__line__);
}
}
phptest2($this->_componentConfig,__FILE__.__METHOD__.__line__);
}
二preloadComponents()
capplication 的__structor$this->preloadComponents();会调用cmodule的preloadComponents()
然会 foreach($this->preload as $id) $this->getComponent($id);
调用cmodule的getComponent($id);其过程大致如下;
如果this->_components找到了$id,则表示已经创建过,即初始化过,
如果没有找到,而且之前注册的_componentConfig存在这个$id,同时$createIfNull为true
,同时component的配置文件中没有设置enable或者设置了为true,则调用Yii::createComponent();
创建这个component,并且初始化,同时写入到>_components数组中