国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

ThinkPHP框架引導(dǎo)類的示例代碼分析

PHP中文網(wǎng)
發(fā)布: 2017-04-21 09:37:30
原創(chuàng)
1301人瀏覽過(guò)

該類文件在:thinkphp/library/think/think.class.php

?????? 該類可以說(shuō)是ThinkPHP框架最為核心的類庫(kù),負(fù)責(zé)諸多配置加載,注冊(cè)核心系統(tǒng)擴(kuò)展(自動(dòng)加載類庫(kù)、異常處理、錯(cuò)誤處理等),管理和維護(hù)類實(shí)例、別名映射,可以一說(shuō)是一個(gè)框架的工廠(該類有些許面向?qū)ο蟊锥?,比如:違背了面向?qū)ο髥我宦氊?zé),其負(fù)責(zé)功能復(fù)雜,關(guān)聯(lián)類庫(kù)和文件較多,有動(dòng)一牽百的憂慮)。類中遇到的函數(shù)會(huì)在該類分析之后徹底分析,所涉及的其它類庫(kù)會(huì)專門講解。

一、類結(jié)構(gòu)?

namespace Think;//定義命名空間
class Think {
    private static $_map      = array();//類庫(kù)別名映射
    private static $_instance = array();//保存類實(shí)例(這么說(shuō)也不合理,等會(huì)分析該功能時(shí)具體說(shuō)明)
    static public function start() {}//應(yīng)用程序初始化
    static public function addMap($class, $map=''){}// 注冊(cè)classmap
    static public function getMap($class=''){}// 獲取classmap
    public static function autoload($class) {}//類庫(kù)自動(dòng)加載
    static public function instance($class,$method='') {}//取得對(duì)象實(shí)例 支持調(diào)用類的靜態(tài)方法
    static public function appException($e) {}//自定義異常處理
    static public function appError($errno, $errstr, $errfile, $errline) {}//自定義錯(cuò)誤處理
    static public function fatalError() {} // 致命錯(cuò)誤捕獲
    static public function halt($error) {}//錯(cuò)誤輸出
    static public function trace($value='[think]',$label='',$level='DEBUG',$record=false) {}//添加和獲取頁(yè)面Trace記錄
}
登錄后復(fù)制

二、應(yīng)用程序初始化start()方法分析,該方法包含一套錯(cuò)誤和異常處理機(jī)制,非常受用。該方法作為ThinkPHP框架的引導(dǎo)接口,實(shí)現(xiàn)錯(cuò)誤、異常處理,配置加載,別名映射,行為注冊(cè),包含運(yùn)行緩存的生成,網(wǎng)站應(yīng)用目錄檢測(cè),自動(dòng)類庫(kù)加載行為注冊(cè)。

