异常与错误处理

异常与错误处理

PHP的异常与错误是分开的,当程序出现异常时会throw一个\Exception(或子类)对象,但是当出现错误时会触发一个错误。

1. 异常处理

1.1 通过try...catch主动处理异常

<?php
/**
 * Class UserNotExistsException
 * @datetime 2020/7/2 5:11 下午
 * @author   roach
 * @email    jhq0113@163.com
 */
class UserNotExistsException extends \Exception
{
}

/**
 * Class TransferException
 * @datetime 2020/7/2 5:13 下午
 * @author   roach
 * @email    jhq0113@163.com
 */
class TransferException extends \Exception
{

}

/**
 * @param int   $inUserId
 * @param int   $outUserId
 * @param float $amout
 * @return bool
 * @throws TransferException
 * @throws UserNotExistsException
 * @datetime 2020/7/2 5:15 下午
 * @author   roach
 * @email    jhq0113@163.com
 */
function transfer($inUserId, $outUserId, $amout)
{
    if($inUserId < 1) {
        throw new UserNotExistsException('入金用户不存在');
    }

    if($outUserId < 1) {
        throw new UserNotExistsException('出金用户不存在');
    }

    if(bccomp($amout, '0', 2) <= 0) {
        throw new TransferException('转账金额必须大于0');
    }

    if(bccomp($amout, '100000000', 2) > 0) {
        throw new \Exception('服务器内部错误');
    }

    return true;
}


try {
    $result = transfer(1,0, 5);
    if($result) {
        exit(json_encode([
            'code' => 200,
            'msg'  => 'success',
            'data' => []
        ]));
    }
}catch (\UserNotExistsException $exception) {
    exit(json_encode([
        'code' => 404,
        'msg'  => $exception->getMessage(),
        'data' => []
    ]));
} catch (\TransferException $exception) {
    exit(json_encode([
        'code' => 501,
        'msg'  => $exception->getMessage(),
        'data' => []
    ]));
} catch (\Exception $exception) {
    throw $exception;
}

以上例程输出:

{"code":404,"msg":"\u51fa\u91d1\u7528\u6237\u4e0d\u5b58\u5728","data":[]}

当开发人员对异常有明确的预期时,可以使用以上的方式,catch不用类型的异常,然后对不用的异常做各种不同的处理。

1.2 set_exception_handler处理异常

PHP可以通过set_exception_handler方法注册一个回调函数,当没有用try...catch捕获的异常都会交个回调函数处理。

同样调用以上转账逻辑,不同的是,把try...catch代码块注释掉,换成如下代码:

set_exception_handler(function ($exception){
    echo $exception->getMessage().PHP_EOL;
});

transfer(1, 1, 0);

以上例程输出:

转账金额不能小于0

从以上例程我们可以看出,set_exception_handler方式适合做默认统一的异常处理,同时也可以加一些统一的处理(如:异常日志、监控报警等)
try...catch适合做一些开发主动捕获异常,通过主动捕获异常做一些特殊需求处理。

2. 错误处理

PHP语言的error分为如下等级:

常量说明备注
E_ERROR1致命的运行时错误。这类错误一般是不可恢复的情况,例如内存分配导致的问题。后果是导致脚本终止不再继续运行
E_WARNING2运行时警告 (非致命错误)。仅给出提示信息,但是脚本不会终止运行
E_PARSE4编译时语法解析错误。解析错误仅仅由分析器产生。
E_NOTICE8运行时通知。表示脚本遇到可能会表现为错误的情况,但是在可以正常运行的脚本里面也可能会有类似的通知。
E_CORE_ERROR16在PHP初始化启动过程中发生的致命错误。该错误类似E_ERROR,但是是由PHP引擎核心产生的。
E_CORE_WARNING32PHP初始化启动过程中发生的警告(非致命错误) 。类似E_WARNING,但是是由PHP引擎核心产生的。
E_COMPILE_ERROR64编译时致命错误。类似E_ERROR, 但是是由Zend脚本引擎产生的。
E_COMPILE_WARNING128编译时警告(非致命错误)。类似E_WARNING,但是是由Zend脚本引擎产生的。
E_USER_ERROR256用户产生的致命错误信息。类似E_ERROR, 但是是由用户自己在代码中使用PHP函数trigger_error()来产生的。
E_USER_WARNING512用户产生的警告(非致命)信息。类似E_WARNING, 但是是由用户自己在代码中使用PHP函数trigger_error()来产生的。
E_USER_NOTICE1024用户产生的通知信息。类似E_NOTICE, 但是是由用户自己在代码中使用PHP函数trigger_error()来产生的。
E_STRICT2048启用PHP对代码的修改建议,以确保代码具有最佳的互操作性和向前兼容性。
E_RECOVERABLE_ERROR4096可被捕捉的致命错误。 它表示发生了一个可能非常危险的错误,但是还没有导致PHP引擎处于不稳定的状态。 如果该错误没有被set_error_handler()捕捉,将成为一个E_ERROR从而脚本会终止运行
E_DEPRECATED8192运行时通知。启用后将会对在未来版本中可能无法正常工作的代码给出警告。
E_USER_DEPRECATED16384用户产生的警告信息。 类似E_DEPRECATED, 但是是由用户自己在代码中使用PHP函数trigger_error()来产生的。
E_ALL30719E_STRICT之外的所有错误和警告信息。

