>php教程 >PHP开发 >Yii 프레임워크 분석(4) - WebApplication의 실행 기능 상세 분석

Yii 프레임워크 분석(4) - WebApplication의 실행 기능 상세 분석

黄舟
黄舟원래의
2016-12-27 11:15:441427검색

Yii 애플리케이션 입력 스크립트의 마지막 문장에서 WebApplication이 시작됩니다

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

CApplication:

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

processRequest()는 CWebApplication에 의해 구현된 요청 처리를 시작합니다. urlManager 애플리케이션 구성 요소의

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

parseUrl()은 $route(controllerID/actionID 형식의 문자열)를 생성하고 runController( ) 컨트롤러 객체 핸들 http 요청 생성을 시작합니다.

$route 값에는 다음과 같은 상황이 있을 수 있습니다.
- 비어 있음: defaultController 값으로 대체됨
- "moduleID/controllerID/actionID":
- 모듈 / 아래의 "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->Action 객체는 run($actionID)에서 처음 생성됩니다:

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

여기에 있을 수 있습니다. 컨트롤러가 액션 메소드를 직접 호출하지 않고, 컨트롤러 액션을 실행하기 위해서는 액션 객체가 필요하다는 것을 알 수 있었습니다. 이는 컨트롤러 메소드에 매핑된 액션 객체에 의한 액션 처리, 즉 두 가지 형태의 액션 처리를 일원화한 것입니다. 이들은 모두 IAction 인터페이스의 run() 호출로 통합됩니다.

IAction 인터페이스에는 run(), getId(), getController() 세 가지 메소드의 구현이 필요합니다. Yii에서 제공하는 CAction 클래스는 Controller와 Id를 제공하는 생성자를 필요로 하며 getId 처리를 구현합니다. () 및 getController(). Action 클래스는 CAction에서 상속할 수 있습니다.

웹/액션 아래의 CInlineAction인 run()은 컨트롤러의 액션 메서드를 호출하는 매우 간단한 프로세스입니다.

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를 반환하므로 액션 개체는 실제로 컨트롤러의 runActionWithFilters()를 통해 실행됩니다.

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()은 이전에 생성된 작업 개체를 최종적으로 호출하는 run() 메서드입니다.

public function runAction($action)
{
    $priorAction=$this->_action;
    $this->_action=$action;
    if($this->beforeAction($action))
    {
        $action->run();
        $this->afterAction($action);
    }
    $this->_action=$priorAction;
}

각 필터는 IFilter 인터페이스를 구현해야 하며, 필터에 의해 구현된 preFilter() 메서드는 $action- 전에 호출됩니다. >run(), 액션을 실행할 수 있다고 판단되면 true, 그렇지 않으면 false를 반환합니다

if($filter1->preFilter())
if($filter2- > ;preFilter())
if($filtern->preFilter())
$action->run()
$filtern->postFilter()
$filter2-> postFilter()
$filter1->postFilter()

실행 중인 가장 일반적인 작업은 렌더 뷰 파일인 renderPartial() 및 render()입니다. render()는 뷰 파일을 처리한 후 결과를 레이아웃 파일에 저장합니다.

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는 '/'로 시작하고 시스템 뷰 디렉토리를 시작 디렉토리로 사용합니다. +$view+.php
$ 뷰에 별칭이 포함된 경우 별칭의 실제 경로를 찾습니다.
다른 사람들은 modele 뷰 디렉터리를 시작 디렉터리로 사용합니다. + $view+.php

타사 렌더러가 구성되지 않은 경우 $config, in renderFile() 실제로 Yii 자체에서 제공하는 renderInternal()은 뷰 파일을 렌더링하기 위해 호출됩니다.

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의 렌더러는 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() 실제로 부분 뷰 파일이 먼저 렌더링된 다음 renderFile 레이아웃 파일이 렌더링되고 뷰 파일의 결과가 $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은 헤드에 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 프레임워크 분석(4) - WebApplication의 실행 기능에 대한 자세한 분석입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요. !


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.