引出
如果你調一個類,調用時數據驗證時報了個錯,你會以什么方式返回
數組,布爾值?
數組這個可以帶錯誤原因回來,那布爾值呢?
返回了個 false, 報錯時把錯誤放在類變量里?
還是專門用一個獲取錯誤的方法進行獲取?
上面說的情況是代碼完全沒有問題的情況。
那如果是一些第三方的工具包,你又怎么知道他里面的執行會不會導致整個系統崩潰。
你說本地運行是沒問題的,環境這種東西不好說。
所以我們就用到了 異常 這個東西
下面是我們需要了解的問題
什么時候拋異常?怎么接異常?異常要怎么處理?他的使用場景又是什么?
基礎知識
- 基礎操作
try ... catch()
throw - 錯誤級別
致命錯誤 E_ERROR,
語法錯誤 E_PARSE,
警告錯誤 E_WARNING,
通知錯誤 E_NOTICE - php異常處理類
預定義異常
* ErrorException (extends Exception)
SPL異常類
* LogicException (extends Exception) // 表示程序邏輯中的錯誤的異常。這種異常應該直接在代碼中的修復* BadFunctionCallException // 回調調用未定義的函數或缺少一些參數時會拋出該異常* BadMethodCallException // 回調方法是一個未定義的方法或缺失一些參數時會拋出該異常* DomainException // 值不遵守定義的有效數據域時會拋出該異常* InvalidArgumentException // 參數不是預期類型時會拋出該異常* LengthException // 長度無效時會拋出該異常* OutOfRangeException // 請求非法索引時引發的異常,這應該在編譯時就檢測到的錯誤* RuntimeException (extends Exception) // 在運行時發生的錯誤會拋出該異常* OutOfBoundsException // 值不是有效鍵時會拋出該異常,這表示在編譯時無法檢測到的錯誤* OverflowException // 在向完整容器中添加元素時引發的異常* RangeException // 在程序執行期間為指示范圍錯誤而引發的異常。通常這意味著除了/overflow以外還有一個算術錯誤。這是運行時的DomainException版本* UnderflowException // 在空容器上執行無效操作(如刪除元素)時引發的異常* UnexpectedValueException // 值與一組值不匹配時會拋出該異常。通常,當一個函數調用另一個函數并期望返回值為某種類型或值(不包括算術或緩沖區相關錯誤)時,就會發生這種情況
- 異常處理相關函數
error_reporting // 設置報告的錯誤級別register_shutdown_function // 注冊一個會在php中止時執行的函數set_error_handler // 設置用戶自定義的錯誤處理函數set_exception_handler // 設置用戶自定義的異常處理函數error_get_last // 獲取最后發生的錯誤
使用場景
- 系統
主要抓的是無法預測的錯誤,統一返回,沒有使用 try...catch 接收的異常直接跳進設置的方法中
<?phpnamespace 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'));
- 工具
定義自定義的異常,一有錯誤直接拋出。使用工具的程序只需通過 Exception 接收異常即可, 所有異常都通過這個進行處理的
<?phpnamespace 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("連接失敗");}}
}?>
文章例子
- 工具包例子
- 項目例子
FQA
1、為什么要定自定義異常類, 系統不是已經給了很多選擇,而且很多 composer 包里都只是繼承一下。
答:其實自定義異常是為了用區分異常顆粒度的,比如
我定了 訂單異常,商品異常,用戶異常 類,但是 訂單里的異常多種多樣,比如訂單支付異常,訂單生成異常。
* RuntimeException (extends Exception)* Order* Paymen* Created* Goods* User* Withdraw
當項目拋出異常時
<?phptry {$param = []; // 操作那個方法時傳的參數throw App\Exception\Order\Payment::forParam('執行xxx操作異常', $param);} catch (Exception $e) {// 相關操作get_class($e); // 當前異常類 App\Exception\Order\Payment}
通過異常類名,我們可以知道是訂單支付異常。這里可以代替錯誤號,而且更清晰明了
2、為什么有一些 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:組件級別的異常
<?phpnamespace 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) {// 這里取得的異常只會是繼承這個接口的異常// 可以只針對這個工具包進行處理
}