YII 的源碼分析(-)

作爲源碼分析的首秀,我就挑了yii(讀做歪依依而不是歪愛愛);它的讚美之詞我就很少說了,直接入正題。先準備材料,建議直從官網下載yii的源碼包(1.1.15)。php

在demos裏邊有一個最簡單的應用—helloworld.就是用yii框架輸出一句話:」hello world」;web

我就從它下手,分析框架執行一個最小流程要通過哪些組件,淺析它的運行過程。bootstrap

首先從單一入口文件開始閱讀。(源碼通常都是從調用處開始分析)
數組

 index.php 只有兩行代碼,很是的簡單,就是導入yiibase類 ,啓動應用。mvc

// include Yii bootstrap file
require_once(dirname(__FILE__).'/../../framework/yii.php');

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

//YiiBase is a helper class serving common framework functionalities.app

//YiiBase是一個助手類,它服務於整個框架。YiiBase代碼有點長. 主要完成一些常量的定義和初始化。框架

代碼到這裏彷佛就終結了,頁面的內容也程現出來,但是框架到底作了些什麼,咱們卻一無所知。因此咱們須要把這一步進行分解,把裏邊的細節暴露出來。yii

Yii::createWebApplication()->run() 一共能夠分紅三部分函數

  1. Yii
  2. createWebApplication
  3. run

Yii 這個東西是什麼? 這個很容易,從yii.php 能夠找到這樣一行代碼class Yii extends YiiBase,說明它就是YiiBase的繼承類,並且做者的擴展是留空的,因此Yii就是YiiBase的一個引用而已。oop

眼下還有兩件事要搞清楚,一是這個new 作了什麼操做,二是run作了什麼操做?

先看第一個問題,new操做幹了些什麼事。

    public static function createWebApplication($config=null)
    {
        return self::createApplication('CWebApplication',$config);
    }

它又把任務傳遞給了createApplication:

         public static function createApplication($class,$config=null)
         {
                   return new $class($config);
         }

 

結合起來看,createWebApplication () 就是return new CWebApplication($config); 因此new 就是返回了CWebApplication的實例.然而這個CWebApplication類又在哪呢?它又是怎麼引入的呢?它執行了哪些操做?帶着這些問題,咱們再次回到YiiBase.php.

在YiiBase.php裏邊定義了一個很長的數組,你能夠找到:

'CWebApplication' => '/web/CWebApplication.php',

說膽CWebApplication這個類是屬於自動加載的。關於它是如何實現自動加載的,能夠查看spl_autoload_register的相關文檔,在這裏,咱們先只要知道它是Yii框架給咱們自動加載進來的就能夠了。

咱們繼續往CWebApplication這個裏邊深挖。打開/web/CWebApplication.php這個文件,竟然沒有構造函數,根據個人經驗,用了new的類,通常有一個構造函數進行一些初始化工做的,因而我試着往父類找找看。從 class CWebApplication extends CApplication 能夠看出,父類是CApplication. 它確實有一個構造函數,代碼以下:

public function __construct($config=null)
    {
        Yii::setApplication($this);

        // set basePath at early as possible to avoid trouble
        if(is_string($config))
            $config=require($config);

        if(isset($config['basePath']))
        {
            $this->setBasePath($config['basePath']);
            unset($config['basePath']);
        }
        else
            $this->setBasePath('protected');
        Yii::setPathOfAlias('application',$this->getBasePath());
        Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
        if(isset($config['extensionPath']))
        {
            $this->setExtensionPath($config['extensionPath']);
            unset($config['extensionPath']);
        }
        else
            Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
        if(isset($config['aliases']))
        {
            $this->setAliases($config['aliases']);
            unset($config['aliases']);
        }

        $this->preinit();//預留

        $this->initSystemHandlers();//設置錯誤處理
        $this->registerCoreComponents(); //註冊核心組件

        $this->configure($config); //加載配置文件

        $this->attachBehaviors($this->behaviors);//行爲相關
        $this->preloadComponents(); //加載預加載組件

        $this->init(); 
    }

