如何优雅的使用异常

更新时间:2016-03-22 14:25:16 点击次数:2283次

老子曰:程序开发时,有 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 说再见了。


本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

回到顶部
嘿,我来帮您!