php異常處理的深刻

引出

若是你調一個類,調用時數據驗證時報了個錯,你會以什麼方式返回php

數組,布爾值?git

數組這個能夠帶錯誤緣由回來,那布爾值呢?github

返回了個 false, 報錯時把錯誤放在類變量裏?
仍是專門用一個獲取錯誤的方法進行獲取?sql

上面說的狀況是代碼徹底沒有問題的狀況。
那若是是一些第三方的工具包,你又怎麼知道他裏面的執行會不會致使整個系統崩潰。數組

你說本地運行是沒問題的,環境這種東西很差說。composer

因此咱們就用到了 異常 這個東西函數

下面是咱們須要瞭解的問題工具

何時拋異常?怎麼接異常?異常要怎麼處理?他的使用場景又是什麼?this

基礎知識

  1. 基礎操做

    try ... catch()
    throwspa

  2. 錯誤級別

    致命錯誤 E_ERROR,
    語法錯誤 E_PARSE,
    警告錯誤 E_WARNING,
    通知錯誤 E_NOTICE

  3. php異常處理類

預約義異常

* ErrorException (extends Exception)

SPL異常類

* LogicException (extends Exception)      // 表示程序邏輯中的錯誤的異常。這種異常應該直接在代碼中的修復
        * BadFunctionCallException            // 回調調用未定義的函數或缺乏一些參數時會拋出該異常
            * BadMethodCallException          // 回調方法是一個未定義的方法或缺失一些參數時會拋出該異常
        * DomainException                     // 值不遵照定義的有效數據域時會拋出該異常
        * InvalidArgumentException            // 參數不是預期類型時會拋出該異常
        * LengthException                     // 長度無效時會拋出該異常
        * OutOfRangeException                 // 請求非法索引時引起的異常,這應該在編譯時就檢測到的錯誤

    * RuntimeException (extends Exception)    // 在運行時發生的錯誤會拋出該異常
        * OutOfBoundsException                // 值不是有效鍵時會拋出該異常,這表示在編譯時沒法檢測到的錯誤
        * OverflowException                   // 在向完整容器中添加元素時引起的異常
        * RangeException                      // 在程序執行期間爲指示範圍錯誤而引起的異常。一般這意味着除了/overflow之外還有一個算術錯誤。這是運行時的DomainException版本
        * UnderflowException                  // 在空容器上執行無效操做(如刪除元素)時引起的異常
        * UnexpectedValueException            // 值與一組值不匹配時會拋出該異常。一般,當一個函數調用另外一個函數並指望返回值爲某種類型或值(不包括算術或緩衝區相關錯誤)時,就會發生這種狀況
  1. 異常處理相關函數
error_reporting // 設置報告的錯誤級別
    register_shutdown_function // 註冊一個會在php停止時執行的函數
    set_error_handler // 設置用戶自定義的錯誤處理函數
    set_exception_handler // 設置用戶自定義的異常處理函數
    error_get_last // 獲取最後發生的錯誤

使用場景

  1. 系統

    主要抓的是沒法預測的錯誤,統一返回,沒有使用 try...catch 接收的異常直接跳進設置的方法中

<?php

namespace App\Exception;

use Exception;

/**
 * 異常句柄(入口)類
 */
class Handler
{
    // 默認錯誤處理
    public static function errorHandler($errno, $errstr, $errfile = '', $errline = 0)
    {
    }

    // 默認異常處理
    public static function exceptionHandler($ex)
    {
        try {
            throw $ex;
        } catch (Order $e) {
            echo "訂單異常";
        } catch (Goods $e) {
            echo "商品異常";
        } catch (User $e) {
            echo "用戶異常";
        } catch (Exception $e) {
            echo "其餘異常";
        }
    }

    // 致命錯誤處理
    public static function fatalErrorHandler()
    {
        if ($e = error_get_last()) {
            print_r($e);
        }
    }
}

/**
 * 訂單異常
 */
class Order extends Exception
{
}

/**
 * 商品異常
 */
class Goods extends Exception
{
}

/**
 * 用戶異常
 */
class User extends Exception
{
}
<?php
// 入口文件中

error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);

register_shutdown_function(array('App\\Exception\\Handler', 'fatalErrorHandler'));

set_error_handler(array('App\\Exception\\Handler', 'errorHandler'));

set_exception_handler(array('App\\Exception\\Handler', 'exceptionHandler'));
  1. 工具

    定義自定義的異常,一有錯誤直接拋出。使用工具的程序只需經過 Exception 接收異常便可, 全部異常都經過這個進行處理的

<?php

namespace Testlin\Db\Exception;

use Exception;

interface ExceptionInterface
{
}

class Db extends Exception implements ExceptionInterface
{
}

class Pdo extends Db
{
}
?>

<?php
namespace Testlin\Db;

use Exception;
use Testlin\Db\Exception\Pdo;

class Db
{
    protected $db;

    public function __construct($config)
    {
        $this->db = new PDO($config);

        if ($this->db == false) {
            throw new Pdo("鏈接失敗");
        }
    }
}

?>

文章例子

  1. 工具包例子
  2. 項目例子

FQA

一、爲何要定自定義異常類, 系統不是已經給了不少選擇,並且不少 composer 包裏都只是繼承一下。

答:其實自定義異常是爲了用區分異常顆粒度的,好比

我定了 訂單異常,商品異常,用戶異常 類,可是 訂單裏的異常多種多樣,好比訂單支付異常,訂單生成異常。

* RuntimeException (extends Exception)
    * Order
        * Paymen
        * Created
    * Goods
    * User
        * Withdraw

當項目拋出異常時

<?php
    try {
        $param = []; // 操做那個方法時傳的參數

        throw App\Exception\Order\Payment::forParam('執行xxx操做異常', $param);
    } catch (Exception $e) {
        // 相關操做
        get_class($e); // 當前異常類 App\Exception\Order\Payment
    }

經過異常類名,咱們能夠知道是訂單支付異常。這裏能夠代替錯誤號,並且更清晰明瞭

二、爲何有一些 composer 包裏的自定義異常,有的有不少方法。有什麼用處嗎?

做用1:格式化異常

好比:拋出的異常提示是 "id=xx 的用戶不存在",咱們會有如下兩種寫法

<?php

// 普通操做
$id = 1;
throw new Payment("id={$id} 的用戶不存在");

// 格式化異常
use App\Exception\Order;

class Payment extends Order
{
    public static function forId($id)
    {
        return new self(sprintf(
            'id=%s 的用戶不存在',
            $id
        ));
    }
}

$id = 1;
throw Payment::forId($id);

做用2:組件級別的異常

<?php

namespace Testlin\Db\Exception;

use Exception;

interface ExceptionInterface
{
}

class Mysqli extends Exception impements ExceptionInterface
{
}

class Pdo extends Exception impements ExceptionInterface
{
}


try {
    throw new Testlin\Db\Exception\Mysqli('sql 執行失敗');
} catch (Testlin\Db\Exception\ExceptionInterface $e) {
    // 這裏取得的異常只會是繼承這個接口的異常
    // 能夠只針對這個工具包進行處理
}
相關文章
相關標籤/搜索