這樣,new的過程就完結了,沒有什麼特別的,下面咱們重點來看看run作了哪些工做。在CWebApplication 找不到run方法,它來自父類CApplication:

  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(); 由於前面和後面部分都是註冊事件相關的,當前條件下執行不到。而CApplication中的processRequest方法是抽象的,因此猜想是在子類中進行實現的。又回到CWebApplication中,查找processRequest:

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

注意重點在$this->runController($route);

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

$controller->run($actionID);

這裏的$controller能夠能過查看createController得知,就是默認的控制器Sitecontroller.php

而Action則是index,你問我是怎麼看出來的?哈哈,我在猜不出來的地方echo或var_dump一下不就能夠了嗎?這麼簡單的邏輯,還輪不到xdebug 這樣的神器出場。後面咱們分析路由的時候,會知道,沒有指明的時候,系統會有一個默認的index做爲Action.

顯然,init什麼也沒有作,看看run作了什麼

Sitecontroller中沒有run方法,又要去它的父類中查找。從定義:class SiteController extends CController得出父類。

在CController中有這個方法:

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

查看$this->createAction($actionID),獲得return new CInlineAction($this,$actionID);

咱們呆會再看這個CInlineAction,先看$this->runActionWithFilters($action,$this->filters());

 

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

顯然$filters是空的,因此執行第一個表達式$this->runAction($action);

public function runAction($action)
         {
                   $priorAction=$this->_action;
                   $this->_action=$action;
                   if($this->beforeAction($action))
                   {
                            if($action->runWithParams($this->getActionParams())===false){
                                     $this->invalidActionParams($action);
                            }
                            else{
                                     $this->afterAction($action);
                            }
                   }
                   $this->_action=$priorAction;
         }

這段代碼的重點是 $action->runWithParams($this->getActionParams())這一句;

這裏的$action就是$this->createAction($actionID)返回的結果,而它的結果就是

return new CInlineAction($this,$actionID);

CInlineAction.php

是時候查看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();
         }

 

哇哦,好高級,竟然還用了反射,不過我喜歡!

不過呢,打印$method發現:

object(ReflectionMethod)#6 (2) {

 

["name"]=>

 

string(11) "actionIndex"

 

["class"]=>

 

string(14) "SiteController"

 

}

沒有參數,因此此處代碼至關因而執行了SiteController->actionIndex();

在class SiteController 中能夠看到actionIndex 的定義 

 public function actionIndex()
         {
                   echo 'Hello World';
         }

因而就看到屏幕上那一句Hello World ,整個程序也就跑完了。也許有人要問了,爲何輸出一句話還這麼複雜,不是脫了褲子打屁嗎? (請容許個人粗俗);

若是是這麼簡單的需求,固然不可能這麼幹。舉這個例子,只是說明yii的基礎流程,爲下面的複雜應用作一個過渡。

Yii做爲一個優秀的oop框架,這個例子只是介紹了它的繼承,接口,mvc中的vc特性,關於數據模型,我將在後面的分析中陸續給出。最終的目標,是利用yii框架簡化咱們的開發過程。

 

到此,咱們整理一下流程中的主要繼承關係:

 

class CComponent {
    public function __set($name,$value){} public function __get($name){} } abstract class CModule extends CComponent { function __construct(){ echo 'CModule'; } } abstract class CApplication extends CModule{ function __construct(){ echo 'CApplication'; } public function run(){ $this->processRequest(); } } class CWebApplication extends CApplication{ public $defaultController='site'; public $layout='main'; public function processRequest(){ //$this->runController($route);  } } $app = new CWebApplication->run();

這個繼承關係很重要,後面的分析都會用到。

好了,今天的分析就在到了,若是有什麼不妥的,請留言,若是以爲有幫助,請順手點個推薦!

相關文章
相關標籤/搜索