你知道PHP中Exception, Error Handler的這些細
前言
最近專案中有一個功能需要實現:
除錯模式下, 將所有錯誤提前輸出, 再輸出頁面內容.
為實現上述功能, 需使用到Exception
, Error
相關Handler
方法, 發現有許多坑, 故寫此文與大家分享.
主要函式
此篇文章重點關注以下幾個函式
error_reporting()
set_error_handler()
set_exception_handler()
register_shutdown_function()
error_get_last()
這有什麼難的?
哈~ 如果您現在有標題中的感慨, 那麼也請關注以下本文中將重點講述的問題列表:
error_reporting()
與error_get_last()
有什麼聯絡?set_error_handler()
與set_exception_handler()
繫結的handler
什麼時候才會啟動? 它們有什麼聯絡?register_shutdown_function()
通常跟Exception/Error
有關係麼?
上述問題描述模糊, 因此答案也可能千人千面.
因此, 本文只給出自己的答案與大家分享, 如有問題或不同的見解, 期待與您溝通.
如果以上問題, 並不能引起您的興趣, 或者您已理解透徹了, 就可以自行右上角小紅叉啦~
解疑:
1. error_reporting()
與 error_get_last()
有什麼聯絡?
int error_reporting ([ int $level ] )
大家應該再熟悉不過了, 因此不再贅述.-
array error_get_last ( void )
獲取最後發生的錯誤.
通常用來獲取PHP執行過程中的
Fatal Error
錯誤(PHP 5
).
這兩個函式在字面上關聯性並不強, 但請觀察以下程式碼及輸出
8 [message] => Undefined variable: b [file] => /app/t.php [line] => 3 ) */?phperror_reporting>
error_get_last()
雖然說明了獲取最後發生的錯誤, 實際上也是如此. 但卻沒有說明, 被error_reporting()
忽略掉的錯誤是否有可能被獲取到, 因此, 當我們使用error_get_last()
時需要注意我平時忽略掉的錯誤, 如: E_DEPRECATED
2. set_error_handler()
與 set_exception_handler()
繫結的handler
什麼時候才會啟動? 它們有什麼聯絡?
-
mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )
設定使用者自定義的錯誤處理函式.
通常在PHP指令碼執行過程中, 出現一些非中斷性錯誤時觸發.
我們會用這個來記錄錯誤日誌或直接輸出等操作.
注意:FALSE
: 標準的錯誤處理依然會被執行(標準錯誤處理根據 display_errors = true/false 決定是否輸出到stderr
)
引數
$error_types
大多設定為error_reporting()
, 但建議設定為E_ALL
, 具體哪些錯誤需要被處理, 哪些不需要, 在handler
內進行判斷明顯更加靈活.以下級別的錯誤不能由使用者定義的函式來處理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 呼叫 set_error_handler() 函式所在檔案中產生的大多數 E_STRICT
handler
被觸發後, 並不會中斷PHP執行.bool error_handler ( int $errno , string $errstr [, string $errfile [, int $errline [, array $errcontext ]]] )
注意error_handler
的返回值:
callable set_exception_handler ( callable $exception_handler )
設定使用者自定義的異常處理函式
設定預設的異常處理程式,用於沒有用 try/catch 塊來捕獲的異常。 在 exception_handler 呼叫後異常會中止。
注意:
注意點中2, 3項輕描淡寫了一下PHP 5/PHP 7
之間的不同卻透露出重要的訊息(坑..)PHP 7
中, exception_handler
不再只接受Exception
了, 並且接收了Error
錯誤.
exception_handler
呼叫後異常會中止(指令碼終止).PHP 5
,PHP 7
的exception_handler
並不相同.PHP 5
:void handler ( Exception $ex )
PHP 7
:void handler ( Throwable $ex )
自 PHP 7 以來,大多數錯誤丟擲 Error 異常,也能被捕獲。 Error 和 Exception 都實現了 Throwable 介面。
因此, set_error_handler()
與 set_exception_handler()
之間的關係也迎刃而解:
-
PHP 5
:set_error_handler()
: 負責非中斷行錯誤.set_exception_handler()
: 負責沒有被catch的異常(會中斷).Fatal Error
等: 並不會被兩者管理, 正常輸出到螢幕上(弊端).
-
PHP 7
:set_error_handler()
: 負責非中斷行錯誤.set_exception_handler()
: 負責沒有被catch的異常,Error
(會中斷)Fatal Error
等: 由set_exception_handler()
管理.
3. register_shutdown_function()
通常跟Exception/Error有關係麼?
註冊一個 callback ,它會在指令碼執行完成或者 exit() 後被呼叫。
根據說明可以得出結論, 它與Exception/Error
完全沒關係.
提出這個問題, 主要是因為, 在PHP5
中Fatal Error
並沒有明確的接收地點, 所以我們通常配合error_get_last()
來接收Fatal Error
output: Array ( [type] => 1 [message] => Uncaught Error: Call to undefined function unknown_function() in /app/t.php:3 Stack trace:#0 {main} thrown [file] => /app/t.php [line] => 3 ) */
然而隨著PHP 7
的到來, Error
已經可以被set_exception_handler()
捕捉了, 再透過error_get_last()
就多餘了. shutdown
中更多的是一些版本冗餘的工作.
栗子
前言中的需求: 除錯模式下, 將所有錯誤提前輸出, 再輸出頁面內容.
以下是demo, 省去了環境判斷(debug環境), 大家可以根據下面這段程式碼, 瞭解本文中所說的各種handler
的觸發和呼叫情況.
要求: 將所有異常列印在螢幕最上方 *//* Fatal Error 中斷指令碼 -> shutdown_handler *///設定錯誤級別define("END_ERRORS", '--END ERRORS--' . PHP_EOL . PHP_EOL); ini_set('display_errors', true); ini_set('error_reporting', E_ALL & ~E_DEPRECATED); set_error_handler('usr_err_handler', error_reporting()); //註冊錯誤處理函式set_exception_handler('usr_ex_handler'); //註冊異常處理函式register_shutdown_function('shutdown_handler'); //註冊會在php中止時執行的函式$global_errors = []; //用於記錄所有錯誤$errnos = [ //錯誤級別 0 => 'ERROR',//PHP7 ERROR的CODE 1 => 'E_ERROR',//FATAL ERROR(PHP5), E_ERROR 2 => 'E_WARNING', 4 => 'E_PARSE', 8 => 'E_NOTICE', 16 => 'E_CORE_ERROR', 32 => 'E_CORE_WARNING', 64 => 'E_COMPILE_ERROR', 128 => 'E_COMPILE_WARNING', 256 => 'E_USER_ERROR', 512 => 'E_USER_WARNING', 1024 => 'E_USER_NOTICE', 2048 => 'E_STRICT', 4096 => 'E_RECOVERABLE_ERROR', 8192 => 'E_DEPRECATED', 16384 => 'E_USER_DEPRECATED', 30719 => 'E_ALL', ];function reset_errors(){ global $global_errors; $global_errors = []; }function get_errnostr($errno){ global $errnos; return $errnos[$errno]; }function set_errnos($errno, $errstr){ global $global_errors; $global_errors[] = [ 'errno' => $errno, 'errnostr' => get_errnostr($errno), 'errstr' => $errstr, ]; }function print_errors($prefix){ global $global_errors; foreach ($global_errors as $err) {//由於handler中依然有可能有error 因此放最後 printf("[%s]: %s, %d, %sn", $prefix, $err['errnostr'], $err['errno'], $err['errstr']); } }//使用者異常處理函式 (進來就中斷指令碼) PHP5只有Exception進來 PHP7Error和Exception//PHP7中 void handler (Throwable $ex) 可捕獲Error和Exception兩種異常, 暫不管// PHP7 Error閱讀//內部如果有Error則觸發Error函式, 再回到錯誤行繼續執行function usr_ex_handler($ex){ $content = ob_get_clean(); //讓Exception/Error提前展示 print_errors('EX ERROR'); reset_errors(); $errnostr = get_errnostr($ex->getCode()); $errno = $ex->getCode(); $errstr = $ex->getMessage(); if ($ex instanceof Exception) { printf("[EXCEPTION]: %s, %d, %sn", $errnostr, $errno, $errstr); } else {//針對PHP7 $ex instanceof Error printf("[EX FATAL ERROR]: %s, %d, %sn", $errnostr, $errno, $errstr); } //由於handler中依然有可能有error 因此放最後 print_errors('EX ERROR'); reset_errors(); echo END_ERRORS; echo $content; return; }//使用者錯誤處理函式//E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING不能被使用者處理function usr_err_handler($errno, $errstr, $errfile, $errline, $errcontext){ set_errnos($errno, $errstr); return true; //如果函式返回 FALSE,標準錯誤處理處理程式將會繼續呼叫。}//使用者PHP終止函式function shutdown_handler(){ $content = ob_get_clean(); //讓Exception/Error提前展示 $err = error_get_last();//檢查一下是否有遺漏掉的錯誤 php5 fatal error if ($err['type'] & error_reporting()) { set_errnos($err['type'], $err['message']); } print_errors('ST ERROR'); reset_errors(); echo $content; } ob_start();echo 'Main function...', PHP_EOL;//搞事情//throw new Exception('這是一個異常');trigger_error('這是一個使用者error');//E_USER_NOTICEif (version_compare(PHP_VERSION, '7.0.0') >= 0) { mcrypt_encrypt();//E_WARNING, E_DEPRECATED} else { mysql(); } unknown_function(); //fatal error$content = ob_get_clean();//優先輸出錯誤print_errors('MA ERROR');if (!empty($global_errors)) { echo END_ERRORS; } reset_errors();//輸出正文內容echo $content;
作者:鋼管君
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2983/viewspace-2803835/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 面試官帶你學Android——面試中Handler 這些必備知識點你都知道嗎?面試Android
- Netty中的這些知識點,你需要知道!Netty
- Chrome DevTools中的這些騷操作,你都知道嗎?Chromedev
- 這10條PHP7中的禁忌,你知道嗎?PHP
- Python——你應該知道這些Python
- 你知道黑客的入侵方式都有哪些嗎?這些你知道幾個?黑客
- 這些Python騷操作,你知道嗎?Python
- Web直播,你需要先知道這些Web
- 學node 之前你要知道這些
- 浮點數的這些坑,你未必知道
- 第四篇:Hyperion安裝配置,這些細節你都知道嗎
- Exception和ErrorExceptionError
- Exception和Error的理解ExceptionError
- ??你以為你瞭解TCP協議?這些你可能不知道的細節才是關鍵!TCP協議
- 這些關於WebSocket的知識,你知道多少?Web
- 天天寫 SQL,這些神奇的特性你知道嗎?SQL
- 軟體測試這些你知道嗎?
- 這些免費OA陷阱你知道嗎?
- 關於雲原生,這些你要知道
- 用Java這麼多年,這些祕密你知道嗎?Java
- 不知道這些,你以為你還能devops?dev
- 關於MySQL引數,這些你要知道MySql
- 這些CSS提效技巧,你需要知道!CSS
- 關於灰度釋出,你得知道這些......
- 入門Python,這些優點你要知道!Python
- 收藏!這些 IDE 使用技巧,你都知道嗎IDE
- 入手《死亡擱淺》,這些問題你需要知道的
- 關於JVM,你必須知道的這些知識點JVM
- Linux常見面試題,這些你知道多少?Linux面試題
- 關於Linux系統,這些你都知道嗎?Linux
- 純鹼,這些乾貨你應該知道QLX
- 你居然不知道postman還有這些用法?Postman
- 【Java面試指北】Exception Error Throwable 你分得清麼?Java面試ExceptionError
- 這些著名資料庫之間的“關係”,你知道嗎?資料庫
- 這些技巧你知道嗎?macOS的Fn鍵實用秘訣Mac
- 建立合格的跨職能團隊,你需要知道這些!
- 超好用的macOS Monterey 隱藏功能,這些你都知道嗎Mac
- 你說你懂計算機網路,那這些你都知道嗎計算機網路