前言
刚开始接触PHP的时候没有意识到异常的重要性,有时候出问题很难精确的找到问题点,正确的处理异常也是一门学问
异常的类别
PHP7异常做了很多变动,异常类 Exception
和错误类 Error
都实现了 Throwable
接口
结构如下:
-
Throwable
-
Error
-
ArithmeticError
- DivisionByZeroError
- AssertionError
- ParseError
-
TypeError
- ArgumentCountError
-
-
Exception
- ClosedGeneratorException
- DOMException
- ErrorException
- IntlException
-
LogicException
-
BadFunctionCallException
- BadMethodCallException
- DomainException
- InvalidArgumentException
- LengthException
- OutOfRangeException
-
- PharException
- ReflectionException
-
RuntimeException
- OutOfBoundsException
- OverflowException
- PDOException
- RangeException
- UnderflowException
- UnexpectedValueException
- SodiumException
-
什么时候才需要抛异常
这个一切从实际出发,如果你觉得你的代码可能会出现问题,就可以进行抛出异常
如何捕获异常
PHP中使用 try...catch...finally
捕获异常
public function test()
{
try {
//可能出错的代码逻辑
} catch (\Exception $e) {
echo $e->getMessage();
} finally {
//todo
}
}
如果不确定出现异常还是错误,可以直接捕获 Throwable
异常
public function test()
{
try {
//可能出错的代码逻辑
} catch (\Throwable $e) {
echo $e->getMessage();
} finally {
//todo
}
}
业务场景实战
现在的项目很多都是前后端分离、restful风格接口的设计进行开发。现在我就用tp5
框架来进行实战下在实际业务中是如何优雅的使用异常的
场景描述
选择一个比较简单的业务场景,以登录模块为例,用户在移动端进行登录时,需要进行登录,注册,忘记密码,获取手机验证码等接口。
构建约束条件
登录
入参:
- 用户手机号
- 用户密码
- 手机验证码
约束:
- 用户手机号不能为空,格式正确,且此用户确实是存在的
- 密码不能为空,密码格式正确
- 手机验证码不能为空,且是在有效期内的
注册
入参:
- 用户手机号
- 用户密码
- 密码二次确认
- 手机验证码
约束:
- 用户手机号不能为空,格式正确,且此用户确实是新用户,系统不存在此用户信息
- 密码不能为空,密码格式正确
- 二次密码要跟密码一样
- 手机验证码不能为空,且是在有效期内的
忘记密码:
入参:
- 用户手机号
- 用户新密码
- 密码二次确认
- 手机验证码
约束:
- 用户手机号不能为空,格式正确,且此用户确实是存在的
- 密码不能为空,密码格式正确
- 二次密码要跟密码一样
- 手机验证码不能为空,且是在有效期内的
获取手机验证码
入参:
- 用户手机号
约束:
- 用户手机号不能为空,格式正确
- 一分钟内只能获取一次
自定义tp5异常
创建异常处理Handle类
# application\lib\exception\ExceptionHandle
namespace application\lib\exception;
use Exception;
use think\exception\Handle;
class ExceptionHandle extends Handle
{
/**
* @var $httpStatusCode http状态码
*/
private $httpStatusCode;
/**
* @var $msg 错误信息
*/
private $msg;
/**
* @var $code 错误码
*/
private $code;
# 自定义错误异常需要重写tp5父类的这个render方法
public function render(Exception $e)
{
if ($e instanceof BaseException) {
# 自定义异常
$this->httpStatusCode = $e->httpStatusCode;
$this->msg = $e->msg;
$this->code = $e->code;
} else {
# 系统异常
//TODO记录错误日志
return parent::render($e)
}
$result = [
'code' => $this->code,
'msg' => $this->msg
];
# 返回给前端
return json($result, $this->httpStatusCode);
}
创建基础的异常类
# application\lib\exception\BaseException
namespace application\lib\exception;
use think\Exception;
# 异常类需要继承tp5的异常基类
class BaseException extends Exception
{
public $httpStatusCode = 401;
public $msg = 'parameter error';
public $code = 10000;
public function __construct(array $params = [])
{
if (array_key_exists('httpStatusCode', $params)) {
$this->httpStatusCode = $params['httpStatusCode'];
}
if (array_key_exists('msg', $params)) {
$this->msg = $params['msg'];
}
if (array_key_exists('code', $params)) {
$this->code = $params['code'];
}
}
}
创建具体异常类
参数错误异常类
namespace app\lib\exception;
class ParameterException extends BaseException
{
public $httpStatusCode = 200;
public $msg = 'parameter error';
public $code = 10000;
}
用户不存在
class UserNotExistsException extends BaseException
{
public $httpStatusCode = 200;
public $msg = 'user is not exists';
public $code = 20000;
}
如何使用
我们先来看看登录这个功能
传统处理方法
public function login($phone, $password)
{
$uid = $this->getUidByPhone($phone);
if (!$uid) {
# 进行处理
}
}
通过异常处理
public function login($phone, $password)
{
$uid = $this->getUidByPhone($phone);
if (!$uid) {
# 抛出异常,返回给前端
throw new UserNotExistsException();
}
}