您好,欢迎访问一九零五行业门户网

Yii框架分析(四)——WebApplication的run函数详细解析

yii应用的入口脚本最后一句启动了webapplication
yii::createwebapplication($config)->run();
capplication:
public function run() { $this->onbeginrequest(new cevent($this)); $this->processrequest(); $this->onendrequest(new cevent($this)); }
processrequest()开始处理请求,由cwebapplication实现:
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); }
urlmanager应用组件的parseurl() 创建了$route (形式为controllerid/actionid的字符串),runcontroller()创建controller对象开始处理http请求。
$route 的值可能存在以下几种情况:
- 为空: 用 defaultcontroller 值代替;
- “moduleid/controllerid/actionid”: module下的
- “controllerid/actionid” : 最常见的形式
- “folder1/folder2/controllerid/actionid” 多级目录下的控制器
runcontroller首先调用createcontroller()创建控制器对象
public function createcontroller($route,$owner=null) { // $owner为空则设置为$this,即 $_app对象 if($owner===null) $owner=$this; // $route为空设置为defaultcontroller,在$config里配置 if(($route=trim($route,’/'))===”) $route=$owner->defaultcontroller; $casesensitive=$this->geturlmanager()->casesensitive; $route.=’/'; // 逐一取出 $route 按 ‘/’分割后的第一段进行处理 while(($pos=strpos($route,’/'))!==false) { // $id 里存放的是 $route 第一个 ‘/’前的部分 $id=substr($route,0,$pos); if(!preg_match(‘/^\w+$/’,$id)) return null; if(!$casesensitive) $id=strtolower($id); // $route 存放’/’后面部分 $route=(string)substr($route,$pos+1); if(!isset($basepath)) // 完整$route的第一段 { // 如果$id在controllermap[]里做了映射 // 直接根据$id创建controller对象 if(isset($owner->controllermap[$id])) { return array( yii::createcomponent($owner->controllermap[$id],$id,$owner===$this?null:$owner), $this->parseactionparams($route), ); } // $id 是系统已定义的 module,根据$id取得module对象作为$owner参数来createcontroller if(($module=$owner->getmodule($id))!==null) return $this->createcontroller($route,$module); // 控制器所在的目录 $basepath=$owner->getcontrollerpath(); $controllerid=”; } else $controllerid.=’/'; $classname=ucfirst($id).’controller’; $classfile=$basepath.directory_separator.$classname.’.php’; // 控制器类文件存在,则require并创建控制器对象&返回 if(is_file($classfile)) { if(!class_exists($classname,false)) require($classfile); if(class_exists($classname,false) && is_subclass_of($classname,’ccontroller’)) { $id[0]=strtolower($id[0]); return array( new $classname($controllerid.$id,$owner===$this?null:$owner), $this->parseactionparams($route), ); } return null; } // 未找到控制器类文件,可能是多级目录,继续往子目录搜索 $controllerid.=$id; $basepath.=directory_separator.$id; } }
createcontroller() 返回一个创建好的控制器对象和actionid, runcontroller()调用控制器的init()方法和run($actionid)来运行控制器:
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))); }
$controller->init()里没有动作, run():
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); }
$controller->run($actionid)里首先创建了action对象:
public function createaction($actionid) { // 为空设置为defaultaction if($actionid===”) $actionid=$this->defaultaction; // 控制器里存在 ‘action’.$actionid 的方法,创建cinlineaction对象 if(method_exists($this,’action’.$actionid) && strcasecmp($actionid,’s')) // we have actions method return new cinlineaction($this,$actionid); // 否则根据actions映射来创建action对象 else return $this->createactionfrommap($this->actions(),$actionid,$actionid); }
这里可以看到控制器并不是直接调用了action方法,而是需要一个action对象来运行控制器动作,这样就统一了控制器方法和actions映射的action对象对action的处理,即两种形式的action处理都统一为iaction接口的run()调用。
iaction接口要求实现run(),getid(),getcontroller () 三个方法,yii提供的caction类要求构造函数提供controller和id并实现了getid()和getcontroller ()的处理,action类从caction继承即可。
cinlineaction在web/action下,run()是很简单的处理过程,调用了controller的action方法:
class cinlineaction extends caction { public function run() { $method=’action’.$this->getid(); $this->getcontroller()->$method(); } }
回到 $controller->run($actionid)
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); }
yii::app()->beforecontrolleraction() 实际是固定返回true的,所以action对象实际是通过控制器的runactionwithfilters()被run的
public function runactionwithfilters($action,$filters) { // 控制器里没有设置过滤器 if(empty($filters)) $this->runaction($action); else { // 创建过滤器链对象并运行 $prioraction=$this->_action; $this->_action=$action; cfilterchain::create($this,$action,$filters)->run(); $this->_action=$prioraction; } }
没有过滤器,runaction()就是最终要调用前面创建的action对象的run()方法:
public function runaction($action) { $prioraction=$this->_action; $this->_action=$action; if($this->beforeaction($action)) { $action->run(); $this->afteraction($action); } $this->_action=$prioraction; }
每个filter都要实现ifilter接口,filter实现的prefilter()方法在$action->run()之前调用,如果判断action可以执行则返回true,否则返回false
if($filter1->prefilter())
if($filter2->prefilter())
if($filtern->prefilter())
$action->run()
$filtern->postfilter()
$filter2->postfilter()
$filter1->postfilter()
在action里最常见的操作就是render view文件: renderpartial()和render()。render()在处理view文件后会把结果放入layout文件内。
public function renderpartial($view,$data=null,$return=false,$processoutput=false) { if(($viewfile=$this->getviewfile($view))!==false) { $output=$this->renderfile($viewfile,$data,true); if($processoutput) $output=$this->processoutput($output); if($return) return $output; else echo $output; } else throw new cexception(yii::t(‘yii’,'{controller} cannot find the requested view “{view}”.’, array(‘{controller}’=>get_class($this), ‘{view}’=>$view))); }
getviewfile($view)获得$view的完整路径:
$view 以 ‘/’开头的,以系统views目录作为起始目录+$view+.php
$view含有别名的,查找别名的真实路径
其他的以modele view目录作为起始目录+$view+.php
如果没有在$config里配置第三方的renderer,renderfile() 里实际是调用了yii自身提供的renderinternal()来render view文件:
public function renderfile($viewfile,$data=null,$return=false) { $widgetcount=count($this->_widgetstack); // 如果配置了其他的viewrenderer if(($renderer=yii::app()->getviewrenderer())!==null) $content=$renderer->renderfile($this,$viewfile,$data,$return); else // yii 自身的render $content=$this->renderinternal($viewfile,$data,$return); if(count($this->_widgetstack)===$widgetcount) return $content; else { $widget=end($this->_widgetstack); throw new cexception(yii::t(‘yii’,'{controller} contains improperly nested widget tags in its view “{view}”. a {widget} widget does not have an endwidget() call.’,array(‘{controller}’=>get_class($this), ‘{view}’=>$viewfile, ‘{widget}’=>get_class($widget)))); } }
yii的renderer用的是php本身作为模板系统:
public function renderinternal($_viewfile_,$_data_=null,$_return_=false) { // extract函数将$_data_从数组中将变量导入到当前的符号表 if(is_array($_data_)) extract($_data_,extr_prefix_same,’data’); else $data=$_data_; if($_return_) { ob_start(); ob_implicit_flush(false); require($_viewfile_); return ob_get_clean(); } else require($_viewfile_); }
render()的实际上是先renderpartial view文件,然后renderfile layoutfile,并将view文件的结果做为$content变量传入。
public function render($view,$data=null,$return=false) { $output=$this->renderpartial($view,$data,true); if(($layoutfile=$this->getlayoutfile($this->layout))!==false) $output=$this->renderfile($layoutfile,array(‘content’=>$output),true); $output=$this->processoutput($output); if($return) return $output; else echo $output; }
processoutput将render的结果再做处理,比如在head加上css或js脚本等。
public function processoutput ($output) { yii::app()->getclientscript()->render($output); // if using page caching, we should delay dynamic output replacement if($this->_dynamicoutput!==null && $this->iscachingstackempty()) $output=$this->processdynamicoutput($output); if($this->_pagestates===null) $this->_pagestates=$this->loadpagestates(); if(!empty($this->_pagestates)) $this->savepagestates($this->_pagestates,$output); return $output; }
以上就是yii框架分析(四)——webapplication的run函数详细解析的内容。
其它类似信息

推荐信息