ホームページ  >  記事  >  php教程  >  Yii フレームワーク分析 (4) - WebApplication の実行機能の詳細分析

Yii フレームワーク分析 (4) - WebApplication の実行機能の詳細分析

黄舟
黄舟オリジナル
2016-12-27 11:15:441351ブラウズ

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);
}

getViewFile($view) は $view のフルパスを取得します:
$view は '/' で始まり、システムビューディレクトリを開始ディレクトリ + $view+.php として使用します
$view にエイリアスが含まれている場合は、実際のエイリアスのパス

他の人は、modele view ディレクトリを開始ディレクトリ + $view+.php として使用します

サードパーティのレンダラーが $config で設定されていない場合、renderFile() は実際に Yii 自体によって提供される renderInternal() を呼び出して、ビュー ファイル:

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

Yii のレンダラーは、PHP 自体をテンプレート システムとして使用します。
public function runAction($action)
{
    $priorAction=$this->_action;
    $this->_action=$action;
    if($this->beforeAction($action))
    {
        $action->run();
        $this->afterAction($action);
    }
    $this->_action=$priorAction;
}

render() は実際に最初に部分ビュー ファイルをレンダリングし、次に renderFile レイアウトファイルをレンダリングし、ビュー ファイルの結果を $content 変数として渡します。

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) に注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。