上面的值(数值或者符号)用于建立一个二进制位掩码,来制定要报告的错误信息。可以使用按位运算符来组合这些值或者屏蔽某些类型的错误。请注意,在 php.ini 之中,只有’|’, ‘~’, ‘!’, ‘^’ 和 ‘&’ 会正确解析。

Error层次结构

Throwable
    Error
        ArithmeticError
            DivisionByZeroError
            
        AssertionError
        
        CompileError
            ParseError
            
        TypeError
            ArgumentCountError

2.1 try...catch处理错误

PHP7改变了大多数错误的报告方式。不同于传统(PHP5)的错误报告机制,现在大多数错误被作为Error异常抛出。

try...catch捕捉错误代码格式

<?php
try {
   //你的代码逻辑
} catch (\Throwable $t) {
   //只会在PHP7版本执行,PHP5不会执行
}
catch (\Exception $e)
{
   //只会在PHP5执行,PHP7不会执行
}

案例:

<?php
/**
 * @param int   $inUserId
 * @param int   $outUserId
 * @param float $amout
 * @return bool
 * @datetime 2020/7/2 5:15 下午
 * @author   roach
 * @email    jhq0113@163.com
 */
function transfer($inUserId, $outUserId, $amout)
{
    if($inUserId < 1) {
        trigger_error('入金用户不存在', E_USER_WARNING);
    }

    if($outUserId < 1) {
        trigger_error('出金用户不存在', E_USER_WARNING);
    }

    if(bccomp($amout, '0', 2) <= 0) {
        trigger_error('转账金额必须大于0', E_USER_WARNING);
    }

    if(bccomp($amout, '100000000', 2) > 0) {
        trigger_error('服务器内部错误', E_USER_ERROR);
    }

    return true;
}

try {
    transfer(1, 0, 0);
    fff();
}catch (\Throwable $throwable) {
    echo $throwable->getMessage().PHP_EOL;
}

以上例程输出:

PHP Warning:  出金用户不存在 in Exception.php on line 18
PHP Warning:  转账金额必须大于0 in Exception.php on line 22
Call to undefined function fff()

从以上例程可以看出,并不是所有的error都能被try...catch捕捉到,当调用不存在的方法fff()时,被try...catch捕捉到了。

2.2 set_error_handler捕捉错误

同样调用以上转账逻辑,增加set_error_handler捕获错误,如下:

<?php
/**
 * 致命错误
 */
$fatalError = [
    E_ERROR => 1,
    E_PARSE => 1,
    E_CORE_ERROR => 1,
    E_CORE_WARNING => 1,
    E_COMPILE_ERROR => 1,
    E_COMPILE_WARNING => 1
];

set_error_handler(function() use(&$fatalError){
    $args = func_get_args();
    $throwable = new \ErrorException($args[1], $args[0], 1, $args[2], $args[3]);

    echo $throwable->getMessage().PHP_EOL;
    if(isset($fatalError[ $throwable->getCode() ])) {
        exit();
    }
});

try {
    transfer(1, 0, 0);
    fff();
}catch (\Throwable $throwable) {
    echo $throwable->getMessage().PHP_EOL;
}

以上例程输出:

出金用户不存在
转账金额必须大于0
Call to undefined function fff()

关于异常和错误处理就介绍这么多,大家自动动手封装一个面向对象的通用异常处理吧。

学习更多内容: https://404.360tryst.com

我的视频课程: https://edu.csdn.net/course/detail/9933

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍穹0113

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值