Yii PHP 框架分析(四)

  1.   Yii PHP 框架分析(四) 
  2.  
  3. 作者:wdy 
  4.  
  5. http://hi.baidu.com/delphiss/blog/item/c15b314f05f9dfc0d0c86a26.html 
  6.  
  7. Yii應用的入口腳本最後一句啓動了WebApplication 
  8.  
  9. Yii::createWebApplication($config)->run(); 
  10.  
  11. CApplication: 
  12.  
  13. public function run() 
  14.    $this->onBeginRequest(new CEvent($this)); 
  15.    $this->proce***equest(); 
  16.    $this->onEndRequest(new CEvent($this)); 
  17.  
  18. proce***equest()開始處理請求,由CWebApplication實現: 
  19.  
  20. public function proce***equest() 
  21.    if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0])) 
  22.    { 
  23.     $route=$this->catchAllRequest[0]; 
  24.     foreach(array_splice($this->catchAllRequest,1) as $name=>$value
  25.      $_GET[$name]=$value
  26.    } 
  27.    else 
  28.     $route=$this->getUrlManager()->parseUrl($this->getRequest()); 
  29.    $this->runController($route); 
  30.  
  31. urlManager應用組件的parseUrl() 創建了$route (形式爲controllerID/actionID的字符串),runController()創建Controller對象開始處理http請求。 
  32.  
  33. $route 的值可能存在以下幾種情況: 
  34. - 爲空: 用 defaultController 值代替; 
  35. - “moduleID/controllerID/actionID”: module下的 
  36. - “controllerID/actionID” : 最常見的形式 
  37. - “folder1/folder2/controllerID/actionID” 多級目錄下的控制器 
  38.  
  39. runController首先調用createController()創建控制器對象 
  40.  
  41.  
  42. public function createController($route,$owner=null) 
  43.    // $owner爲空則設置爲$this,即 $_app對象 
  44.    if($owner===null) 
  45.     $owner=$this
  46.    // $route爲空設置爲defaultController,在$config裏配置 
  47.    if(($route=trim($route,'/'))===''
  48.     $route=$owner->defaultController; 
  49.    $caseSensitive=$this->getUrlManager()->caseSensitive; 
  50.  
  51.    $route.='/'
  52.    // 逐一取出 $route 按 ‘/’分割後的第一段進行處理 
  53.    while(($pos=strpos($route,'/'))!==false) 
  54.    { 
  55.     // $id 裏存放的是 $route 第一個 ‘/’前的部分 
  56.     $id=substr($route,0,$pos); 
  57.     if(!preg_match('/^\w+$/',$id)) 
  58.      return null; 
  59.     if(!$caseSensitive
  60.      $id=strtolower($id); 
  61.     // $route 存放’/’後面部分 
  62.     $route=(string)substr($route,$pos+1); 
  63.     if(!isset($basePath)) // 完整$route的第一段 
  64.     { 
  65.      // 如果$id在controllerMap[]裏做了映射 
  66.      // 直接根據$id創建controller對象 
  67.      if(isset($owner->controllerMap[$id])) 
  68.      { 
  69.       return array
  70.        Yii::createComponent($owner->controllerMap[$id],$id,$owner===$this?null:$owner), 
  71.        $this->parseActionParams($route), 
  72.       ); 
  73.      } 
  74.  
  75.      // $id 是系統已定義的 module,根據$id取得module對象作爲$owner參數來createController 
  76.      if(($module=$owner->getModule($id))!==null) 
  77.       return $this->createController($route,$module); 
  78.      // 控制器所在的目錄 
  79.      $basePath=$owner->getControllerPath(); 
  80.      $controllerID=''
  81.     } 
  82.     else 
  83.      $controllerID.='/'
  84.     $className=ucfirst($id).'Controller'
  85.     $classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php'
  86.     // 控制器類文件存在,則require並創建控制器對象&返回 
  87.     if(is_file($classFile)) 
  88.     { 
  89.      if(!class_exists($className,false)) 
  90.       require($classFile); 
  91.      if(class_exists($className,false) && is_subclass_of($className,'CController')) 
  92.      { 
  93.       $id[0]=strtolower($id[0]); 
  94.       return array
  95.        new $className($controllerID.$id,$owner===$this?null:$owner), 
  96.        $this->parseActionParams($route), 
  97.       ); 
  98.      } 
  99.      return null; 
  100.     } 
  101.     // 未找到控制器類文件,可能是多級目錄,繼續往子目錄搜索 
  102.     $controllerID.=$id
  103.     $basePath.=DIRECTORY_SEPARATOR.$id
  104.    } 
  105.  
  106.  
  107. createController() 返回一個創建好的控制器對象和actionID, runController()調用控制器的init()方法和run($actionID)來運行控制器: 
  108.  
  109. public function runController($route
  110.    if(($ca=$this->createController($route))!==null) 
  111.    { 
  112.     list($controller,$actionID)=$ca
  113.     $oldController=$this->_controller; 
  114.     $this->_controller=$controller
  115.     $controller->init(); 
  116.     $controller->run($actionID); 
  117.     $this->_controller=$oldController
  118.    } 
  119.    else 
  120.     throw new CHttpException( 404, Yii::t('yii','Unable to resolve the request "{route}".'array'{route}'=>$route==='' ? $this->defaultController:$route))); 
  121.  
  122. $controller->init()裏沒有動作, run(): 
  123.  
  124. public function run($actionID
  125.    if(($action=$this->createAction($actionID))!==null) 
  126.    { 
  127.     if(($parent=$this->getModule())===null) 
  128.      $parent=Yii::app(); 
  129.     if($parent->beforeControllerAction($this,$action)) 
  130.     { 
  131.      $this->runActionWithFilters($action,$this->filters()); 
  132.      $parent->afterControllerAction($this,$action); 
  133.     } 
  134.    } 
  135.    else 
  136.     $this->missingAction($actionID); 
  137.  
  138. $controller->run($actionID)裏首先創建了 Action對象: 
  139.  
  140. public function createAction($actionID
  141.    // 爲空設置爲defaultAction 
  142.    if($actionID===''
  143.     $actionID=$this->defaultAction; 
  144.    // 控制器裏存在 'action'.$actionID 的方法,創建CInlineAction對象 
  145.    if(method_exists($this,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method 
  146.     return new CInlineAction($this,$actionID); 
  147.    // 否則根據actions映射來創建Action對象 
  148.    else 
  149.     return $this->createActionFromMap($this->actions(),$actionID,$actionID); 
  150.  
  151. 這裏可以看到控制器並不是直接調用了action方法,而是需要一個Action對象來運行控制器動作,這樣就統一了控制器方法和actions映射的action對象對action的處理,即兩種形式的action處理都統一爲 IAction接口的run()調用。 
  152.  
  153. IAction接口要求實現run(),getId(),getController () 三個方法,Yii提供的CAction類要求構造函數提供Controller和Id並實現了getId()和getController ()的處理,Action類從CAction繼承即可。 
  154.  
  155. CInlineAction在web/action下,run()是很簡單的處理過程,調用了Controller的action方法: 
  156.  
  157. class CInlineAction extends CAction 
  158. public function run() 
  159.    $method='action'.$this->getId(); 
  160.    $this->getController()->$method(); 
  161.  
  162. 回到 $controller->run($actionID
  163.  
  164. public function run($actionID
  165.    if(($action=$this->createAction($actionID))!==null) 
  166.    { 
  167.     if(($parent=$this->getModule())===null) 
  168.      $parent=Yii::app(); 
  169.     if($parent->beforeControllerAction($this,$action)) 
  170.     { 
  171.      $this->runActionWithFilters($action,$this->filters()); 
  172.      $parent->afterControllerAction($this,$action); 
  173.     } 
  174.    } 
  175.    else 
  176.     $this->missingAction($actionID); 
  177.  
  178. Yii::app()->beforeControllerAction() 實際是固定返回true的,所以action對象實際是通過控制器的runActionWithFilters()被run的 
  179.  
  180. public function runActionWithFilters($action,$filters
  181.    // 控制器裏沒有設置過濾器 
  182.    if(emptyempty($filters)) 
  183.     $this->runAction($action); 
  184.    else 
  185.    { 
  186.     // 創建過濾器鏈對象並運行 
  187.     $priorAction=$this->_action; 
  188.     $this->_action=$action
  189.     CFilterChain::create($this,$action,$filters)->run(); 
  190.     $this->_action=$priorAction
  191.    } 
  192.  
  193. 沒有過濾器,runAction()就是最終要調用前面創建的action對象的 run()方法: 
  194.  
  195. public function runAction($action
  196.    $priorAction=$this->_action; 
  197.    $this->_action=$action
  198.    if($this->beforeAction($action)) 
  199.    { 
  200.     $action->run(); 
  201.     $this->afterAction($action); 
  202.    } 
  203.    $this->_action=$priorAction
  204.  
  205. 每個filter都要實現IFilter接口,filter實現的 preFilter()方法在$action->run()之前調用,如果判斷action可以執行則返回true,否則返回false 
  206.  
  207. if($filter1->preFilter()) 
  208. if($filter2->preFilter()) 
  209.    if($filtern->preFilter()) 
  210.     $action->run() 
  211.     $filtern->postFilter() 
  212.    $filter2->postFilter() 
  213. $filter1->postFilter() 
  214.  
  215. 在action裏最常見的操作就是render view文件: renderPartial()和render()。render()在處理view文件後會把結果放入layout文件內。 
  216.  
  217. public function renderPartial($view,$data=null,$return=false,$processOutput=false) 
  218.    if(($viewFile=$this->getViewFile($view))!==false) 
  219.    { 
  220.     $output=$this->renderFile($viewFile,$data,true); 
  221.     if($processOutput
  222.      $output=$this->processOutput($output); 
  223.     if($return
  224.      return $output
  225.     else 
  226.      echo $output
  227.    } 
  228.    else 
  229.     throw new CException(Yii::t('yii','{controller} cannot find the requested view "{view}".'
  230.      array('{controller}'=>get_class($this), '{view}'=>$view))); 
  231.  
  232.  
  233. getViewFile($view)獲得$view的完整路徑: 
  234. $view 以 ‘/’開頭的,以系統views目錄作爲起始目錄+$view+.php 
  235. $view含有別名的,查找別名的真實路徑 
  236. 其他的以modele view目錄作爲起始目錄+$view+.php 
  237.  
  238. 如果沒有在$config裏配置第三方的renderer,renderFile() 裏實際是調用了yii自身提供的renderInternal()來render view文件: 
  239.  
  240. public function renderFile($viewFile,$data=null,$return=false) 
  241.    $widgetCount=count($this->_widgetStack); 
  242.    // 如果配置了其他的ViewRenderer 
  243.    if(($renderer=Yii::app()->getViewRenderer())!==null) 
  244.     $content=$renderer->renderFile($this,$viewFile,$data,$return); 
  245.    else 
  246.     // yii 自身的render 
  247.     $content=$this->renderInternal($viewFile,$data,$return); 
  248.    if(count($this->_widgetStack)===$widgetCount
  249.     return $content
  250.    else 
  251.    { 
  252.     $widget=end($this->_widgetStack); 
  253.     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.'
  254.      array('{controller}'=>get_class($this), '{view}'=>$viewFile'{widget}'=>get_class($widget)))); 
  255.    } 
  256.  
  257. Yii的renderer用的是php本身作爲模板系統: 
  258.  
  259. public function renderInternal($_viewFile_,$_data_=null,$_return_=false) 
  260.    // extract函數將$_data_從數組中將變量導入到當前的符號表 
  261.    if(is_array($_data_)) 
  262.     extract($_data_,EXTR_PREFIX_SAME,'data'); 
  263.    else 
  264.     $data=$_data_
  265.    if($_return_
  266.    { 
  267.     ob_start(); 
  268.     ob_implicit_flush(false); 
  269.     require($_viewFile_); 
  270.     return ob_get_clean(); 
  271.    } 
  272.    else 
  273.     require($_viewFile_); 
  274.  
  275. render()的實際上是先renderPartial view文件,然後renderFile layoutfile,並將view文件的結果做爲$content變量傳入。 
  276.  
  277. public function render($view,$data=null,$return=false) 
  278.    $output=$this->renderPartial($view,$data,true); 
  279.    if(($layoutFile=$this->getLayoutFile($this->layout))!==false) 
  280.     $output=$this->renderFile($layoutFile,array('content'=>$output),true); 
  281.  
  282.    $output=$this->processOutput($output); 
  283.  
  284.    if($return
  285.     return $output
  286.    else 
  287.     echo $output
  288.  
  289. processOutput將render的結果再做處理,比如在head加上css 或js腳本等。 
  290.  
  291. public function processOutput ($output
  292.    Yii::app()->getClientScript()->render($output); 
  293.  
  294.    // if using page caching, we should delay dynamic output replacement 
  295.    if($this->_dynamicOutput!==null && $this->isCachingStackEmpty()) 
  296.     $output=$this->processDynamicOutput($output); 
  297.  
  298.    if($this->_pageStates===null) 
  299.     $this->_pageStates=$this->loadPageStates(); 
  300.    if(!emptyempty($this->_pageStates)) 
  301.     $this->savePageStates($this->_pageStates,$output); 
  302.  
  303.    return $output

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章