老子曰:程序開發時,有 80% 的代碼在處理各種異常。
由于php實在是太過于靈活簡單,很多phper對異常的處理其實不太感冒,于是乎,我們會經常看到
die("xxx");
exit("xxx");
這樣的異常處理,但這類異常對于項目的穩定性卻很不友好,主要有以下幾點問題:
1:粗暴的打斷正常的業務流。
2:調試非常因難。
3:靈活度太差
那我們展開來看這三個問題:
1:現代的框架,大都有一個標準的處理流程:
_before(); //前置控制器,可以做一個數據的初始化
run(); //業務邏輯的處理
_after(); //后置控制器,在處理完業務,有機會進行收尾(比如回收資源,統一打日志等)。
但如果的 業務邏輯處理里(run)直接用 exit, die這類函數會直接退出php當前腳本的執行,從而跳過_after(),這顯然不符合正常的邏輯。
2:筆者曾經有個經歷,打開某個頁面,突然白屏,經過一翻苦苦的debug,終于在某處發現了一個孤零零的exit,沒有任何提示,碰到這樣的代碼,對于調試者來說,就是個噩夢。
3:現在已經不再是pc互聯網的時候,移動互聯網比例已大幅增加,這時,我們往往是輸出一個接口,如果直接碰到exit, die這類輸出可能直接導致客戶端崩潰。
那正確的使用方式是什么?
沒錯,就是php自帶的Exception, php自帶的Exception非常的強大而且友好,可能由于歷史原因,很多人沒有習慣使用它。
所以,針對個問題,我們在進行框架設計的時候,就可以這么處理:
try {
$ctrl->_before();
$ctrl->$method();
$ctrl->_after();
} catch (\Exception $e) {
$ctrl->_atfer(); //讓_after在異常后也能正常執行
throw $e; //再拋出異常
}
拋出異常之后, 通過Exception類自帶的 getTrace()方法,可以獲得調用棧,這樣就能很方便的進行調試。
后可以通過set_exception_handler自定義異常處理,終輸出正確的數據格式。
帖上一小段我常用的異常處理代碼。
假定我們的api代碼約定:
{
code: 0, //非0表示異常
msg: "", //提示信息,非0時有值
data: {} //code=0時的業務數據,
}
自定義異常處理類
<?php
class MyException extends \Exception
{
public $realCode = '';
public function __construct($message, $code = -1)
{
$this->realCode = $code;
parent::__construct($message, $code);
}
public static function exceptionHandler(\Exception $exception)
{
$model = ZFormater::exception($exception); //格式化異常
Log::info([\var_export($model, true)], 'exception'); //異常寫日志
$info = array();
if(property_exists($exception, 'realCode')) {
$codeArr = explode('_', $exception->realCode);
if(count($codeArr) > 1) {
$model['code'] = intval($codeArr[0]);
$model['msg'] = $codeArr[1];
}
}
if ($config['debug_mode']) { //調式模式,輸出調用棧
$info['debug'] = $model;
}
$info['msg'] = $model['message'];
$info['ret'] = empty($model['code']) ? -1 : $model['code'];
if(Request::isAjax()) { //ajax請求,json串輸出
Request::setViewMode('Json');
}
if('Php' == Request::getViewMode()) { //頁面請求,統一的異常頁面展示
if ($config['debug_mode']) {
Request::setTplFile('public/exception.php');
} else {
Request::setTplFile('public/error.php');
}
}
Response::display($info);
}
realCode對應的定義:
<?php
class ERROR
{
const DEF_MSG = '系統異常';
//系統級異常碼
const PARAM_ERROR = '1_參數異常';
const NEED_LOGIN = '2_需要登錄';
const USER_ERROR = '3_用戶名不存在';
const PASS_ERROR = '4_密碼異常';
}
然后通過set_exception_handler("MyException::exceptionHandler"); 進行自定義異常處理后,我們在業務層,碰到異常的邏輯,就可以統一的、愉快的進行下面這樣的異常拋出了:
throw new MyException('param xxx error', ERROR::PARAM_ERROR);
那么終輸出的api將會是:
{
"code": 1,
"msg": "參數異常"
}
這樣就可以和exit, die 說再見了。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。