如何優雅的設計PHP異常

前言

剛開始接觸PHP的時候沒有意識到異常的重要性,有時候出問題很難精確的找到問題點,正確的處理異常也是一門學問php

異常的類別

PHP7異常作了不少變更,異常類 Exception 和錯誤類 Error 都實現了 Throwable 接口前端

結構以下:json

  • Throwable後端

    • Errorrestful

      • ArithmeticErrorapp

        • DivisionByZeroError
      • AssertionError
      • ParseError
      • TypeError框架

        • ArgumentCountError
    • Exception前後端分離

      • ClosedGeneratorException
      • DOMException
      • ErrorException
      • IntlException
      • LogicExceptionui

        • BadFunctionCallExceptionthis

          • 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();
    }
}
相關文章
相關標籤/搜索