# 1、引言
## 1.1、Yii 簡介
* Yii 的作者是美籍華人“薛強”,他原是 Prado 核心開發成員之一。2008 年薛強另起爐灶, 開發了 Yii 框架,于 2008 年 12 月 3 日發布了 Yii1.0 版本。
* Yii 是目前比較優秀的 PHP 框架之一,它的支持的特性包括:MVC、DAO/ActiveRecord、 I18N/L10N、caching、AJAX 支持、用戶認證和基于角色的訪問控制、腳手架、輸入驗證、部 件、事件、主題化以及 Web 服務等。
`Yii 的很多思想參考了其它一些比較優秀的 Web 框架(我們寫東西時是不是也喜歡參考別人的? 有木有?嘿嘿,都喜歡站在別人的肩膀上干活!)`
## 1.2、本文內容與結構
本文對 Yii1.1.8 版本的源代碼進行了深入的分析,本文的內容與結構為:
* 組件化與模塊化:
* 對 Yii 的基于組件和事件驅動編程模式的基礎類(CComponent)進行分析;
* 對組件化和模塊化的工作原理進行分析;
* 對 WebApp 應用創建 Controller 流程等進行分析。
* 系統組件:對 Yii 框架自帶的重要組件進行分析,主要包括:日志路由組件、Url 管理組 件、異常處理組件、Cache 組件、基于角色的訪問控制組件等。
* 控制器層:控制器主要包含 Action 和 Filter,對 Action 與 Filter 的工作原理進行分析。
* 模型層:對 DAO 層、元數據和 Command 構造器、ORM 的原理進行分析
* 視圖層:對視圖層的渲染過程、Widget 和客戶端腳本組件等進行分析
`本文檔中的錯誤或不妥之處在所難免,殷切希望本文檔的讀者給予批評指正!`
# 2、組件化與模塊化
## 2.1、框架加載和運行流程
* 首先從入口處分析index.php
```php
$yii=dirname(__FILE__).'/../framework/yii.php';
$config=dirname(__FILE__).'/protected/config/main.php';
require_once($yii); //引入yii框架
$application = Yii::createWebApplication($config) //先從這里追蹤
$application->run();
```
$config 為配置文件,先看看如何加載配置文件內容的,
找到Yii類,該類沒有任何內容,只是繼承了Yiibase。
```
class Yii extends YiiBase{
}
```
* Yiibase.php
* 從Yiibase中找到了createWebApplication方法
調用createApplication并傳遞了參數'CWebApplication' 和 $config
```
public static function createWebApplication($config=null)
{
return self::createApplication('CWebApplication',$config);
}
```
* 返回并實例化 CWebApplication 對象,$config也傳遞了過去。
```
public static function createApplication($class,$config=null)
{
return new $class($config);
}
```
* CWebApplication.php,因為該類沒有構造方法,所以找到父類的CApplication.php的構造方法
* 執行構造方法__construct
```
public function __construct($config=null)
{
Yii::setApplication($this); //把當前對象屬性值賦值給Yiibase,這樣便可通過Yii::app()調用main.php中的值
// set basePath at early as possible to avoid trouble
if(is_string($config))
$config=require($config); //引入main.php文件,并賦值給$config
//basePath為項目路徑,檢測路徑是否正確,并賦值給$this->_basePath。
if(isset($config['basePath']))
{
$this->setBasePath($config['basePath']);
unset($config['basePath']);
}
else
$this->setBasePath('protected');
//初始化別名,用于import引入,如:Yii::import('system.web.*'); 或 'application.models.*'
Yii::setPathOfAlias('application',$this->getBasePath());
Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
$this->preinit();
//初始化類autoloader和錯誤處理程序。
$this->initSystemHandlers();
//注冊核心應用程序組件。將db,messages等注冊到CModule類的_componentConfig屬性中
//例如:Yii::app()->db,Yii::app()->message
$this->registerCoreComponents();
//用指定的配置配置模塊,即main.php中的所有配置注冊到CModule中
$this->configure($config);
//將行為列表附加到組件。這里$this->behaviors為空數組
$this->attachBehaviors($this->behaviors);
//加載靜態應用程序組件。
$this->preloadComponents();
$this->init();
}
```
* $this->registerCoreComponents();
```
//注冊核心應用程序組件。將db,messages等注冊到CModule類的_componentConfig屬性中
protected function registerCoreComponents()
{
$components=array(
'coreMessages'=>array(
'class'=>'CPhpMessageSource',
'language'=>'en_us',
'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages',
),
'db'=>array(
'class'=>'CDbConnection',
),
'messages'=>array(
'class'=>'CPhpMessageSource',
),
'errorHandler'=>array(
'class'=>'CErrorHandler',
),
'securityManager'=>array(
'class'=>'CSecurityManager',
),
'statePersister'=>array(
'class'=>'CStatePersister',
),
'urlManager'=>array(
'class'=>'CUrlManager',
),
'request'=>array(
'class'=>'CHttpRequest',
),
'format'=>array(
'class'=>'CFormatter',
),
);
$this->setComponents($components);
}
```
* $this->configure($config);
```
//用指定的配置配置模塊,即main.php中的所有配置注冊到CModule中,
//配置中下標為class的注冊到_componentConfig屬性,其余的注冊到_params屬性
public function configure($config)
{
if(is_array($config))
{
foreach($config as $key=>$value)
$this->$key=$value;
}
}
```
* $this->preloadComponents();
```
//加載靜態應用程序組件。main.php中
//reload'=>array('log'),,即預加載日志組件
//CLogRouter 對象信息(包含main.php中log['routes']的所有配置信息
//賦值給CModule->_components['CLogRouter ']
//當前可以調用Yii::log()來記錄日志
protected function preloadComponents()
{
foreach($this->preload as $id)
$this->getComponent($id);
}
```
* $application->run();
```
//運行控制器
public function run()
{
if($this->hasEventHandler('onBeginRequest'))
$this->onBeginRequest(new CEvent($this));
register_shutdown_function(array($this,'end'),0,false);
//運行默認控制器,或獲取url地址運行指定控制器
$this->processRequest();
if($this->hasEventHandler('onEndRequest'))
$this->onEndRequest(new CEvent($this));
}
```
* $this->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()); //獲取url中的controller/action , 如message/list
//運行控制器
$this->runController($route);
}
```
* $this->runController($route);
```
public function runController($route)
{
//實例化控制器,如 new messageController
if(($ca=$this->createController($route))!==null)
{
list($controller,$actionID)=$ca;
$oldController=$this->_controller;
$this->_controller=$controller;
$controller->init();
//運行action方法
$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->run($actionID); 運行控制器中的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);
}
```
* 之后就會運行咱自己書寫的控制機及方法