当前位置: 代码迷 >> PHP >> YII 的源码分析(2)
  详细解决方案

YII 的源码分析(2)

热度:454   发布时间:2016-04-28 18:08:55.0
YII 的源码分析(二)

上一篇简单分析了一下yii的流程,从创建一个应用,到屏幕上输出结果。这一次我来一个稍复杂一点的,重点在输出上,不再是简单的一行"hello world",而是要经过view(视图)层的处理。

依然是demos目录,这次我们选择hangman,一个简单的猜字游戏。老规则,还是从入口处开始看。

index.php:

<?php// change the following paths if necessary$yii=dirname(__FILE__).'/../../framework/yii.php';$config=dirname(__FILE__).'/protected/config/main.php';// remove the following line when in production mode// defined('YII_DEBUG') or define('YII_DEBUG',true);require_once($yii);Yii::createWebApplication($config)->run();

和helloworld应用相比,这次多了main.php,打开main看下源码:

<?phpreturn array(    'name'=>'Hangman Game',    'defaultController'=>'game',    'components'=>array(        'urlManager'=>array(            'urlFormat'=>'path',            'rules'=>array(                'game/guess/<g:\w>'=>'game/guess',            ),        ),    ),);

在我们以后的实际项目中,也是经常要用到配置文件的,所以我觉得有必要了解一下yii的配置文件--main.php

'name'=>'这里通常是定义网站的标题',也就是我们打开index.php时,在网页上显示的标题。

'defaultController'=>'这里是默认的控制器',也就是我们的index.php后面没有指定控制器时系统采用的控制器,如果我们这里没有指出来,默认就是site

'components'=>'这里是组件的参数,用多维数组进行配置。' 具体的参数可以查看yii手册。

Yii::createWebApplication($config)->run(); 上一次我们已经详细分析过它了,这里再简单的走一遍:

CWebApplication.php -> CApplication.php -> __construct($config) :

$this->preinit();        $this->initSystemHandlers();        $this->registerCoreComponents();        $this->configure($config);        $this->attachBehaviors($this->behaviors);        $this->preloadComponents();        $this->init();

上次我们没有配置过程,所以$this->configure($config)什么也没有做,但是这次有配置参数,所以我们进去看看yii做了哪些操作:

CApplication自己没有实现configure方法,是继承于CModule.php的:

    public function configure($config)    {        if(is_array($config))        {            foreach($config as $key=>$value)                $this->$key=$value;        }    }

代码非常简单,就是把配置参数的键做为类的属性名,value做为类的属性值进行了扩展。完成这一过程就运行CApplication 上的run方法了。

    public function run()    {        if($this->hasEventHandler('onBeginRequest'))            $this->onBeginRequest(new CEvent($this));        register_shutdown_function(array($this,'end'),0,false);        $this->processRequest();        if($this->hasEventHandler('onEndRequest'))            $this->onEndRequest(new CEvent($this));    }

我们前面说过,这里只要关注 $this->processRequest(); 就可以了。运行的结果就是执行$this->runController('');  

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

由于url是index.php,后面没有任何参数,所以都是走的默认控制器,也就是我们在main.php中设定的game. 所以$controller 就等于 controllers/gameController.php, 通过上次的源码分析我们可以知道,在gameController.php中没有init方法时,都是走的父类中定义的默认方法(实际上是一个空方法),

$controller->run($actionID); == gameController->run(''); gameController上没有实现run方法,于是又是去父类中找run

从class GameController extends CController 可以看出,父类是CController , 找到相应的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);    }

前面已经分析过了,没有指定时,都是默认参数。那么此时的$actionID为空,actionID就是gameController中定义的默认动作:public $defaultAction='play'; 

runActionWithFilters --->  runAction --> $action->runWithParams

这里的$action 需要从CAction -> CInlineAction中去找

public function runWithParams($params)    {        $methodName='action'.$this->getId();        $controller=$this->getController();        $method=new ReflectionMethod($controller, $methodName);        if($method->getNumberOfParameters()>0)            return $this->runWithParamsInternal($controller, $method, $params);        else            return $controller->$methodName();    }

走了这么多过程,和hello world的流程是差不多的。据上次的分析可以知道,这里执行了

$controller->$methodName(); 也就是GameController->actionPlay()

到此,我们本节的重点才真正开始:
    public function actionPlay()    {        static $levels=array(            '10'=>'Easy game; you are allowed 10 misses.',            '5'=>'Medium game; you are allowed 5 misses.',            '3'=>'Hard game; you are allowed 3 misses.',        );        // if a difficulty level is correctly chosen        if(isset($_POST['level']) && isset($levels[$_POST['level']]))        {            $this->word=$this->generateWord();            $this->guessWord=str_repeat('_',strlen($this->word));            $this->level=$_POST['level'];            $this->misses=0;            $this->setPageState('guessed',null);            // show the guess page            $this->render('guess');        }        else        {            $params=array(                'levels'=>$levels,                // if this is a POST request, it means the level is not chosen                'error'=>Yii::app()->request->isPostRequest,            );            // show the difficulty level page            $this->render('play',$params);        }    }
显然走的是else的逻辑,重点请看 $this->render('play',$params); 这个render方法这么面熟,很多框架中都有类似的方法,比如discuz,smarty,CI 等等. 纵观yii框架,rnder 在它整个MVC模式中,是V得以实现的重要骨干。所以有必要把它翻个底朝天。
在CController.php中有这个方法:
    public function render($view,$data=null,$return=false)    {        if($this->beforeRender($view))        {            $output=$this->renderPartial($view,$data,true);            if(($layoutFile=$this->getLayoutFile($this->layout))!==false)                $output=$this->renderFile($layoutFile,array('content'=>$output),true);            $this->afterRender($view,$output);            $output=$this->processOutput($output);            if($return)                return $output;            else                echo $output;        }    }

当我们echo $output=$this->renderPartial($view,$data,true);的时候,就发现,此时的$output已经就拿到我们最终的结果了。它对应的文件是views/game/play.php

也就是我们在index.php上最终看到的内容了。由于本次渲染比较简单,所以程序经过的流程也较少,但是从源码中可以看到,里边进行了许多的处理,比如主题什么的。本次就先分析到这。晚安!




  相关解决方案