Thinkphp5進階——01 異常

TimChen666發表於2019-01-18

前言

和PHP預設的異常處理不同,ThinkPHP丟擲的不是單純的錯誤資訊,而是一個錯誤頁面。

如果是做web開發,給客戶端返回這種錯誤頁面還好;但如果是做API開發的話,返回這個頁面客戶端根本無法處理,所以我們需要對異常進行捕獲處理。




異常分類


使用者行為導致的異常

沒有通過驗證器、沒有查詢到結果。

通常不需要記錄日誌,需要向使用者返回具體資訊。



伺服器自身的異常

程式碼錯誤、呼叫外部介面錯誤。

通常需要記錄日誌,不需要向客戶端返回具體原因。



try-catch

相信對於大部分人而言,try-catch並不陌生。

使用try-catch我們可以在可能發生異常的地方進行異常捕獲,然後對異常進行後續處理,返回給客戶端可以處理的錯誤資訊。


例項


try{
	// 可能發生異常的業務邏輯
}
catch(Exception $ex){

	// RESTful風格的錯誤返回資訊
	$err = [
	  'error_code'=>10001,
	  'msg'=>$ex->getMessage()
	];
	
	// 返回json格式的錯誤資訊,並且規定HTTP狀態碼
	return json($err,400);
} 


try-catch這種異常捕獲方式存在以下問題:

  1. 逐層丟擲,如果是最底下的業務邏輯出現了問題會將異常逐層丟擲,否則就會直接被原有的預設異常捕獲到輸出一個預設的錯誤頁面;

  2. 如果是不可預知的異常,未做try-catch處理的業務邏輯,就會輸出一個預設的錯誤頁面;

  3. 某些錯誤資訊不適合直接返回給客戶端,需要做統一處理。

總結起來就是繁瑣,不夠健壯。




構建Validate層

實現步驟大致為:

  1. 自定義錯誤處理器——ExceptionHandler。

  2. config.php的exception_hadle配置項修改為上面的自定義錯誤處理器。

  3. 建立基礎異常處理類——BaseException。

  4. 建立具體異常處理類——xxxException。



錯誤處理器——ExceptionHandler

1)模組同級目錄下,建立lib/exception資料夾

2)建立ExceptionHandler,繼承think\exception\Handle

3)引用Exception類

4)重寫render方法
我們定義,凡是繼承了BaseException的異常,就屬於使用者導致的異常。


namespace app\lib\exception;

// 引用框架的Exception
use Exception;

// 引用Log類
use think\facade\Log;

// 引用Request類
use think\facade\Request;

// 引用框架的Handle
use think\exception\Handle;


class ExceptionHandler extends Handle
{
	private $code;
	private $msg;
	private $errorCode;

	// 重寫框架的render方法
	public function render(Exception $e){
        
		// 如果是使用者類錯誤
		if($e instanceof BaseException){
		  $this->code = $e->code;
		  $this->msg = $e->msg;
		  $this->errorCode = $e->errorCode;
		}
		// 如果是伺服器內部錯誤
		else{
		  
		  $switch = config('app.app_debug');

		  // 開發模式下顯示html頁面
		  if($switch){
		    return parent::render($e);
		  }
		  // 生產模式下顯示json資料
		  else{
		    $this->code = 500;
		    $this->msg = '伺服器內部錯誤,不想告訴你';
		    $this->errorCode = 999;
    
		    // 寫入日誌
		    $this->recordErrorLog($e);
		  }
		}

		// 返回陣列
		$result = [
		  'msg' => $this->msg,
		  'errorCode' => $this->errorCode,
		  'request_url' => Request::url()
		];

		return json($result,$this->code);
	}


	// 伺服器內部錯誤資訊寫入日誌
	private function recordErrorLog(Exception $e){
		Log::write($e->getMessage(),'error');
	}

}



基礎異常處理類——BaseException

1)引用Exception類。

2)定義通用異常錯誤的屬性(http狀態碼、錯誤資訊、自定義錯誤碼)。

3)建構函式可以傳值,覆蓋預設屬性。


namespace app\lib\exception;

use Exception;

class BaseException extends Exception
{   
	// 預設HTTP狀態碼
	public $code = 400;

	// 預設錯誤提示資訊
	public $msg = '引數錯誤';

	// 預設業務錯誤碼
	public $errorCode = 10000;


	// 建構函式
	public function __construct($params = [])
	{
		// 判斷是否陣列
		if(!is_array($params)){
		  return;
		}

		// 判斷是否有code、errorCode、msg這些引數代入
		if(array_key_exists('code',$params)){
		  $this->code = $params['code'];
		}
		if(array_key_exists('msg',$params)){
		  $this->msg = $params['msg'];
		}
		if(array_key_exists('errorCode',$params)){
		  $this->errorCode = $params['errorCode'];
		}
        
    }
}



具體異常處理類

1)新建對應異常類,繼承BaseException。

2)定義具體的異常錯誤屬性。


namespace app\lib\exception;


class BannerMissException extends BaseException
{
	// HTTP狀態碼
	public $code = 404;

	// 提示錯誤資訊
	public $msg = '請求的banner不存在';

	// 自定義錯誤碼
	public $errorCode = 40000;
}


相關文章