/**
     * 應(yīng)用程序初始化
     * @access public
     * @return void
     */
    static public function start() {
    	//使用spl標(biāo)準(zhǔn)庫(kù)中提供__autoload()函數(shù)的默認(rèn)實(shí)現(xiàn),比__autoload()效率更高,更加靈活
    	//一下可以使用spl_autoload_register(array('ThinkThink','autoload'));
    	//建議使用spl_autoload_register(__NAMESPACE__.'Think::autoload');實(shí)現(xiàn)
    	//一下所有注冊(cè)方式均可以使用上面3中形式傳遞參數(shù)
    	spl_autoload_register('ThinkThink::autoload');
    	
    	//注冊(cè)全局腳本"析構(gòu)函數(shù)",使用該方式注冊(cè)的函數(shù),會(huì)在腳本結(jié)束前調(diào)用,大多數(shù)情況用來(lái)處理致命錯(cuò)誤
    	register_shutdown_function('ThinkThink::fatalError');
    	//設(shè)置自定義錯(cuò)誤處理函數(shù),用于處理錯(cuò)誤信息
    	set_error_handler('ThinkThink::appError');
    	//設(shè)置未異常處理函數(shù)
    	set_exception_handler('ThinkThink::appException');
    	//可以把register_shutdown_function(),set_error_handler(),set_error_handler()3個(gè)函數(shù)組合完成自定義、多元化的錯(cuò)誤處理模塊
    	
    	//根據(jù)STORAGE_TYPE的值設(shè)置分布式文件存儲(chǔ)方案,Storage是一個(gè)工廠類,用于管理和維護(hù)分布式文件存儲(chǔ)組件
    	//后面會(huì)詳細(xì)講解Storage類,并指出設(shè)計(jì)缺陷
    	Storage::connect(STORAGE_TYPE);
    	
    	//根據(jù)運(yùn)行模式在運(yùn)行緩存目錄下生成編譯緩存文件APP_MODE.'~runtime.php',從而減少IO開(kāi)銷
    	//下面會(huì)詳細(xì)介紹生成緩存文件的方式
    	$runtimefile  = RUNTIME_PATH.APP_MODE.'~runtime.php';
    	
    	//如果不是在調(diào)試模式,并且編譯緩存文件存在,直接加載編譯緩存
    	if(!APP_DEBUG && Storage::has($runtimefile)){
    		Storage::load($runtimefile);
    	}else{
    		//判斷編譯緩存文件是否存在,存在就刪除
    		if(Storage::has($runtimefile))
    			Storage::unlink($runtimefile);
    		
    		//預(yù)編譯內(nèi)容變量
    		$content =  '';
    		//判斷是否存在運(yùn)行模式配置文件,如果不存在就加載MODE_PATH.APP_MODE.'.php',運(yùn)行模式配置文件會(huì)影響下列加載不同的類庫(kù)和配置
    		//運(yùn)行配置文件后期會(huì)詳細(xì)講解
    		$mode   =   include is_file(CONF_PATH.'core.php')?CONF_PATH.'core.php':MODE_PATH.APP_MODE.'.php';
    	
    		//以下所有配置項(xiàng)加載都會(huì)根據(jù)加載的先后順序覆蓋之前的配置項(xiàng),一般都是先加載ThinkPHP默認(rèn)配置,再加載應(yīng)用配置
    		//core下標(biāo)決定要加載的核心類和函數(shù)文件
    		foreach ($mode['core'] as $file){
    			if(is_file($file)) {
    				include $file;
    				//如果不是調(diào)試模式,則編譯該文件內(nèi)容并儲(chǔ)存到預(yù)編譯內(nèi)容變量中
    				if(!APP_DEBUG) $content   .= compile($file);
    			}
    		}
    
    		//config下標(biāo)決定要加載的核心配置文件
    		foreach ($mode['config'] as $key=>$file){
    			//判斷下標(biāo)是否為數(shù)字,如果不是就會(huì)把該配置文件中的配置項(xiàng)加載到對(duì)應(yīng)的鍵下面,相當(dāng)于給配置項(xiàng)增加一個(gè)緯度
    			is_numeric($key)?C(include $file):C($key,include $file);
    		}
    
    		//如果不是普通運(yùn)行模式,則判斷是否存在運(yùn)行模式應(yīng)用配置文件
    		if('common' != APP_MODE && is_file(CONF_PATH.'config_'.APP_MODE.'.php'))
    			C(include CONF_PATH.'config_'.APP_MODE.'.php');
    
    		//alias下標(biāo)記錄類庫(kù)別名映射規(guī)則,ThinkPHP獨(dú)創(chuàng)別名機(jī)制,用于提升自動(dòng)加載的效率
    		if(isset($mode['alias'])){
    			//由這句代碼可以看出alias規(guī)則可以是一個(gè)數(shù)組,或者將規(guī)則數(shù)組單獨(dú)作為一個(gè)文件
    			self::addMap(is_array($mode['alias'])?$mode['alias']:include $mode['alias']);
    		}
    
    		//加載應(yīng)用中定義的別名配置
    		if(is_file(CONF_PATH.'alias.php'))
    			self::addMap(include CONF_PATH.'alias.php');
    
    		//tags下標(biāo)用于標(biāo)識(shí)系統(tǒng)行為,行為擴(kuò)展具體由Hook鉤子類實(shí)現(xiàn)
    		if(isset($mode['tags'])) {
    			//由這句代碼可以看出tags規(guī)則可以是一個(gè)數(shù)組,或者將規(guī)則數(shù)組單獨(dú)作為一個(gè)文件
    			Hook::import(is_array($mode['tags'])?$mode['tags']:include $mode['tags']);
    		}
    
    		//加載應(yīng)用中的行為擴(kuò)展配置
    		if(is_file(CONF_PATH.'tags.php'))
    			// 允許應(yīng)用增加開(kāi)發(fā)模式配置定義
    			Hook::import(include CONF_PATH.'tags.php');
    
    		//加載框架底層語(yǔ)言包,有核心配置文件中的DEFAULT_LANG配置項(xiàng)決定
    		L(include THINK_PATH.'Lang/'.strtolower(C('DEFAULT_LANG')).'.php');
    
    		//如果不是調(diào)試模式則生成編譯緩存文件
    		if(!APP_DEBUG){
    			//namespace {}這種方式用于聲明代碼塊中的命名空間屬于全局命名空間
    			//這句代碼用于生成加載別名映射的php代碼
    			$content  .=  "
namespace { ThinkThink::addMap(".var_export(self::$_map,true).");";
    			//L(".var_export(L(),true).");生成語(yǔ)言加載代碼
    			//C(".var_export(C(),true).');生成配置項(xiàng)加載代碼
    			//ThinkHook::import('.var_export(Hook::get(),true).');生成鉤子加載代碼
    			$content  .=  "
L(".var_export(L(),true).");
C(".var_export(C(),true).');ThinkHook::import('.var_export(Hook::get(),true).');}';
    			//將$content變量?jī)?nèi)容去除注釋和換行、空隔之后寫入到運(yùn)行時(shí)編譯緩存文件
    			Storage::put($runtimefile,strip_whitespace('<?php '.$content));
    		}else{
    			// 調(diào)試模式加載系統(tǒng)默認(rèn)的配置文件
    			C(include THINK_PATH.'Conf/debug.php');
    			// 讀取應(yīng)用調(diào)試配置文件
    			if(is_file(CONF_PATH.'debug.php'))
    				C(include CONF_PATH.'debug.php');
    		}
    	}
    
    	//根據(jù)APP_STATUS讀取當(dāng)前部署環(huán)境配置文件,常用在上線前數(shù)據(jù)庫(kù)連接配置等,用于覆蓋默認(rèn)配置行為
    	if(APP_STATUS && is_file(CONF_PATH.APP_STATUS.'.php'))
    		C(include CONF_PATH.APP_STATUS.'.php');
    
    	// 設(shè)置系統(tǒng)時(shí)區(qū)
    	//建議可以寫成date_default_timezone_set(C('DEFAULT_TIMEZONE',null,'PRC'));防止配置項(xiàng)讀取問(wèn)題導(dǎo)致的時(shí)區(qū)設(shè)置錯(cuò)誤
    	date_default_timezone_set(C('DEFAULT_TIMEZONE'));
    
    	// 檢查應(yīng)用目錄結(jié)構(gòu) 如果不存在則自動(dòng)創(chuàng)建
    	if(C('CHECK_APP_DIR') && !is_dir(LOG_PATH)) {
    		//build.php負(fù)責(zé)創(chuàng)建應(yīng)用目錄結(jié)構(gòu)
    		require THINK_PATH.'Common/build.php';
    	}
    
    	// 記錄加載文件時(shí)間
    	G('loadTime');
    	// 運(yùn)行應(yīng)用
    	App::run();
    }
登錄后復(fù)制

三、類庫(kù)別名映射機(jī)制實(shí)現(xiàn)addMap()和getMap()方法分析;該機(jī)制使用Think::_map變量存儲(chǔ)別名映射記錄,通過(guò)Think::addMap()添加或修改別名映射記錄,使用Think:getMap()獲取別名映射記錄。

立即學(xué)習(xí)PHP免費(fèi)學(xué)習(xí)筆記(深入)”;

/**
     * 注冊(cè)或修改類庫(kù)別名映射記錄
     * @access public
     * @param class String|Array 如果為數(shù)組鍵為類庫(kù)別名,鍵值為類庫(kù)實(shí)際位置;如果字符串代表類庫(kù)別名
     * @param map   String 		    如果class為字符串,該參數(shù)代表類庫(kù)實(shí)際位置,否則沒(méi)有意義
     * @return void
     */
    static public function addMap($class, $map=''){
    	//判斷class是否為數(shù)組,如果是就合并當(dāng)前類庫(kù)別名記錄,如果有相同記錄就會(huì)覆蓋
    	if(is_array($class)){
    		self::$_map = array_merge(self::$_map, $class);
    	}else{
    		//如果class不是數(shù)組,就作為別名儲(chǔ)存在類庫(kù)別名記錄中,并把map作為實(shí)際類庫(kù)位置
    		self::$_map[$class] = $map;
    	}
    }
    
    /**
     * 根據(jù)別名獲取類庫(kù)實(shí)際地址
     * @param class String 	類庫(kù)別名
     * @return Array|String|NULL	如果class為空則獲取所有類庫(kù)別名記錄,否者返回別名對(duì)應(yīng)的類庫(kù)位置
     */
    static public function getMap($class=''){
    	//如果class為空,直接返回所有類庫(kù)別名記錄
    	if(''===$class){
    		return self::$_map;
    		//判斷對(duì)應(yīng)別名是否在別名映射記錄中,如果存在返回類庫(kù)實(shí)際地址,否者返回null
    	}elseif(isset(self::$_map[$class])){
    		return self::$_map[$class];
    	}else{
    		return null;
    	}
    }
登錄后復(fù)制

四、ThinkPHP類庫(kù)自動(dòng)加載機(jī)制autoload()方法分析,該方法是由Think::start()方法中的第一句代碼注冊(cè)實(shí)現(xiàn)spl_autoload_register('ThinkThink::autoload');

/**
     * 類庫(kù)自動(dòng)加載
     * @param string $class 對(duì)象類名
     * @return void
     */
    public static function autoload($class) {
        //判斷是否存在別名映射
        //標(biāo)記一處bug,如果使用Think::addMap('ThinkTest');注冊(cè)別名就完了,程序邏輯不嚴(yán)謹(jǐn),不會(huì)有大的安全問(wèn)題,可以無(wú)視
        //具體可以看Think::addMap()方法的實(shí)現(xiàn)        
        if(isset(self::$_map[$class])) {
            include self::$_map[$class];
            //建議在這里renturn;
            
        //判斷是否存在符號(hào),存在則使用命名空間加載機(jī)制
        //此處與配置說(shuō)明不符'APP_AUTOLOAD_PATH'     =>  '', // 自動(dòng)加載的路徑 關(guān)閉APP_USE_NAMESPACE后有效
        //現(xiàn)在判斷的是否在類名中使用命名規(guī)則而不是使用APP_USE_NAMESPACE配置,當(dāng)然該項(xiàng)配置主要作用在路由模塊,后續(xù)會(huì)講解
        }elseif(strpos($class,'\')){
         //獲取命名空間的第一個(gè)命名范圍
          $name           =   strstr($class, '\', true);
          //判斷該命名空間的第一個(gè)命名范圍是否在Think約定范圍類,并在Library目錄下存在該目錄
          if(in_array($name,array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$name)){ 
              // Library目錄下面的命名空間自動(dòng)定位
              $path       =   LIB_PATH;
          }else{
              //檢測(cè)自定義命名空間 否則就以模塊為命名空間
              $namespace  =   C('AUTOLOAD_NAMESPACE');
              $path       =   isset($namespace[$name])? dirname($namespace[$name]).'/' : APP_PATH;
          }
          //這里可以看出ThinkPHP命名空間的命名規(guī)則是以LIB_PATH和APP_PATH作為根目錄的目錄原則,也可以為AUTOLOAD_NAMESPACE配置項(xiàng)來(lái)自定義命名規(guī)則根目錄
          $filename       =   $path . str_replace('\', '/', $class) . EXT;
          
          //判斷文件是否存在,為啥在注冊(cè)別名的時(shí)候不去判斷文件是否存在呢?那樣是否可以無(wú)視一些有問(wèn)題的別名映射,而使用正確的加載規(guī)則呢?
          if(is_file($filename)) {
              //如果在windows環(huán)境下運(yùn)行,使用strpos來(lái)檢測(cè)是否大小寫一致問(wèn)題,如果不一致直接返回
              if (IS_WIN && false === strpos(str_replace('/', '\', realpath($filename)), $class . EXT)){
                  return ;
              }
              
              //導(dǎo)入類文件
              include $filename;
              
              //這里建議return;
          }
        }else{
            //不用按照配置文件中的APP_USE_NAMESPACE的值來(lái)決定是否APP_AUTOLOAD_LAYER配置該項(xiàng)
            //只要在類庫(kù)加載中沒(méi)有使用命名空間就會(huì)調(diào)用以下規(guī)則來(lái)查找類庫(kù)(要實(shí)例化的類不能聲明命名規(guī)則)
            foreach(explode(',',C('APP_AUTOLOAD_LAYER')) as $layer){
            	//判斷類名最后幾位是否符合APP_AUTOLOAD_LAYER配置項(xiàng)
                if(substr($class,-strlen($layer))==$layer){
                	//加載當(dāng)前模塊下對(duì)應(yīng)的類文件,這個(gè)其實(shí)可以直接判斷文件是否存在,并用include加載即可,沒(méi)有必要調(diào)用require_cache函數(shù)
                	//以上是個(gè)人見(jiàn)解,緣由是因?yàn)樯厦娴募虞d機(jī)制都沒(méi)有使用,我覺(jué)得是否應(yīng)該統(tǒng)一,而且自動(dòng)加載類文件,不用限制加載一次,如果已經(jīng)加載了,也不會(huì)調(diào)用這個(gè)方法了。
                    if(require_cache(MODULE_PATH.$layer.'/'.$class.EXT)) {
                    	//加載文件成功直接返回
                        return ;
                    }
                }            
            }
            
            //根據(jù)APP_AUTOLOAD_PATH配置設(shè)置的路徑規(guī)則自動(dòng)搜索并加載文件
            foreach (explode(',',C('APP_AUTOLOAD_PATH')) as $path){
            	//這里同上,是否也可以不用調(diào)用import()方法加載,或者統(tǒng)一了呢?
                if(import($path.'.'.$class))
                    // 如果加載類成功則返回
                    return ;
            }
          }
    }
登錄后復(fù)制

五、管理類實(shí)例或者'緩存'類靜態(tài)方法調(diào)用結(jié)果instance()方法分析,之前在類結(jié)構(gòu)分析中說(shuō)$_instance變量保存類實(shí)例并不合理,因?yàn)樵擃愡€可以調(diào)用類的靜態(tài)方法,并'緩存'結(jié)果。我在該方法注釋中提出一些個(gè)人見(jiàn)解,并不是說(shuō)該方法設(shè)計(jì)的不合理,這個(gè)是畢竟是ThinkPHP專有方法,任何一個(gè)項(xiàng)目的設(shè)計(jì)都不會(huì)像我那般去考慮一個(gè)方法的諸多問(wèn)題,首要問(wèn)題是解決是否符合項(xiàng)目應(yīng)用足矣。同樣看到這篇文章的朋友可以思考,在項(xiàng)目是否有很多類并不需要多個(gè)實(shí)例(沒(méi)有強(qiáng)制約束的情況下),如果有那可以設(shè)計(jì)一個(gè)適合自己項(xiàng)目的偽單例工廠來(lái)管理這些類的實(shí)例。

/**
     * 取得對(duì)象實(shí)例 支持調(diào)用類的靜態(tài)方法
     * 解析:我把該類看成一個(gè)偽單例工廠,不是嚴(yán)格要求類不許是單例,統(tǒng)一使用該方法獲取類對(duì)象,可以實(shí)現(xiàn)單例模式(極其適合php這種較為靈活的語(yǔ)言)
     * 		說(shuō)這是一個(gè)單例模式工廠不合格,因?yàn)闆](méi)有嚴(yán)格要求所管理類必須符合單例模式約束。
     * 問(wèn)題:該方法說(shuō)此類可以調(diào)用類的靜態(tài)方法,并沒(méi)有約定靜態(tài)方法必須返回類的實(shí)例(self),可以返回任意結(jié)果,這個(gè)讓我很詫異
     * 		如果是想要緩存類方法調(diào)用結(jié)果,是否應(yīng)該提供給方法傳遞參數(shù)選項(xiàng)呢?
     * 		如果僅僅為了管控類的實(shí)例(比如嚴(yán)格按照單例模式設(shè)計(jì)的類,如果要管理,必須使用靜態(tài)方法),是否應(yīng)該說(shuō)明或者在方法中檢測(cè)返回值呢?
     * 解惑:這個(gè)畢竟不是提供給應(yīng)用的方法(當(dāng)然可以使用,設(shè)計(jì)初衷肯定不是,這算是ThinkPHP開(kāi)發(fā)團(tuán)隊(duì)約定俗成的規(guī)定(口頭約束使用方法)吧?)
     * 		這至少給我們一個(gè)啟示,可以這么管理偽單例模式(口頭約束的方式,當(dāng)然比之更可靠),之前我在一個(gè)項(xiàng)目中設(shè)計(jì)過(guò)這樣一個(gè)工廠類(那時(shí)候還沒(méi)有分析過(guò)任何產(chǎn)品的源碼)
     * @param string $class 對(duì)象類名
     * @param string $method 類的靜態(tài)方法名
     * @return object
     */
    static public function instance($class,$method='') {
    	//生成實(shí)例管理標(biāo)識(shí)
        $identify   =   $class.$method;
        //判斷是否已經(jīng)存在改類實(shí)例標(biāo)識(shí)
        if(!isset(self::$_instance[$identify])) {
        	//判斷類是否存在,這里反應(yīng)不能使用自動(dòng)加載機(jī)制,必須在調(diào)用該方法前加載類文件
            if(class_exists($class)){
            	//實(shí)例化類,這里可以看出并不能管理嚴(yán)格意義上的單例類
                $o = new $class();
                
                //判斷是否要調(diào)用靜態(tài)方法,并確定該類是否存在該方法
                if(!empty($method) && method_exists($o,$method))
                	//返回調(diào)用結(jié)果(不明確是什么)
                    self::$_instance[$identify] = call_user_func(array(&$o, $method));
                else
                	//存儲(chǔ)類實(shí)例對(duì)象
                    self::$_instance[$identify] = $o;
            }
            else
            	//輸出錯(cuò)誤信息
                self::halt(L('_CLASS_NOT_EXIST_').':'.$class);
        }
        //返回實(shí)例對(duì)象
        return self::$_instance[$identify];
    }
登錄后復(fù)制

六、ThinkPHP內(nèi)置錯(cuò)誤處理和異常處理實(shí)現(xiàn)分析。appException()方法由Think::start()中set_exception_handler('ThinkThink::appException');語(yǔ)句實(shí)現(xiàn)。appError()方法由Think::start()中set_error_handler('ThinkThink::appError');語(yǔ)句實(shí)現(xiàn),fatalError()方法由Think::start()中register_shutdown_function('ThinkThink::fatalError');語(yǔ)句實(shí)現(xiàn)。halt()方法用來(lái)輸出重要錯(cuò)誤信息和異常,并終止程序執(zhí)行。trace()方法用來(lái)記錄并管理Trace調(diào)試工具中的錯(cuò)誤信息。

/**
     * 自定義異常處理
     * @access public
     * @param mixed $e 異常對(duì)象
     * @param void
     */
    static public function appException($e) {
        $error = array();
        //獲取異常錯(cuò)誤信息
        $error['message']   =   $e->getMessage();
        //獲取backtrace()回溯信息
        $trace              =   $e->getTrace();
        //判斷是否由異常處理方法拋出,ThinkPHP自定義拋出異常處理函數(shù)
        if('E'==$trace[0]['function']) {
            $error['file']  =   $trace[0]['file'];//獲取錯(cuò)誤文件
            $error['line']  =   $trace[0]['line'];//獲取錯(cuò)誤行號(hào)
        }else{
            $error['file']  =   $e->getFile();//獲取錯(cuò)誤文件
            $error['line']  =   $e->getLine();//獲取錯(cuò)誤行號(hào)
        }
        //已格式錯(cuò)誤回溯信息
        $error['trace']     =   $e->getTraceAsString();
        //寫入到錯(cuò)誤日志
        Log::record($error['message'],Log::ERR);
        // 發(fā)送404信息
        header('HTTP/1.1 404 Not Found');
        header('Status:404 Not Found');
        //顯示錯(cuò)誤信息
        self::halt($error);
    }

    /**
     * 自定義錯(cuò)誤處理
     * @access public
     * @param int $errno 錯(cuò)誤類型
     * @param string $errstr 錯(cuò)誤信息
     * @param string $errfile 錯(cuò)誤文件
     * @param int $errline 錯(cuò)誤行數(shù)
     * @return void
     */
    static public function appError($errno, $errstr, $errfile, $errline) {
      switch ($errno) {
      	  //一些重要的錯(cuò)誤信息,會(huì)影響之后的程序執(zhí)行
          case E_ERROR:
          case E_PARSE:
          case E_CORE_ERROR:
          case E_COMPILE_ERROR:
          case E_USER_ERROR:
          	//清空輸出緩沖(不知道在哪里開(kāi)啟了,后面分析會(huì)遇到)
            ob_end_clean();//其實(shí)就是把php默認(rèn)輸出的錯(cuò)誤信息清除掉
            //錯(cuò)誤信息
            $errorStr = "$errstr ".$errfile." 第 $errline 行.";
            //根據(jù)LOG_RECORD是否記錄錯(cuò)誤信息,決定是否寫入錯(cuò)誤日志
            if(C('LOG_RECORD')) Log::write("[$errno] ".$errorStr,Log::ERR);
            //輸出錯(cuò)誤信息
            self::halt($errorStr);
            break;
            
          //可以忽略的錯(cuò)誤信息,不會(huì)輸出,會(huì)記錄到trace當(dāng)中,使用SHOW_PAGE_TRACE配置可以查看的錯(cuò)誤信息
          default:
          	//這里程序還要繼續(xù)執(zhí)行,不能清空輸出緩沖,如果不希望顯示這類錯(cuò)誤信息,應(yīng)當(dāng)在php.ini中調(diào)節(jié),或者使用ini_set()的函數(shù)改變
            //錯(cuò)誤信息
          	$errorStr = "[$errno] $errstr ".$errfile." 第 $errline 行.";
          	//記錄到trace當(dāng)中
            self::trace($errorStr,'','NOTIC');
            break;
      }
    }
    
    // 致命錯(cuò)誤捕獲
    static public function fatalError() {
    	//致命錯(cuò)誤必須保存到日志中
        Log::save();
        //獲取上一個(gè)錯(cuò)誤信息,沒(méi)有錯(cuò)誤就跳過(guò)了(⊙0⊙)
        if ($e = error_get_last()) {
        	//處理致命錯(cuò)誤信息
            switch($e['type']){
              case E_ERROR:
              case E_PARSE:
              case E_CORE_ERROR:
              case E_COMPILE_ERROR:
              case E_USER_ERROR:  
              	//清空輸出緩存,都導(dǎo)致程序停止了,還顯示什么呀
                ob_end_clean();
                //輸出錯(cuò)誤信息
                self::halt($e);
                break;
            }
        }
    }
    
	/**
     * 錯(cuò)誤輸出
     * @param mixed $error 錯(cuò)誤
     * @return void
     */
    static public function halt($error) {
        $e = array();
        //判斷是否是調(diào)試模式,或者命令行模式
        if (APP_DEBUG || IS_CLI) {
            //調(diào)試模式下輸出錯(cuò)誤信息
            //如果錯(cuò)誤信息不是一個(gè)數(shù)組,就回溯最后一次執(zhí)行方法的信息
            if (!is_array($error)) {
                $trace          = debug_backtrace();
                $e['message']   = $error;
                $e['file']      = $trace[0]['file'];
                $e['line']      = $trace[0]['line'];
                ob_start();//開(kāi)始輸出緩存
                debug_print_backtrace();//輸出一條回溯信息
                $e['trace']     = ob_get_clean();//獲取輸出緩沖信息,并清空
            } else {
                $e              = $error;
            }
            if(IS_CLI){
            	//命令行模式,轉(zhuǎn)換為gbk編碼,終止程序執(zhí)行并輸出錯(cuò)誤信息
                exit(iconv('UTF-8','gbk',$e['message']).PHP_EOL.'FILE: '.$e['file'].'('.$e['line'].')'.PHP_EOL.$e['trace']);
            }
        } else {
            //不是調(diào)試模式,重定向到錯(cuò)誤頁(yè)面
            $error_page         = C('ERROR_PAGE');//獲取設(shè)置的錯(cuò)誤頁(yè)面
            if (!empty($error_page)) {
            	//重定向到錯(cuò)誤頁(yè)面
                redirect($error_page);
            } else {
            	//根據(jù)SHOW_ERROR_MSG配置決定是否顯示詳細(xì)的錯(cuò)誤信息,還是采用ERROR_MESSAGE設(shè)定的錯(cuò)誤信息
                $message        = is_array($error) ? $error['message'] : $error;
                $e['message']   = C('SHOW_ERROR_MSG')? $message : C('ERROR_MESSAGE');
            }
        }
        //根據(jù)TMPL_EXCEPTION_FILE配置決定調(diào)用錯(cuò)誤信息顯示模版,否者采用ThinkPHP默認(rèn)模版
        $exceptionFile =  C('TMPL_EXCEPTION_FILE',null,THINK_PATH.'Tpl/think_exception.tpl');
        include $exceptionFile;
        exit;//終止程序運(yùn)行,很重要的。
    }

    /**
     * 添加和獲取頁(yè)面Trace記錄
     * @param string $value 變量
     * @param string $label 標(biāo)簽
     * @param string $level 日志級(jí)別(或者頁(yè)面Trace的選項(xiàng)卡)
     * @param boolean $record 是否記錄日志
     * @return void
     */
    static public function trace($value='[think]',$label='',$level='DEBUG',$record=false) {
    	//采用靜態(tài)變量存儲(chǔ)Trace記錄
        static $_trace =  array();
        if('[think]' === $value){ // 獲取trace信息
            return $_trace;
        }else{
        	//錯(cuò)誤信息
            $info   =   ($label?$label.':':'').print_r($value,true);
            $level  =   strtoupper($level);//將錯(cuò)誤級(jí)別轉(zhuǎn)換為大寫
            
            //如果是AjAX請(qǐng)求或者不顯示TRACE調(diào)試工具,或者$record要求記錄日志,就不會(huì)記錄該條錯(cuò)誤信息
            if((defined('IS_AJAX') && IS_AJAX) || !C('SHOW_PAGE_TRACE')  || $record) {
                Log::record($info,$level,$record);//將錯(cuò)誤信息寫入到日志文件
            }else{
            	//判斷錯(cuò)誤等級(jí)是否存在或者該類錯(cuò)誤信息是否達(dá)到錯(cuò)誤類型記錄上限,由TRACE_MAX_RECORD配置
                if(!isset($_trace[$level]) || count($_trace[$level])>C('TRACE_MAX_RECORD')) {
                	//這里有個(gè)我很詫異的地方,當(dāng)錯(cuò)誤類別記錄達(dá)到錯(cuò)誤類型記錄上限的是否為什么要重置該類型錯(cuò)誤記錄
                	//而不是不記錄當(dāng)前錯(cuò)誤信息,或者刪除最先一條的錯(cuò)誤信息,追加到最后
                	//我建議是不理會(huì)比較合理,因?yàn)檎{(diào)試錯(cuò)誤,也有先后嗎,先把之前遇到的錯(cuò)誤解決,就會(huì)看到新的錯(cuò)誤了(這是否有點(diǎn)坑⊙0⊙)
                    $_trace[$level] =   array();
                }
                //按錯(cuò)類別記錄錯(cuò)誤信息
                $_trace[$level][]   =   $info;
            }
        }
    }
登錄后復(fù)制

七、總結(jié):對(duì)該類分析,主要掌控php錯(cuò)誤處理和異常處理方面的知識(shí),并了解基于命名空間自動(dòng)加載的規(guī)則定義基礎(chǔ),同樣接觸了ThinkPHP運(yùn)行時(shí)編譯緩存機(jī)制帶來(lái)的IO優(yōu)化思路以及類庫(kù)別名機(jī)制對(duì)與類自動(dòng)加載帶來(lái)的優(yōu)化。在分析該類時(shí)站在ThinkPHP應(yīng)用外對(duì)該類提出幾處質(zhì)疑,僅為個(gè)人對(duì)面向?qū)ο笤O(shè)計(jì)的理解和認(rèn)知,不作為詳細(xì)參考。

PHP速學(xué)教程(入門到精通)
PHP速學(xué)教程(入門到精通)

PHP怎么學(xué)習(xí)?PHP怎么入門?PHP在哪學(xué)?PHP怎么學(xué)才快?不用擔(dān)心,這里為大家提供了PHP速學(xué)教程(入門到精通),有需要的小伙伴保存下載就能學(xué)習(xí)啦!

下載
來(lái)源:php中文網(wǎng)
本文內(nèi)容由網(wǎng)友自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請(qǐng)聯(lián)系admin@php.cn
最新問(wèn)題
開(kāi)源免費(fèi)商場(chǎng)系統(tǒng)廣告
最新下載
更多>
網(wǎng)站特效
網(wǎng)站源碼
網(wǎng)站素材
前端模板
關(guān)于我們 免責(zé)申明 意見(jiàn)反饋 講師合作 廣告合作 最新更新
php中文網(wǎng):公益在線php培訓(xùn),幫助PHP學(xué)習(xí)者快速成長(zhǎng)!
關(guān)注服務(wù)號(hào) 技術(shù)交流群
PHP中文網(wǎng)訂閱號(hào)
每天精選資源文章推送
PHP中文網(wǎng)APP
隨時(shí)隨地碎片化學(xué)習(xí)
PHP中文網(wǎng)抖音號(hào)
發(fā)現(xiàn)有趣的

Copyright 2014-2025 http://m.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)