PHP 中的 Exception, Error, Throwable
- PHP 中將代碼自身例外(一般是環境或者語法非法所致)稱作錯誤
Error,將運行中出現的邏輯錯誤稱為例外Exception - 錯誤是沒法通過代碼處理的,而例外則可以通過
try/catch來處理 - PHP7 中出現了
Throwable介面,該介面由Error和Exception實作,用戶不能直接實作Throwable介面,而只能通過繼承Exception來實作介面
PHP7 例外處理機制
過去的 PHP,處理致命錯誤幾乎是不可能的,致命錯誤不會呼叫由 set_error_handler() 設定的處理方式,而是簡單的停止腳本的執行,
在 PHP7 中,當致命錯誤和可捕獲的錯誤(E_ERROR 和 E_RECOVERABLE_ERROR)發生時會拋出例外,而不是直接停止腳本的運行,對于某些情況,比如記憶體溢位,致命錯誤則仍然像之前一樣直接停止腳本執行,在 PHP7 中,一個未捕獲的例外也會是一個致命錯誤,這意味著在 PHP5.x 中致命錯誤拋出的例外未捕獲,在 PHP7 中也是致命錯誤,
注意:其他級別的錯誤如warning和notice,和之前一樣不會拋出例外,只有fatal和recoverable級別的錯誤會拋出例外,
從 fatal 和 recoverable 級別錯誤拋出的例外并非繼承自 Exception 類,這種分離是為了防止現有 PHP5.x 的用于停止腳本運行的代碼也捕獲到錯誤拋出的例外,fatal 和 recoverable 級別的錯誤拋出的例外是一個全新分離出來的類 Error 類的實體,跟其他例外一樣,Error 類例外也能被捕獲和處理,同樣允許在 finally 之類的塊結構中運行,
Throwable
為了統一兩個例外分支,Exception 和 Error 都實作了一個全新的介面:Throwable
PHP7 中新的例外結構如下:
1 interface Throwable 2 |- Exception implements Throwable 3 |- ... 4 |- Error implements Throwable 5 |- TypeError extends Error 6 |- ParseError extends Error 7 |- ArithmeticError extends Error 8 |- DivisionByZeroError extends ArithmeticError 9 |- AssertionError extends Error
如果在 PHP7 的代碼中定義了 Throwable 類,它將會是如下這樣:
1 interface Throwable{ 2 public function getMessage(): string; 3 public function getCode(): int; 4 public function getFile(): string; 5 public function getLine(): int; 6 public function getTrace(): array; 7 public function getTraceAsString(): string; 8 public function getPrevious(): Throwable; 9 public function __toString(): string; 10 }
這個介面看起來很熟悉,Throwable 規定的方法跟 Exception 幾乎是一樣的,唯一不同的是 Throwable::getPrevious() 回傳的是 Throwable 的實體而不是 Exception 的,Exception 和 Error 的建構式跟之前 Exception 一樣,可以接受任何 Throwable 的實體,
Throwable 可以用于 try/catch塊中捕獲 Exception 和 Error 物件(或是任何未來可能的例外型別),記住捕獲更多特定型別的例外并且對之做相應的處理是更好的實踐,然而在某種情況下我們想捕獲任何型別的例外(比如日志或框架中錯誤處理),在 PHP7 中,要捕獲所有的應該使用 Throwable 而不是 Exception,
1 try { 2 // Code that may throw an Exception or Error. 3 } catch (Throwable $t) { 4 // Handle exception 5 }
用戶定義的類不能實作 Throwable 介面,做出這個決定一定程度上是為了預測性和一致性——只有 Exception 和 Error 的物件可以被拋出,此外,例外需要攜帶物件在追溯堆疊中創建位置的資訊,而用戶定義的物件不會自動的有引數來存盤這些資訊,
Throwable 可以被繼承從而創建特定的包介面或者添加額外的方法,一個繼承自 Throwable 的介面只能被 Exception 或 Error 的子類來實作,
1 interface MyPackageThrowable extends Throwable {} 2 3 class MyPackageException extends Exception implements MyPackageThrowable {} 4 5 throw new MyPackageException();
Error
事實上,PHP5.x 中所有的錯誤都是 fatal 或 recoverable 級別的錯誤,在 PHP7 中都能拋出一個 Error實體,跟其他任何例外一樣,Error 物件可以使用 try/catch 塊來捕獲,
1 $var = 1; 2 try { 3 $var->method(); // Throws an Error object in PHP 7. 4 } catch (Error $e) { 5 // Handle error 6 }
通常情況下,之前的致命錯誤都會拋出一個基本的 Error 類實體,但某些錯誤會拋出一個更具體的 Error 子類:TypeError、ParseError 以及 AssertionError,
TypeError
當函式引數或回傳值不符合宣告的型別時,TypeError 的實體會被拋出,
1 function add(int $left, int $right){ 2 return $left + $right; 3 } 4 5 try { 6 $value = add('left', 'right'); 7 } catch (TypeError $e) { 8 echo $e->getMessage(), "\n"; 9 } 10 11 //Argument 1 passed to add() must be of the type integer, string given
ParseError
當 include/require 檔案或 eval() 代碼存在語法錯誤時,ParseError 會被拋出,
1 try { 2 require 'file-with-parse-error.php'; 3 } catch (ParseError $e) { 4 echo $e->getMessage(), "\n"; 5 }
ArithmeticError
ArithmeticError 在兩種情況下會被拋出,一是位移操作負數位,二是呼叫intdiv() 時分子是 PHP_INT_MIN 且分母是 -1 (這個使用除法運算子的運算式:PHP_INT_MIN / -1,結果是浮點型),
1 try { 2 $value = 1 << -1; 3 catch (ArithmeticError $e) { 4 echo $e->getMessage();//Bit shift by negative number 5 }
DevisionByZeroError
當 intdiv() 的分母是 0 或者取模操作 (%) 中分母是 0 時,DivisionByZeroError 會被拋出,注意在除法運算子 (/) 中使用 0 作除數(也即xxx/0這樣寫)時只會觸發一個 warning,這時候若分子非零結果是 INF,若分子是 0 結果是 NaN,
1 try { 2 $value = 1 % 0; 3 } catch (DivisionByZeroError $e) { 4 echo $e->getMessage();//Modulo by zero 5 }
AssertionError
當 assert() 的條件不滿足時,AssertionError 會被拋出,
ini_set('zend.assertions', 1);
1 ini_set('assert.exception', 1); 2 3 $test = 1; 4 5 assert($test === 0); 6 7 //Fatal error: Uncaught AssertionError: assert($test === 0)
只有斷言啟用并且是設定 ini 配置的 zend.assertions = 1 和 assert.exception = 1 時,assert()才會執行并拋 AssertionError,
在你的代碼中使用 Error
用戶可以通過繼承 Error 來創建符合自己層級要求的 Error 類,這就形成了一個問題:什么情況下應該拋出 Exception,什么情況下應該拋出 Error,
Error 應該用來表示需要程式員關注的代碼問題,從 PHP 引擎拋出的 Error 物件屬于這些分類,通常都是代碼級別的錯誤,比如傳遞了錯誤型別的引數給一個函式或者決議一個檔案發生錯誤,Exception 則應該用于在運行時能安全的處理,并且另一個動作能繼續執行的情況,
由于 Error 物件不應該在運行時被處理,因此捕獲 Error 物件也應該是不頻繁的,一般來說,Error 物件僅被捕獲用于日志記錄、執行必要的清理以及展示錯誤資訊給用戶,
撰寫代碼支持 PHP5.x 和 PHP7 的例外
為了在同樣的代碼中捕獲任何 PHP5.x 和 PHP7 的例外,可以使用多個 catch,先捕獲 Throwable,然后是 Exception,當 PHP5.x 不再需要支持時,捕獲 Exception 的 catch 塊可以移除,
1 try { 2 // Code that may throw an Exception or Error. 3 } catch (Throwable $t) { 4 // Executed only in PHP 7, will not match in PHP 5.x 5 } catch (Exception $e) { 6 // Executed only in PHP 5.x, will not be reached in PHP 7 7 }
不幸的是,處理例外的函式中的型別宣告不容易確定,當 Exception 用于函式引數型別宣告時,如果函式呼叫時候能用 Error 的實體,這個型別宣告就要去掉,當 PHP5.x 不需要被支持時,型別宣告則可以還原為 Throwable,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/114431.html
標籤:PHP
上一篇:微信小程式獲取用戶手機號
下一篇:LNMP+Redis架構部署
