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

Home php教程 php手冊(cè) ThinkPHP框架引導(dǎo)類(lèi)的示例代碼分析

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

Apr 21, 2017 am 09:37 AM

該類(lèi)文件在:ThinkPHP/Library/Think/Think.class.php

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

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

namespace Think;//定義命名空間
class Think {
    private static $_map      = array();//類(lèi)庫(kù)別名映射
    private static $_instance = array();//保存類(lèi)實(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) {}//類(lèi)庫(kù)自動(dòng)加載
    static public function instance($class,$method='') {}//取得對(duì)象實(shí)例 支持調(diào)用類(lèi)的靜態(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記錄
}

二、應(yīng)用程序初始化start()方法分析,該方法包含一套錯(cuò)誤和異常處理機(jī)制,非常受用。該方法作為T(mén)hinkPHP框架的引導(dǎo)接口,實(shí)現(xiàn)錯(cuò)誤、異常處理,配置加載,別名映射,行為注冊(cè),包含運(yùn)行緩存的生成,網(wǎng)站應(yīng)用目錄檢測(cè),自動(dòng)類(lèi)庫(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('Think\Think','autoload'));
    	//建議使用spl_autoload_register(__NAMESPACE__.'\Think::autoload');實(shí)現(xiàn)
    	//一下所有注冊(cè)方式均可以使用上面3中形式傳遞參數(shù)
    	spl_autoload_register('Think\Think::autoload');
    	
    	//注冊(cè)全局腳本"析構(gòu)函數(shù)",使用該方式注冊(cè)的函數(shù),會(huì)在腳本結(jié)束前調(diào)用,大多數(shù)情況用來(lái)處理致命錯(cuò)誤
    	register_shutdown_function('Think\Think::fatalError');
    	//設(shè)置自定義錯(cuò)誤處理函數(shù),用于處理錯(cuò)誤信息
    	set_error_handler('Think\Think::appError');
    	//設(shè)置未異常處理函數(shù)
    	set_exception_handler('Think\Think::appException');
    	//可以把register_shutdown_function(),set_error_handler(),set_error_handler()3個(gè)函數(shù)組合完成自定義、多元化的錯(cuò)誤處理模塊
    	
    	//根據(jù)STORAGE_TYPE的值設(shè)置分布式文件存儲(chǔ)方案,Storage是一個(gè)工廠類(lèi),用于管理和維護(hù)分布式文件存儲(chǔ)組件
    	//后面會(huì)詳細(xì)講解Storage類(lèi),并指出設(shè)計(jì)缺陷
    	Storage::connect(STORAGE_TYPE);
    	
    	//根據(jù)運(yùn)行模式在運(yùn)行緩存目錄下生成編譯緩存文件APP_MODE.'~runtime.php',從而減少I(mǎi)O開(kāi)銷(xiāo)
    	//下面會(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ì)影響下列加載不同的類(lèi)庫(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)決定要加載的核心類(lèi)和函數(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)記錄類(lèi)庫(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鉤子類(lèi)實(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  .=  "\nnamespace { Think\Think::addMap(".var_export(self::$_map,true).");";
    			//L(".var_export(L(),true).");生成語(yǔ)言加載代碼
    			//C(".var_export(C(),true).');生成配置項(xiàng)加載代碼
    			//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變量?jī)?nèi)容去除注釋和換行、空隔之后寫(xiě)入到運(yùn)行時(shí)編譯緩存文件
    			Storage::put($runtimefile,strip_whitespace(&#39;<?php &#39;.$content));
    		}else{
    			// 調(diào)試模式加載系統(tǒng)默認(rèn)的配置文件
    			C(include THINK_PATH.&#39;Conf/debug.php&#39;);
    			// 讀取應(yīng)用調(diào)試配置文件
    			if(is_file(CONF_PATH.&#39;debug.php&#39;))
    				C(include CONF_PATH.&#39;debug.php&#39;);
    		}
    	}
    
    	//根據(jù)APP_STATUS讀取當(dāng)前部署環(huán)境配置文件,常用在上線前數(shù)據(jù)庫(kù)連接配置等,用于覆蓋默認(rèn)配置行為
    	if(APP_STATUS && is_file(CONF_PATH.APP_STATUS.&#39;.php&#39;))
    		C(include CONF_PATH.APP_STATUS.&#39;.php&#39;);
    
    	// 設(shè)置系統(tǒng)時(shí)區(qū)
    	//建議可以寫(xiě)成date_default_timezone_set(C(&#39;DEFAULT_TIMEZONE&#39;,null,&#39;PRC&#39;));防止配置項(xiàng)讀取問(wèn)題導(dǎo)致的時(shí)區(qū)設(shè)置錯(cuò)誤
    	date_default_timezone_set(C(&#39;DEFAULT_TIMEZONE&#39;));
    
    	// 檢查應(yīng)用目錄結(jié)構(gòu) 如果不存在則自動(dòng)創(chuàng)建
    	if(C(&#39;CHECK_APP_DIR&#39;) && !is_dir(LOG_PATH)) {
    		//build.php負(fù)責(zé)創(chuàng)建應(yīng)用目錄結(jié)構(gòu)
    		require THINK_PATH.&#39;Common/build.php&#39;;
    	}
    
    	// 記錄加載文件時(shí)間
    	G(&#39;loadTime&#39;);
    	// 運(yùn)行應(yīng)用
    	App::run();
    }

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

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

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

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

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

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

六、ThinkPHP內(nèi)置錯(cuò)誤處理和異常處理實(shí)現(xiàn)分析。appException()方法由Think::start()中set_exception_handler('Think\Think::appException');語(yǔ)句實(shí)現(xiàn)。appError()方法由Think::start()中set_error_handler('Think\Think::appError');語(yǔ)句實(shí)現(xiàn),fatalError()方法由Think::start()中register_shutdown_function('Think\Think::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[&#39;message&#39;]   =   $e->getMessage();
        //獲取backtrace()回溯信息
        $trace              =   $e->getTrace();
        //判斷是否由異常處理方法拋出,ThinkPHP自定義拋出異常處理函數(shù)
        if(&#39;E&#39;==$trace[0][&#39;function&#39;]) {
            $error[&#39;file&#39;]  =   $trace[0][&#39;file&#39;];//獲取錯(cuò)誤文件
            $error[&#39;line&#39;]  =   $trace[0][&#39;line&#39;];//獲取錯(cuò)誤行號(hào)
        }else{
            $error[&#39;file&#39;]  =   $e->getFile();//獲取錯(cuò)誤文件
            $error[&#39;line&#39;]  =   $e->getLine();//獲取錯(cuò)誤行號(hào)
        }
        //已格式錯(cuò)誤回溯信息
        $error[&#39;trace&#39;]     =   $e->getTraceAsString();
        //寫(xiě)入到錯(cuò)誤日志
        Log::record($error[&#39;message&#39;],Log::ERR);
        // 發(fā)送404信息
        header(&#39;HTTP/1.1 404 Not Found&#39;);
        header(&#39;Status:404 Not Found&#39;);
        //顯示錯(cuò)誤信息
        self::halt($error);
    }

    /**
     * 自定義錯(cuò)誤處理
     * @access public
     * @param int $errno 錯(cuò)誤類(lèi)型
     * @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ò)誤信息,決定是否寫(xiě)入錯(cuò)誤日志
            if(C(&#39;LOG_RECORD&#39;)) Log::write("[$errno] ".$errorStr,Log::ERR);
            //輸出錯(cuò)誤信息
            self::halt($errorStr);
            break;
            
          //可以忽略的錯(cuò)誤信息,不會(huì)輸出,會(huì)記錄到trace當(dāng)中,使用SHOW_PAGE_TRACE配置可以查看的錯(cuò)誤信息
          default:
          	//這里程序還要繼續(xù)執(zhí)行,不能清空輸出緩沖,如果不希望顯示這類(lèi)錯(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,&#39;&#39;,&#39;NOTIC&#39;);
            break;
      }
    }
    
    // 致命錯(cuò)誤捕獲
    static public function fatalError() {
    	//致命錯(cuò)誤必須保存到日志中
        Log::save();
        //獲取上一個(gè)錯(cuò)誤信息,沒(méi)有錯(cuò)誤就跳過(guò)了(⊙0⊙)
        if ($e = error_get_last()) {
        	//處理致命錯(cuò)誤信息
            switch($e[&#39;type&#39;]){
              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[&#39;message&#39;]   = $error;
                $e[&#39;file&#39;]      = $trace[0][&#39;file&#39;];
                $e[&#39;line&#39;]      = $trace[0][&#39;line&#39;];
                ob_start();//開(kāi)始輸出緩存
                debug_print_backtrace();//輸出一條回溯信息
                $e[&#39;trace&#39;]     = ob_get_clean();//獲取輸出緩沖信息,并清空
            } else {
                $e              = $error;
            }
            if(IS_CLI){
            	//命令行模式,轉(zhuǎn)換為gbk編碼,終止程序執(zhí)行并輸出錯(cuò)誤信息
                exit(iconv(&#39;UTF-8&#39;,&#39;gbk&#39;,$e[&#39;message&#39;]).PHP_EOL.&#39;FILE: &#39;.$e[&#39;file&#39;].&#39;(&#39;.$e[&#39;line&#39;].&#39;)&#39;.PHP_EOL.$e[&#39;trace&#39;]);
            }
        } else {
            //不是調(diào)試模式,重定向到錯(cuò)誤頁(yè)面
            $error_page         = C(&#39;ERROR_PAGE&#39;);//獲取設(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[&#39;message&#39;] : $error;
                $e[&#39;message&#39;]   = C(&#39;SHOW_ERROR_MSG&#39;)? $message : C(&#39;ERROR_MESSAGE&#39;);
            }
        }
        //根據(jù)TMPL_EXCEPTION_FILE配置決定調(diào)用錯(cuò)誤信息顯示模版,否者采用ThinkPHP默認(rèn)模版
        $exceptionFile =  C(&#39;TMPL_EXCEPTION_FILE&#39;,null,THINK_PATH.&#39;Tpl/think_exception.tpl&#39;);
        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=&#39;[think]&#39;,$label=&#39;&#39;,$level=&#39;DEBUG&#39;,$record=false) {
    	//采用靜態(tài)變量存儲(chǔ)Trace記錄
        static $_trace =  array();
        if(&#39;[think]&#39; === $value){ // 獲取trace信息
            return $_trace;
        }else{
        	//錯(cuò)誤信息
            $info   =   ($label?$label.&#39;:&#39;:&#39;&#39;).print_r($value,true);
            $level  =   strtoupper($level);//將錯(cuò)誤級(jí)別轉(zhuǎn)換為大寫(xiě)
            
            //如果是AjAX請(qǐng)求或者不顯示TRACE調(diào)試工具,或者$record要求記錄日志,就不會(huì)記錄該條錯(cuò)誤信息
            if((defined(&#39;IS_AJAX&#39;) && IS_AJAX) || !C(&#39;SHOW_PAGE_TRACE&#39;)  || $record) {
                Log::record($info,$level,$record);//將錯(cuò)誤信息寫(xiě)入到日志文件
            }else{
            	//判斷錯(cuò)誤等級(jí)是否存在或者該類(lèi)錯(cuò)誤信息是否達(dá)到錯(cuò)誤類(lèi)型記錄上限,由TRACE_MAX_RECORD配置
                if(!isset($_trace[$level]) || count($_trace[$level])>C(&#39;TRACE_MAX_RECORD&#39;)) {
                	//這里有個(gè)我很詫異的地方,當(dāng)錯(cuò)誤類(lèi)別記錄達(dá)到錯(cuò)誤類(lèi)型記錄上限的是否為什么要重置該類(lèi)型錯(cuò)誤記錄
                	//而不是不記錄當(dāng)前錯(cuò)誤信息,或者刪除最先一條的錯(cuò)誤信息,追加到最后
                	//我建議是不理會(huì)比較合理,因?yàn)檎{(diào)試錯(cuò)誤,也有先后嗎,先把之前遇到的錯(cuò)誤解決,就會(huì)看到新的錯(cuò)誤了(這是否有點(diǎn)坑⊙0⊙)
                    $_trace[$level] =   array();
                }
                //按錯(cuò)類(lèi)別記錄錯(cuò)誤信息
                $_trace[$level][]   =   $info;
            }
        }
    }

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

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to run thinkphp project How to run thinkphp project Apr 09, 2024 pm 05:33 PM

To run the ThinkPHP project, you need to: install Composer; use Composer to create the project; enter the project directory and execute php bin/console serve; visit http://localhost:8000 to view the welcome page.

There are several versions of thinkphp There are several versions of thinkphp Apr 09, 2024 pm 06:09 PM

ThinkPHP has multiple versions designed for different PHP versions. Major versions include 3.2, 5.0, 5.1, and 6.0, while minor versions are used to fix bugs and provide new features. The latest stable version is ThinkPHP 6.0.16. When choosing a version, consider the PHP version, feature requirements, and community support. It is recommended to use the latest stable version for best performance and support.

How to run thinkphp How to run thinkphp Apr 09, 2024 pm 05:39 PM

Steps to run ThinkPHP Framework locally: Download and unzip ThinkPHP Framework to a local directory. Create a virtual host (optional) pointing to the ThinkPHP root directory. Configure database connection parameters. Start the web server. Initialize the ThinkPHP application. Access the ThinkPHP application URL and run it.

Which one is better, laravel or thinkphp? Which one is better, laravel or thinkphp? Apr 09, 2024 pm 03:18 PM

Performance comparison of Laravel and ThinkPHP frameworks: ThinkPHP generally performs better than Laravel, focusing on optimization and caching. Laravel performs well, but for complex applications, ThinkPHP may be a better fit.

How to install thinkphp How to install thinkphp Apr 09, 2024 pm 05:42 PM

ThinkPHP installation steps: Prepare PHP, Composer, and MySQL environments. Create projects using Composer. Install the ThinkPHP framework and dependencies. Configure database connection. Generate application code. Launch the application and visit http://localhost:8000.

How is the performance of thinkphp? How is the performance of thinkphp? Apr 09, 2024 pm 05:24 PM

ThinkPHP is a high-performance PHP framework with advantages such as caching mechanism, code optimization, parallel processing and database optimization. Official performance tests show that it can handle more than 10,000 requests per second and is widely used in large-scale websites and enterprise systems such as JD.com and Ctrip in actual applications.

Development suggestions: How to use the ThinkPHP framework for API development Development suggestions: How to use the ThinkPHP framework for API development Nov 22, 2023 pm 05:18 PM

Development suggestions: How to use the ThinkPHP framework for API development. With the continuous development of the Internet, the importance of API (Application Programming Interface) has become increasingly prominent. API is a bridge for communication between different applications. It can realize data sharing, function calling and other operations, and provides developers with a relatively simple and fast development method. As an excellent PHP development framework, the ThinkPHP framework is efficient, scalable and easy to use.

Development suggestions: How to use the ThinkPHP framework to implement asynchronous tasks Development suggestions: How to use the ThinkPHP framework to implement asynchronous tasks Nov 22, 2023 pm 12:01 PM

"Development Suggestions: How to Use the ThinkPHP Framework to Implement Asynchronous Tasks" With the rapid development of Internet technology, Web applications have increasingly higher requirements for handling a large number of concurrent requests and complex business logic. In order to improve system performance and user experience, developers often consider using asynchronous tasks to perform some time-consuming operations, such as sending emails, processing file uploads, generating reports, etc. In the field of PHP, the ThinkPHP framework, as a popular development framework, provides some convenient ways to implement asynchronous tasks.

See all articles