Maison >php教程 >PHP开发 >Yii Framework Analysis (4) - Analyse détaillée de la fonction d'exécution de l'application Web

Yii Framework Analysis (4) - Analyse détaillée de la fonction d'exécution de l'application Web

黄舟
黄舟original
2016-12-27 11:15:441426parcourir

La dernière phrase du script d'entrée de l'application Yii démarre WebApplication

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

CApplication :

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

processRequest() commence le traitement de la requête, implémentée par 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);
}

parseUrl() du composant d'application urlManager crée $route (une chaîne sous la forme de controllerID/actionID), et runController( ) commence à créer l'objet Controller Gérer les requêtes http.

La valeur de $route peut avoir les situations suivantes :
- vide : remplacé par la valeur defaultController
- "moduleID/controllerID/actionID":
- "controllerID" sous module / actionID": La forme la plus courante
- "folder1/folder2/controllerID/actionID" Contrôleurs dans des répertoires multi-niveaux

runController appelle d'abord createController() pour créer un objet contrôleur

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() renvoie un objet contrôleur créé et un actionID, runController() appelle la méthode init() du contrôleur et run($actionID) pour exécuter le contrôleur :

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 ->Il y a aucune action dans 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->L'objet Action est d'abord créé dans 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);
}

ici, cela peut être On voit que le contrôleur n'appelle pas directement la méthode d'action, mais nécessite un objet Action pour exécuter l'action du contrôleur. Cela unifie le traitement des actions par l'objet d'action mappé par la méthode du contrôleur et les actions, c'est-à-dire deux formes de traitement d'action. Ils sont tous unifiés dans l'appel run() de l'interface IAction.

L'interface IAction nécessite l'implémentation de trois méthodes : run(), getId() et getController(). La classe CAction fournie par Yii nécessite que le constructeur fournisse le contrôleur et l'identifiant et implémente le traitement de getId. () et getController (). La classe Action peut hériter de CAction.

CInlineAction sous web/action, run() est un processus très simple, appelant la méthode d'action du contrôleur :

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

Retour à $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() renvoie en fait true, donc l'objet d'action est en fait exécuté via runActionWithFilters() du contrôleur

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

Il n'y a pas de filtre, runAction() est la méthode run() qui appelle finalement l'objet action créé précédemment :

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

Chaque filtre doit implémenter l'interface IFilter, et la méthode preFilter() implémentée par le filtre Appelé avant $action- >run(), s'il est jugé que l'action peut être exécutée, il renvoie vrai, sinon il renvoie faux

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

L'opération la plus courante en action est le fichier de vue de rendu : renderPartial() et render(). render() placera le résultat dans le fichier de mise en page après avoir traité le fichier de vue.

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) obtient le chemin complet de $view :
$view commence par '/' et utilise le répertoire des vues système comme répertoire de départ $view.php
$ view S'il contient un alias, recherchez le chemin réel de l'alias
D'autres utilisent le répertoire modele view comme répertoire de départ $view.php

Si le moteur de rendu tiers n'est pas configuré dans $config , le chemin réel dans renderFile() est Le renderInternal() fourni par yii lui-même est appelé pour restituer le fichier de vue :

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

Le moteur de rendu de Yii utilise PHP lui-même comme système de modèles :

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() en fait, d'abord le fichier de vue renderPartial, puis le fichier de mise en page renderFile, et transmettez le résultat du fichier de vue en tant que variable $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 traitera les résultats du rendu, comme l'ajout de scripts CSS ou js à la tête.

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

Ce qui précède est l'analyse du framework Yii (4) - l'analyse détaillée de la fonction d'exécution de WebApplication. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn). !


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn