作爲源碼分析的首秀,我就挑了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() 一共能夠分紅三部分函數
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();
這個繼承關係很重要,後面的分析都會用到。
好了,今天的分析就在到了,若是有什麼不妥的,請留言,若是以爲有幫助,請順手點個推薦!