錯誤與異常
錯誤,可以理解程式本身的錯誤,例如語法錯誤。而異常則更偏向於程式執行不符合預期或者不符合正常流程;對於 PHP 語言而言,處理錯誤和處理異常使用的機制完全不同,因此很容易讓人產生困惑。
例如,我們希望通過捕獲異常來處理除數為 0 的情況,但是在捕獲到異常之前,PHP 就觸發了錯誤。
try {
$a = 5 / 0;
} catch (Exception $e) {
$e->getMessage();
$a = -1; // 通過異常來處理 $a 為 0 的情況,但是實際上,捕獲不到該異常
}
echo $a;
// PHP Warning: Division by zero
也就是說,PHP 將除數為 0 的情況當成了錯誤而觸發,而不會自動丟擲異常,因此沒法捕獲。類似的,在很多情況下,PHP 都沒辦法自動丟擲異常。只能通過 if - else
語句判斷再結合 throw
方法來並手動丟擲異常。
上述情況的發生,主要還是因為異常機制是 PHP 向物件導向演進後得到的產物。而在此之前 PHP 的報錯主要還是通過錯誤機制,因此,在很多情況下,PHP 的錯誤要比異常更有價值。不過 PHP7 開始統一這兩者,使錯誤也可以像異常那樣丟擲(這部分內容將放在異常部分講解)。
錯誤級別
PHP 中的錯誤可理解為 使指令碼不執行不正常的情況,根據錯誤級別從高到低可劃分為五類
- Parse error 或 Syntax Error - 語法解析錯誤,觸發該錯誤後,指令碼完全無法執行;
- Fatal Error - 致命錯誤,觸發該錯誤後,後面的指令碼無法繼續執行;
- Warning Error - 出現比較不恰當的地方,指令碼可繼續執行;
- Notice Error - 出現不恰當的地方,但是程度比 Warning Error 低,指令碼可繼續執行;
- Deprecated Error - 不推薦這麼使用,未來可能會廢棄,指令碼可繼續執行;
預設情況下,PHP 觸發錯誤,並顯示錯誤的級別及對應的提示。
Parse Error 示例 - 語句結尾不寫分號
echo "abc"
// PHP Parse error: syntax error, unexpected end of file, expecting ',' or ';
Fatal Error 示例 - 使用不存在的函式
echo "before\n";
foo();
echo "after"; // 本行無法繼續執行
// before
// PHP Fatal error: Uncaught Error: Call to undefined function foo()
Warning Error 示例 - 引入不存在的檔案
$a = "foo";
include('bar.php');
echo $a; // 程式繼續執行
// PHP Warning: include(bar.php): failed to open stream: No such file or directory ...
// foo
Notice Error 示例 - 輸出不存在的變數
echo $foo;
echo 12345;
// PHP Notice: Undefined variable: foo
// 12345
Deprecated Error 示例 - 在一些字串函式中傳入數字而非字串
strpos('12345', 3);
// PHP Deprecated: strpos(): Non-string needles will be interpreted as strings in the future
除了預設觸發訊息外,使用者也可以使用 set_error_handler
函式自定義錯誤處理,大多數錯誤型別都可以進行自定義處理,除了 E_ERROR
、 E_PARSE
、 E_CORE_ERROR
、 E_CORE_WARNING
、 E_COMPILE_ERROR
、 E_COMPILE_WARNING
外。
set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] ) : mixed
示例
<?php
// E_ALL - 處理全部錯誤型別
set_error_handler('customError', E_ALL);
/**
* @param int $errno 錯誤的級別
* @param string $errstr 錯誤的資訊
* @param string $errfile 錯誤的檔名(可選)
* @param string $errline 錯誤發生的行號(可選)
*/
function customError(int $errno, string $errstr, string $errfile, string $errline)
{
echo sprintf('錯誤訊息為 %s', $errstr);
}
$a = 5 / 0; // 錯誤訊息為 Division by zero
使用者也可以通過 trigger_error
函式來手動觸發一個使用者級別的錯誤(E_USER_ERROR
、E_USER_WARNING
、E_USER_NOTICE
、E_USER_DEPRECATED
)。
function division($a, $b) {
if($b == 0){
@trigger_error("0 不能作為除數", E_USER_NOTICE);
return -1;
}
return $a / $b;
}
echo division(10, 0);
與錯誤有關的配置
一些錯誤處理相關的常用配置
error_reporting
- 設定錯誤的報告級別display_errors
- 是否顯示錯誤display_startup_error
- 是否顯示 PHP 啟動過程中的顯示log_errors
- 設定是否將指令碼執行的錯誤資訊記錄到伺服器錯誤日誌或者 error_log 之中
《Modern PHP》提出了四個規則
- 一定要讓 PHP 報告錯誤;
- 在開發環境中要顯示錯誤;
- 在生產環境中不能顯示錯誤;
- 在開發環境和生產環境中都要記錄錯誤;
開發環境推薦配置
display_errors = On
display_startup_error = On
error_reporting = -1
log_errors = On
生產環境推薦配置
display_errors = Off
display_startup_error = Off
; 報告 Notice 以外的所有錯誤
error_reporting = E_ALL & ~E_NOTICE
log_errors = On
Symfony 編碼規範相關
異常和錯誤訊息字串必須使用 sprintf
來進行拼接;
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
當錯誤型別為 E_USER_DEPRECATED
時,需要新增 @
@trigger_error("foo", E_USER_DEPRECATED);
參考資料
- PHP Errors: 4 Different Types (Warning, Parse, Fatal, and Notice Error)
- PHP: 預定義常量 - Manual
- PHP 核心技術與最佳實踐 (豆瓣)
- PHP: 執行時配置 - Manual
- PHP 規範 - Symfony 程式碼規範 | PHP 技術論壇
- Modern PHP(中文版) (豆瓣)
本作品採用《CC 協議》,轉載必須註明作者和本文連結