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() は http リクエストの処理を開始するための Controller オブジェクトを作成します。
$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()はコントロールを呼び出します アクションはありませんin run():
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->run($actionID) まず、Action オブジェクトが作成されます:
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); }
ここで、コントローラーがアクション メソッドを直接呼び出しているのではなく、アクション オブジェクトを必要とすることがわかります。コントローラー アクションを実行します。これにより、コントローラー メソッドによってマップされたアクション オブジェクトとアクション処理が統合されます。つまり、両方の形式のアクション処理が IAction インターフェイスの run() 呼び出しに統合されます。
IAction インターフェースには run()、getId()、getController() の 3 つのメソッドの実装が必要です。Yii が提供する CAction クラスは、Controller と Id を提供するコンストラクターを必要とし、getId() と getController() の処理を実装します。 Action クラスは CAction Just 継承から派生します。
CInlineAction web/action では、run() は非常に単純なプロセスであり、コントローラーのアクション メソッドを呼び出します。 beforeControllerAction() は実際には true を返すため、アクション オブジェクトは実際にはコントローラーの runActionWithFilters() を通じて実行されます
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); }
フィルターはありません。runAction() は、以前に作成されたアクション オブジェクト ) メソッドを最終的に呼び出す run() です:
class CInlineAction extends CAction { public function run() { $method=’action’.$this->getId(); $this->getController()->$method(); } }
各フィルターは IFilter インターフェースを実装する必要があります。フィルターによって実装された preFilter() メソッドは、アクションが実行可能であると判断された場合は true を返し、それ以外の場合は false を返します。
if( $filter1->preFilter())
if($filter2->preFilter())if($filtern->preFilter())
$action->run()$filtern- >postFilter( )
$filter2->postFilter()$filter1->postFilter()
実行中の最も一般的な操作は、レンダー ビュー ファイル: renderPartial() および render() です。 render() は、ビュー ファイルを処理した後、結果をレイアウト ファイルに書き込みます。
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); }
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; } }
public function runAction($action) { $priorAction=$this->_action; $this->_action=$action; if($this->beforeAction($action)) { $action->run(); $this->afterAction($action); } $this->_action=$priorAction; }
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))); }
processOutput は、CSS または JS スクリプトを head に追加するなど、レンダリング結果を処理します。
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 フレームワーク分析 (4) - WebApplication の run 関数の詳細な分析です。さらに関連するコンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。