該類文件在: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; } }