程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> YII 的源碼分析(二),yii源碼分析

YII 的源碼分析(二),yii源碼分析

編輯:關於PHP編程

YII 的源碼分析(二),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看下源碼:

<?php

return 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上最終看到的內容了。由於本次渲染比較簡單,所以程序經過的流程也較少,但是從源碼中可以看到,裡邊進行了許多的處理,比如主題什麼的。本次就先分析到這。晚安!




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved