該類文件在:ThinkPHP/Library/Think/Think.class.php
該類可以說是ThinkPHP框架最為核心的類庫,負責諸多配置加載,注冊核心系統擴展(自動加載類庫、異常處理、錯誤處理等),管理和維護類實例、別名映射,可以一說是一個框架的工廠(該類有些許面向對象弊端,比如:違背了面向對象單一職責,其負責功能復雜,關聯類庫和文件較多,有動一牽百的憂慮)。類中遇到的函數會在該類分析之後徹底分析,所涉及的其它類庫會專門講解。
一、類結構
namespace Think;//定義命名空間
class Think {
private static $_map = array();//類庫別名映射
private static $_instance = array();//保存類實例(這麼說也不合理,等會分析該功能時具體說明)
static public function start() {}//應用程序初始化
static public function addMap($class, $map=''){}// 注冊classmap
static public function getMap($class=''){}// 獲取classmap
public static function autoload($class) {}//類庫自動加載
static public function instance($class,$method='') {}//取得對象實例 支持調用類的靜態方法
static public function appException($e) {}//自定義異常處理
static public function appError($errno, $errstr, $errfile, $errline) {}//自定義錯誤處理
static public function fatalError() {} // 致命錯誤捕獲
static public function halt($error) {}//錯誤輸出
static public function trace($value='[think]',$label='',$level='DEBUG',$record=false) {}//添加和獲取頁面Trace記錄
}
二、應用程序初始化start()方法分析,該方法包含一套錯誤和異常處理機制,非常受用。該方法作為ThinkPHP框架的引導接口,實現錯誤、異常處理,配置加載,別名映射,行為注冊,包含運行緩存的生成,網站應用目錄檢測,自動類庫加載行為注冊。
/**
* 應用程序初始化
* @access public
* @return void
*/
static public function start() {
//使用spl標准庫中提供__autoload()函數的默認實現,比__autoload()效率更高,更加靈活
//一下可以使用spl_autoload_register(array('Think\Think','autoload'));
//建議使用spl_autoload_register(__NAMESPACE__.'\Think::autoload');實現
//一下所有注冊方式均可以使用上面3中形式傳遞參數
spl_autoload_register('Think\Think::autoload');
//注冊全局腳本"析構函數",使用該方式注冊的函數,會在腳本結束前調用,大多數情況用來處理致命錯誤
register_shutdown_function('Think\Think::fatalError');
//設置自定義錯誤處理函數,用於處理錯誤信息
set_error_handler('Think\Think::appError');
//設置未異常處理函數
set_exception_handler('Think\Think::appException');
//可以把register_shutdown_function(),set_error_handler(),set_error_handler()3個函數組合完成自定義、多元化的錯誤處理模塊
//根據STORAGE_TYPE的值設置分布式文件存儲方案,Storage是一個工廠類,用於管理和維護分布式文件存儲組件
//後面會詳細講解Storage類,並指出設計缺陷
Storage::connect(STORAGE_TYPE);
//根據運行模式在運行緩存目錄下生成編譯緩存文件APP_MODE.'~runtime.php',從而減少IO開銷
//下面會詳細介紹生成緩存文件的方式
$runtimefile = RUNTIME_PATH.APP_MODE.'~runtime.php';
//如果不是在調試模式,並且編譯緩存文件存在,直接加載編譯緩存
if(!APP_DEBUG && Storage::has($runtimefile)){
Storage::load($runtimefile);
}else{
//判斷編譯緩存文件是否存在,存在就刪除
if(Storage::has($runtimefile))
Storage::unlink($runtimefile);
//預編譯內容變量
$content = '';
//判斷是否存在運行模式配置文件,如果不存在就加載MODE_PATH.APP_MODE.'.php',運行模式配置文件會影響下列加載不同的類庫和配置
//運行配置文件後期會詳細講解
$mode = include is_file(CONF_PATH.'core.php')?CONF_PATH.'core.php':MODE_PATH.APP_MODE.'.php';
//以下所有配置項加載都會根據加載的先後順序覆蓋之前的配置項,一般都是先加載ThinkPHP默認配置,再加載應用配置
//core下標決定要加載的核心類和函數文件
foreach ($mode['core'] as $file){
if(is_file($file)) {
include $file;
//如果不是調試模式,則編譯該文件內容並儲存到預編譯內容變量中
if(!APP_DEBUG) $content .= compile($file);
}
}
//config下標決定要加載的核心配置文件
foreach ($mode['config'] as $key=>$file){
//判斷下標是否為數字,如果不是就會把該配置文件中的配置項加載到對應的鍵下面,相當於給配置項增加一個緯度
is_numeric($key)?C(include $file):C($key,include $file);
}
//如果不是普通運行模式,則判斷是否存在運行模式應用配置文件
if('common' != APP_MODE && is_file(CONF_PATH.'config_'.APP_MODE.'.php'))
C(include CONF_PATH.'config_'.APP_MODE.'.php');
//alias下標記錄類庫別名映射規則,ThinkPHP獨創別名機制,用於提升自動加載的效率
if(isset($mode['alias'])){
//由這句代碼可以看出alias規則可以是一個數組,或者將規則數組單獨作為一個文件
self::addMap(is_array($mode['alias'])?$mode['alias']:include $mode['alias']);
}
//加載應用中定義的別名配置
if(is_file(CONF_PATH.'alias.php'))
self::addMap(include CONF_PATH.'alias.php');
//tags下標用於標識系統行為,行為擴展具體由Hook鉤子類實現
if(isset($mode['tags'])) {
//由這句代碼可以看出tags規則可以是一個數組,或者將規則數組單獨作為一個文件
Hook::import(is_array($mode['tags'])?$mode['tags']:include $mode['tags']);
}
//加載應用中的行為擴展配置
if(is_file(CONF_PATH.'tags.php'))
// 允許應用增加開發模式配置定義
Hook::import(include CONF_PATH.'tags.php');
//加載框架底層語言包,有核心配置文件中的DEFAULT_LANG配置項決定
L(include THINK_PATH.'Lang/'.strtolower(C('DEFAULT_LANG')).'.php');
//如果不是調試模式則生成編譯緩存文件
if(!APP_DEBUG){
//namespace {}這種方式用於聲明代碼塊中的命名空間屬於全局命名空間
//這句代碼用於生成加載別名映射的php代碼
$content .= "\nnamespace { Think\Think::addMap(".var_export(self::$_map,true).");";
//L(".var_export(L(),true).");生成語言加載代碼
//C(".var_export(C(),true).');生成配置項加載代碼
//Think\Hook::import('.var_export(Hook::get(),true).');生成鉤子加載代碼
$content .= "\nL(".var_export(L(),true).");\nC(".var_export(C(),true).');Think\Hook::import('.var_export(Hook::get(),true).');}';
//將$content變量內容去除注釋和換行、空隔之後寫入到運行時編譯緩存文件
Storage::put($runtimefile,strip_whitespace('
三、類庫別名映射機制實現addMap()和getMap()方法分析;該機制使用Think::_map變量存儲別名映射記錄,通過Think::addMap()添加或修改別名映射記錄,使用Think:getMap()獲取別名映射記錄。
/**
* 注冊或修改類庫別名映射記錄
* @access public
* @param class String|Array 如果為數組鍵為類庫別名,鍵值為類庫實際位置;如果字符串代表類庫別名
* @param map String 如果class為字符串,該參數代表類庫實際位置,否則沒有意義
* @return void
*/
static public function addMap($class, $map=''){
//判斷class是否為數組,如果是就合並當前類庫別名記錄,如果有相同記錄就會覆蓋
if(is_array($class)){
self::$_map = array_merge(self::$_map, $class);
}else{
//如果class不是數組,就作為別名儲存在類庫別名記錄中,並把map作為實際類庫位置
self::$_map[$class] = $map;
}
}
/**
* 根據別名獲取類庫實際地址
* @param class String 類庫別名
* @return Array|String|NULL 如果class為空則獲取所有類庫別名記錄,否者返回別名對應的類庫位置
*/
static public function getMap($class=''){
//如果class為空,直接返回所有類庫別名記錄
if(''===$class){
return self::$_map;
//判斷對應別名是否在別名映射記錄中,如果存在返回類庫實際地址,否者返回null
}elseif(isset(self::$_map[$class])){
return self::$_map[$class];
}else{
return null;
}
}