Home  >  Article  >  php教程  >  Yii framework analysis (4) - Detailed analysis of WebApplication's run function

Yii framework analysis (4) - Detailed analysis of WebApplication's run function

黄舟
黄舟Original
2016-12-27 11:15:441350browse

The last sentence of the entry script of the Yii application starts the WebApplication

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

CApplication:

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

processRequest () starts processing the request, implemented by 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 application component's parseUrl() creates $route (a string in the form of controllerID/actionID), and runController() creates a Controller object to start processing http requests. The value of

$route may have the following situations:
- empty: replaced by defaultController value;
- “moduleID/controllerID/actionID”:
- “controllerID under module /actionID”: The most common form
- “folder1/folder2/controllerID/actionID” Controller in a multi-level directory

runController first calls createController() to create a controller object

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() returns a created controller object and actionID. runController() calls the controller's init() method and run($actionID) to run the controller:

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 (), there is no action. The Action object is first created in 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->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);
}

Here you can see that the controller is not directly The action method is called, but an Action object is needed to run the controller action. This unifies the processing of actions by the action object mapped by the controller method and actions. That is, both forms of action processing are unified into the run( of the IAction interface). )transfer.

The IAction interface requires the implementation of three methods: run(), getId(), and getController (). The CAction class provided by Yii requires the constructor to provide the Controller and Id and implements the processing of getId() and getController (). The Action class can inherit from CAction.

CInlineAction is a very simple process under web/action. It calls the action method of the Controller:

class CInlineAction extends CAction
{
    public function run()
    {
        $method=’action’.$this->getId();
        $this->getController()->$method();
    }
}

Return to $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() actually returns true, so the action object is actually run through the controller's 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;
    }
}

No filter , runAction() is the run() method that ultimately calls the previously created action object:

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

Each filter must implement the IFilter interface, and the preFilter() method implemented by the filter is in $action->run( ), if it is judged that the action can be executed, it will return true, otherwise it will return false

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

The most common operation in action is to render the view file: renderPartial() and render(). render() will put the result into the layout file after processing the view file.

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) gets the full path of $view:
$view starts with '/', uses the system views directory as the starting directory +$view+.php
$view contains For aliases, find the real path of the alias
Others use the modele view directory as the starting directory+$view+.php

If the third-party renderer is not configured in $config, the actual value in renderFile() is The renderInternal() provided by Yii itself is called to render the view file:

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's renderer uses PHP itself as the template system:

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() actually renders the Partial view first file, then renderFile layoutfile, and pass in the result of the view file as the $content variable.

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 processes the render results, such as adding css or js scripts to the head.

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

The above is the detailed analysis of Yii framework analysis (4) - WebApplication's run function. For more related content, please pay attention to the PHP Chinese website (www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn