注:非教程,只是研究過程一些心得記錄。大部分抄,加之整理。
一 目錄文件
|-framework 框架核心庫
|--base 底層類庫文件夾,包含:
CComponent(組件類,該文件包含了基於組件和事件驅動編程的基礎類,從版本1.1.0開始,一個行爲的屬性(或者它的公共
成員變量或它通過getter和/或setter方法【類似.net中的property(屬性)概念】定義的屬性)可以通過組件的訪問來調用)。
↑ CModel
extends CComponent,模型類,爲所有的數據模型提供的基類
↑ CBehavior extends CComponent,行爲類,主要負責聲明事件和相應事件處理程序的方法、將對象的行爲附加到組件等等
↑
CModule extends CComponent,是模塊和應用程序的基類,主要負責應用組件和子模塊)等等
↑ CApplicationextends CModule,應用類,負責全局的用戶請求處理,它管理的應用組件集,提供特定功能給整個應用程序
|--caching 所有緩存方法,其中包含了Memcache緩存,APC緩存,數據緩存,CDummyCache虛擬緩存,
CEAcceleratorCache緩存等等各種緩存方法
|--cli YII項目生成腳本
|--collections 用php語言構造傳統OO語言的數據存儲單元。如:隊列,棧,哈希表等等
|--console YII控制檯
|--db 數據庫操作類
|--gii YII 代碼生成器(腳手架),能生成包括模型,控制器,視圖等代碼
|--i18n YII 多語言,提供各種語言的本地化數據,信息、文件的翻譯服務、本地化日期和時間格式,數字等
|--logging 日誌組件,YII提供了靈活和可擴展的日誌記錄功能。消息記錄可分爲根據日誌級別和信息類別。
應用層次和類別過濾器,可進一步選擇的消息路由到不同的目的地,例如文件,電子郵件,瀏覽器窗口,等
|--messages 提示信息的多語言
|--test YII提供的測試,包括單元測試和功能測試
|--utils 提供了常用的格式化方法
|--validators 提供了各種驗證方法
|--vendors 這個文件夾包括第三方由Yii框架使用的資料庫
|--views 提供了YII錯誤、日誌、配置文件的多語言視圖
|--web YII所有開發應用的方法
|---actions 控制器操作類
|---auth 權限認識類,包括身份認證,訪問控制過濾,基本角色的訪問控制等
|---filters 過濾器,可被配置在控制器動作執行之前或之後執行。例如, 訪問控制過濾器將被執行以確保
在執行請求的動作之前用戶已通過身份驗證;性能過濾器可用於測量控制器執行所用的時間
|---form 表單生成方法
|---helpers 視圖助手,包含GOOGLE AJAX API,創建HTML,JSON,JAVASCRIPT相關功能
|---js JS庫
|---renderers 視圖渲染組件
|---services 封裝SoapServer並提供了一個基於WSDL的Web服務
|---widgets 部件
|---CArrayDataProvider.php 可以配置的排序和分頁屬性自定義排序和分頁的行爲
|---CActiveDataProvider.php ActiveRecord方法類
|---CController.php 控制器方法,主要負責協調模型和視圖之間的交互
|---CPagination.php 分頁類
|---CUploadedFile.php 上傳文件類
|---CUrlManager.php URL管理
|---CWebModule.php 應用模塊管理,應用程序模塊可被視爲一個獨立的子應用等等方法
|--.htaccess 重定向文件
|--yii.php 引導文件
|--YiiBase.php YiiBase類最主要的功能是註冊了自動加載類方法,加載框架要用到所有接口。
|--yiic Yii LINUX 命令行腳本
|--yiic.bat YII WINDOW 命令行腳本
|--yiilite.php 它是一些常用到的 Yii 類文件的合併文件。在文件中,註釋和跟蹤語句都被去除。
因此,使用 yiilite.php 將減少被引用的文件數量並避免執行跟蹤語句
1. 啓動
網站的唯一入口程序 index.php :
$yii=dirname(__FILE__).'/../framework/yii.php';
$config=dirname(__FILE__).'/protected/config/main.php';
// remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG',true);
// specify how many levels of call stack should be shown in each log message
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);
require_once($yii);
Yii::createWebApplication($config)->run();
require_once($yii) 引入了全局類Yii,而類Yii只幹了一件事:引入類YiiBase:
系統的全局訪問都是通過Yii類(即YiiBase類)來實現的,Yii類的成員和方法都是static類型。
require(dirname(__FILE__).'/YiiBase.php');
class Yii extends YiiBase
{
}
繼續追蹤Yii的靜態方法createWebApplication()發現它其實在內部調用並返回了CWebApplication類的一個全局對象實例,這個對象就是以後會大量使用到的Yii::app()返回的全局(以後簡稱app)對象,這是個唯一的實例(singleton),稍後詳解。
public static function createWebApplication($config=null)
{
return self::createApplication('CWebApplication',$config);
}
public static function createApplication($class,$config=null)
{
return new $class($config); <-----既 new CWebApplication($config);
}
隨即這裏引出一個疑惑,YiiBase.php中並未引入CWebApplication類所在的路徑,爲何可以直接實例化新的對象並返回呢?這就是Yii利用PHP5提供的spl庫來完成類的自動加載。在YiiBase.php 文件結尾處
spl_autoload_register(array('YiiBase','autoload'));
將YiiBase類的靜態方法autoload 註冊爲類加載器。(此處省略,請自行Google學習spl_autoload_register方法的原理)系統將類名傳遞給被註冊的類加載器函數,類加載器函數根據類名自行找到對應的類文件並include 。
public static function autoload($className,$classMapOnly=false)
{
// use include so that the error PHP file may appear
if(isset(self::$classMap[$className]))
include(self::$classMap[$className]);
elseif(isset(self::$_coreClasses[$className]))
include(YII_PATH.self::$_coreClasses[$className]);
elseif($classMapOnly)
return false;
else
...
return true;
}
可以看到YiiBase的靜態成員$_coreClasses 數組裏預先存放着Yii系統自身用到的類對應的文件路徑:
private static $_coreClasses=array(
'CApplication' => '/base/CApplication.php',
'CApplicationComponent' => '/base/CApplicationComponent.php',
'CBehavior' => '/base/CBehavior.php',
'CComponent' => '/base/CComponent.php',
'CErrorEvent' => '/base/CErrorEvent.php',
'CErrorHandler' => '/base/CErrorHandler.php',
'CException' => '/base/CException.php',
...
...
}
非 coreClasse 的類註冊會保存在YiiBase的$_imports數組及$_classMap中,通過Yii::import()方法將類路徑導入PHP include paths中:
$classMap以鍵值對的形式保存註冊類的類名及相對路徑
// The array keys are the class names and the array values are the corresponding class file paths.
public static $classMap=array();
$_imports以別名(僞路徑)或名字空間(namespace)形式保存要註冊類的類名如別名方式application.components.GoogleMap引入GoogleMap類
或application.components.* 引入components目錄下的所有類
Starting from version 1.1.5, this method can also be used to import a class in namespace format
如名字空間方式application\components\GoogleMap與上例中別名方式引入GoogleMap類功能相同,但此方法
要求名字空間中斜線所分隔的每個空間名所組成的目錄層次是一個真實有效的路徑以便Yii::import()方法找到對應的
類文件並引入
private static $_imports=array(); // alias => class name or directory
現在回到回到前面的程序入口的 Yii::createWebApplication($config)->run(); 現在autoload機制開始工作了。
當系統 執行 new CWebApplication() 的時候,會自動
include(YII_PATH.'/base/CApplication.php')
將main.php裏的配置信息數組$config傳遞給CWebApplication創建出對象,並執行對象的run() 方法啓動框架。
CWebApplication類的繼承關係
CWebApplication -> CApplication -> CModule -> CComponent
$config先被傳遞給CApplication的構造函數
public function __construct($config=null)
{
Yii::setApplication($this); <-----之前說到的設置全局app對象的單例模式(singleton)
將自身的實例對象賦給Yii的靜態成員$_app,以後可以通過 Yii::app() 來取得
// 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(); <----1、註冊系統核心組件
$this->configure($config); <----2、$config既全局配置文件main.php中配置文件
$this->attachBehaviors($this->behaviors);
$this->preloadComponents(); <----3、加載配置文件main.php中'preload'處列舉的組件
$this->init();
}
1、$this->registerCoreComponents();
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',
),
'urlManager'=>array(
'class'=>'CUrlManager',
),
...
...
),
$this->setComponents($components);
}
註冊了幾個系統組件(Components)。Components 是在 CModule 裏定義和管理的,主要包括兩個數組
private $_components=array(); <---Componemt的實例存放在$_components 數組裏
private $_componentConfig=array();<---存放相關的配置信息,配置信息包括Component 的類名和屬性設置
每個 Component 都是 IApplicationComponent接口的實例,
CWebApplication 對象註冊了以下幾個Component:
urlManager, request,session,assetManager,user,themeManager,authManager,clientScript。
CWebApplication的parent(CApplication) 註冊了以下幾個Component:
coreMessages,db,messages,errorHandler,securityManager,statePersister。
Component 在Yii PHP裏是個非常重要的東西,它的特徵是可以通過 CModule 的 __get() 和 __set() 方法來訪問。 Component 註冊的時候並不會創建對象實例,而是在程序裏被第一次訪問到的時候,由CModule 來負責(實際上就是 Yii::app())創建。詳見CModule之類方法getComponent($id,$createIfNull=true)。
2、 $this->configure($config); configure() 還是在CModule 裏:
public function configure($config)
{
if(is_array($config))
{
foreach($config as $key=>$value)
$this->$key=$value;
}
}
實際上是把$config數組裏的每一項傳給 CModule 的 父類 CComponent __set() 方法。
public function __set($name,$value)
{
$setter='set'.$name;
if(method_exists($this,$setter))
return $this->$setter($value);
elseif(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
{
// duplicating getEventHandlers() here for performance
$name=strtolower($name);
if(!isset($this->_e[$name]))
$this->_e[$name]=new CList;
return $this->_e[$name]->add($value);
}
elseif(is_array($this->_m))
{
foreach($this->_m as $object)
{
if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))
return $object->$name=$value;
}
}
if(method_exists($this,'get'.$name))
throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
array('{class}'=>get_class($this), '{property}'=>$name)));
else
throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
array('{class}'=>get_class($this), '{property}'=>$name)));
}
我們來看看: if(method_exists($this,$setter))
根據這個條件,$config 數組裏的basePath, params, modules, import, components 都被傳遞給相應的 setBasePath(), setParams() 等方法裏進行處理。
未完待續...