Loading... # [如何优雅的使用异常](https://blog.p2hp.com/archives/2967) 老子曰:程序开发时,有 80% 的代码在处理各种异常。 由于php实在是太过于灵活简单,很多phper对异常的处理其实不太感冒,于是乎,我们会经常看到 ```php 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非常的强大而且友好,可能由于历史原因,很多人没有习惯使用它。 所以,针对第一个问题,我们在进行框架设计的时候,就可以这么处理: ```php try { $ctrl->_before(); $ctrl->$method(); $ctrl->_after(); } catch (\Exception $e) { $ctrl->_atfer(); //让_after在异常后也能正常执行 throw $e; //再抛出异常 } ``` 抛出异常之后, 通过Exception类自带的 getTrace()方法,可以获得调用栈,这样就能很方便的进行调试。 最后可以通过set_exception_handler自定义异常处理,最终输出正确的数据格式。 帖上一小段我常用的异常处理代码。 假定我们的api代码约定: ```php { code: 0, //非0表示异常 msg: "", //提示信息,非0时有值 data: {} //code=0时的业务数据, } ``` 自定义异常处理类 ```php <?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"); 进行自定义异常处理后,我们在业务层,碰到异常的逻辑,就可以统一的、愉快的进行下面这样的异常抛出了: ```php throw new MyException('param xxx error', ERROR::PARAM_ERROR); ``` 那么最终输出的api将会是: ```php { "code": 1, "msg": "参数异常" } ``` 这样就可以和exit, die 说再见了。 PS: 以上代码大都取自zphp框架,详细可参考ZPHP框架: https://github.com/shenzhe/zphp 最后修改:2023 年 08 月 09 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