免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2138 | 回复: 1
打印 上一主题 下一主题

[转]Yii PHP 框架分析(四) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-01-06 22:39 |只看该作者 |倒序浏览
[转]Yii PHP 框架分析(四)





http://hi.baidu.com/delphiss/blo ... f9dfc0d0c86a26.html

Yii应用的入口脚本最后一句启动了WebApplication

Yii::createWebApplication($config)->run();

CApplication:

  1. public function run(){   $this->onBeginRequest(new CEvent($this));   $this->processRequest();   $this->onEndRequest(new CEvent($this));}

  2. 复制代码
复制代码
processRequest()开始处理请求,由CWebApplication实现:
  1. 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);}

  2. 复制代码
复制代码
urlManager应用组件的parseUrl() 创建了$route (形式为controllerID/actionID的字符串),runController()创建Controller对象开始处理http请求。

$route 的值可能存在以下几种情况:
- 为空: 用 defaultController 值代替;
- “moduleID/controllerID/actionID”: module下的
- “controllerID/actionID” : 最常见的形式
- “folder1/folder2/controllerID/actionID” 多级目录下的控制器

runController首先调用createController()创建控制器对象
  1. 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;   }}

  2. 复制代码
复制代码
createController() 返回一个创建好的控制器对象和actionID, runController()调用控制器的init()方法和run($actionID)来运行控制器:
  1. 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)));}

  2. 复制代码
复制代码
$controller->init()里没有动作, run():
  1. 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);}

  2. 复制代码
复制代码
$controller->run($actionID)里首先创建了Action对象:
  1. 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);}

  2. 复制代码
复制代码
这里可以看到控制器并不是直接调用了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方法:
  1. class CInlineAction extends CAction{public function run(){   $method='action'.$this->getId();   $this->getController()->$method();}}

  2. 复制代码
复制代码
回到
  1. $controller->run($actionID)
复制代码
  1. 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);}

  2. 复制代码
复制代码
  1. Yii::app()->beforeControllerAction() 实际是固定返回true的,所以action对象实际是通过控制器的runActionWithFilters()被run的

  2. 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;   }}

  3. 复制代码
复制代码
没有过滤器,runAction()就是最终要调用前面创建的action对象的run()方法:
  1. public function runAction($action){   $priorAction=$this->_action;   $this->_action=$action;   if($this->beforeAction($action))   {    $action->run();    $this->afterAction($action);   }   $this->_action=$priorAction;}

  2. 复制代码
复制代码
每个filter都要实现IFilter接口,filter实现的preFilter()方法在$action->run()之前调用,如果判断action可以执行则返回true,否则返回false
  1. if($filter1->preFilter())if($filter2->preFilter())   if($filtern->preFilter())    $action->run()    $filtern->postFilter()   $filter2->postFilter()$filter1->postFilter()

  2. 复制代码
复制代码
在action里最常见的操作就是render view文件: renderPartial()和render()。render()在处理view文件后会把结果放入layout文件内。
  1. 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)));}

  2. 复制代码
复制代码
getViewFile($view)获得$view的完整路径:
$view 以 ‘/’开头的,以系统views目录作为起始目录+$view+.php
$view含有别名的,查找别名的真实路径
其他的以modele view目录作为起始目录+$view+.php

如果没有在$config里配置第三方的renderer,renderFile() 里实际是调用了yii自身提供的renderInternal()来render view文件:
  1. 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))));   }}

  2. 复制代码
复制代码
Yii的renderer用的是php本身作为模板系统:
  1. 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_);}

  2. 复制代码
复制代码
render()的实际上是先renderPartial view文件,然后renderFile layoutfile,并将view文件的结果做为$content变量传入。
  1. 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;}

  2. 复制代码
复制代码
processOutput将render的结果再做处理,比如在head加上css或js脚本等。

  1. 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;}

  2. 复制代码
复制代码

论坛徽章:
0
2 [报告]
发表于 2012-01-06 22:52 |只看该作者
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP