“相信大家都有過這樣的體驗,無論在開發階段有多麼嚴謹,都避免不了代碼在上線運行過程中發生不可控的錯誤或異常,導致頁面顯示達不到預期效果,所以説合理的處理此類情況不但能提高系統的健壯性還有利於我們快速的定位問題”
什麼是錯誤
-
PHP程序自身的問題,一般是由非法的語法,環境問題導致成為錯誤
PHP錯誤配置
-
全局的配置修改在php.ini文件中,修改後重啓php服務生效
error_reporting = E_ALL //顯示所有錯誤 display_errors = Off //關閉錯誤提示 log_errors = On //錯誤日誌開啓 log_errors_max_len = 1024//設置日誌最大長度 error_log = /error.log //錯誤日誌文件位置 -
error_reporting
設置錯誤報告的級別,該參數可以是一個任意的表示二進制位字段的整數,或者常數名稱。錯誤級別和常數是在 預定義常量定義的,在程序運行時,還可以通過 error_reporting() 函數進行設置
-
常見級別説明
- Fatal Error:致命錯誤(終止運行)
- Parse Error:編譯時解析錯誤,語法錯誤(終止運行)
- Warning Error:警告錯誤(有出提示信息,不終止運行)
- Notice Error:通知錯誤(有出通知信息,不終止運行)
<?php
test();
//Fatal error: Uncaught Error: Call to undefined function test()
echo 1
//Parse error: syntax error, unexpected end of file, expecting ';' or ','
echo 1/0;
//Warning: Division by zero in
echo $a +1;
//Notice: Undefined variable: a
//常犯的錯誤,雖然最終結果不影響,但會產生警告和錯誤日誌⚠️
$b = 0;
if($b){
$a = [];
}
$a = array_values($a);
if($a){
//todo....
}
//Notice: Undefined variable: a
//Warning: array_values() expects parameter 1 to be array, null
附:官方詳細級別説明
錯誤的處理
-
眾所周知,錯誤不能被try catch異常類所捕獲,不要與異常混淆
<?php try { echo $a + 1; } catch (Exception $exception) { var_dump($exception); } //Notice: Undefined variable:a -
兜底處理
- set_error_handler 附:官方文檔戳我
本函數可以用你自己定義的方式來處理運行中的錯誤, 例如,在應用程序中嚴重錯誤發生時,或者在特定條件下觸發了一個錯誤(使用 trigger_error()),你需要對數據/文件做清理回收。
set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext = []) {
$aDataParam = compact('errno', 'errstr', 'errfile', 'errline');
//TODO 自定義處理:Log記錄、郵件提醒、拋出異常記錄
echo "<pre>";
print_r($aDataParam);exit;
return false;
}, E_ALL);
test();
//Fatal error: Uncaught Error: Call to undefined function test()
echo $a + 1;
/**
Array
(
[errno] => 8
[errstr] => Undefined variable: a
[errfile] => /opt/homebrew/var/www/php/error/index.php
[errline] => 126
)
*/
-
極端兜底處理register_shutdown_function 附:官方文檔戳我
set_error_handler已經捕獲到常遇到的大部分錯誤,未捕獲到的輸入比較嚴重的錯誤了(其實這部分要是能被提到生產環境的話,要好好反省自己了🐶)
register_shutdown_function(function () { $error = error_get_last(); if($error){ //記錄log,郵件提醒 } }); test();注:PHP腳本執行中可以單獨設置,優先級大於php.ini的配置
ini_set('display_errors', 1); // 配置文件設置了錯誤不可見, 使用ini_set命令設置可見 error_reporting(-1); // 設置php錯誤級別 -1 為顯示所有級別的錯誤錯誤使用的一些準則(非原創)
- 一定要讓 PHP 報告錯誤
- 在開發環境中要顯示錯誤
- 再生產環境中不能顯示錯誤
-
在開發和生產環境中都要記錄錯誤
什麼是異常
-
一般是指出現了的不符合預期的情況,但它不是由PHP語法導致的錯誤;比如:參數不合法的拋出,Curl請求,請求第三方API等
具體的使用
try {
if ('他錯誤') {
throw new Exception('他找到錯了', 0);
}
//throw new Exception('他找到錯了', 0);
if ('我錯了') {
throw new Exception('我找到錯了', 1);
}
//throw new Exception('他找到錯了', 0);
$result = '成功';
} catch (Exception $exception) {
$result = "錯誤描述:{$exception->getMessage()},錯誤代碼:{$exception->getCode()}" . PHP_EOL;
} finally {
Log::info('邏輯處理', ['result' => $result]);
}
-
剖析一下Exception
class Exception implements Throwable { protected $message; // 自定義異常提示 private $string; // __toString 信息 protected $code; // 自定義異常代碼 protected $file; // 發生異常的文件名 protected $line; // 發生異常的代碼行號 private $trace; // 數組形式的堆棧跟蹤 private $previous; // 之前拋出的異常 public function __construct($message = null, $code = 0, Exception $previous = null);//異常構造函數 final private function __clone() // 不能被複制,如果clone異常類將直接產生致命錯誤 final public function getMessage(): string // 返回異常信息 final public function getCode(): int // 異常克隆 final public function getFile(): string // 返回發生異常的文件名 final public function getLine():int // 返回發生異常的代碼行號 final public function getTrace(): array // 獲取異常追蹤信息 final public function getPrevious(); // 之前的 exception final public function getTraceAsString():string // 獲取字符串類型的異常追蹤信息 public function __toString():string // 異常對象轉換為字符串 }
可以看到除了__construct和__toString能被我們重寫,其他的方法和部分屬性我們都不能做修改,簡單複習下final 關鍵字
-
final 關鍵字的作用如下:
使用 final 修飾的類,不能被繼承;
類中使用 final 修飾的成員方法,在子類中不能覆蓋(重寫)該方法。自定義異常
//IP限制類 class IpException extends Exception { public $msg; public $code; public $param; public function __construct($message = "", $code = 0, $param = []) { $this->msg = $message; $this->code = $code; $this->param = $param; parent::__construct($this->msg, $this->msg); $this->afterAction(); } public function __toString() { return "非法請求:"; } //自定義處理邏輯 public function afterAction() { if ($this->code >= 500) { Log::ip('重要的記錄IP',$this->param); } } } //驗證類異常 class RuleException extends Exception { public $msg; public $code; public $param; public function __construct($message = "", $code = 0) { $this->msg = $message; $this->code = $code; parent::__construct($this->msg, $this->msg); } public function __toString() { return "非法請求:"; } } //兜底異常,無對應try()catch捕獲時,會進入到自定義處理的方法 set_exception_handler('exception_handler'); function exception_handler($exception) { var_dump($exception); } try { if ('IP不對') { throw new IpException('ip被限制', 500, ['ip' => '0.0.0.0']); } if ('uid不對') { throw new RuleException('缺少uid'); } } catch (RuleException $ruleException) { echo $ruleException . $ruleException->getMessage(); } catch (IpException $ipException) { echo $ipException . $ipException->getMessage(); } -
set_exception_handler()
設置默認的異常處理程序,用於沒有用 try/catch 塊來捕獲的異常。 在 exception_handler 調用後異常會中止。
-
自定義異常作用(非原創)
- 開閉原則對接口編程,依賴於抽象而不依賴於具體(繼承Exception後的各sonException可以實現更多的按需原則)
- 迪米特法則(最少知道原則)一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模塊相對獨立。
php7小技巧
Throwable 配合下方的層級圖可更好的理解
附:官方解釋戳我
//php5
try
{
//業務出錯
}catch (Exception $exception)
{
//捕獲異常出錯
}
catch (Error $error)
{
//捕獲基本錯誤
}
//>php7
try
{
//業務出錯
}
catch (Throwable $t)
{
//捕獲異常出錯,捕獲基本錯誤
}
總結
通過上邊的介紹相信大家對php的錯誤和異常有了更深一步的瞭解,平常開發中總是使用它們,而不能發揮出它們的最佳效果,這也是一件比較悲傷的事情;so,趕快行動起來吧!
參考文檔:
laravel異常分析文檔
PHP 最佳實踐之異常和錯誤
PHP錯誤和異常詳解(PHP7錯誤處理)
PHP 錯誤與異常