PHP的異常處理機制
1、PHP中異常的獨特性
PHP中的異常的獨特性,即PHP中的異常不同於主流語言C++、java中的異常。在Java中,異常是唯一的錯誤報告方式,而在PHP中卻不是這樣,而是把所有不正常的情況都視作了錯誤進行處理。這兩種語言對異常和錯誤的界定存在分歧。什麽是異常什麽是錯誤,兩種語言的設計者存在不同的觀點。
PHP中的異常:
是程序在運行中出現不符合預期的情況及與正常流程不同的狀況。一種不正常的情況,按照正常邏輯本不該出的錯誤,但仍然會出現的錯誤,這是屬於邏輯和業務流程的錯誤,而不是編譯或者語法上的錯誤。
PHP中的錯誤:
是屬於php腳本自身的問題,大部分情況是由錯誤的語法,服務器環境導致,使得編譯器無法通過檢查,甚至無法運行的情況。warning、notice都是錯誤,只是他們的級別不同而已,並且錯誤是不能被try-catch捕獲的。
在PHP中遇到任何自身錯誤都會觸發一個錯誤,而不是拋出異常。PHP一旦遇到非正常代碼,通常都會觸發錯誤,而不是拋出異常。因此,如果想要使用異常處理不可預料的問題,是辦不到的。
典型例子:
<?php try { echo 1/0; } catch (Exception $e){ echo $e->getMessage(); }
輸出:
Warning: Division by zero in D:\web\mytest\test.php on line 4
INF
結果顯示: 此時出現了一個警告級別的錯誤,程序終止。 結論: PHP通常是無法自動捕獲有意義的異常,它把所有不正常的情況都視作了錯誤,你要想捕獲異常就得使用if....else結構,保證代碼是正常的,然後判斷進行手動拋出異常。
2、PHP中的錯誤級別
PHP中的異常機制是不足的,絕大多數情況下無法自動拋出異常,必須使用if....else語句先進行判斷,在進行手動拋出異常。 手動拋出異常的意義不大,是已經預料到的錯誤,這種方式將會使你陷入紛繁復雜的業務邏輯判斷和處理中。 因此我們可以通過一些特殊的函數來自定義錯誤處理函數,來接管PHP原生的錯誤處理函數,然後再進行拋出異常。 接下來我們需要了解PHP中的一些錯誤。 錯誤顯示控制: 【ALL設置】 全局:php.ini中設置display_error = on/off; 局部:ini_set("display_error", true/false); PHP.ini中display_errors = Off失效的解決 問題: PHP設置文件php.ini中明明已經設置display_errors = Off,但是在運行過程中,網頁上還是會出現錯誤信息。 解決: 經 查log_errors= On,據官方的說法,當這個log_errors設置為On,那麽必須指定error_log文件,如果沒指定或者指定的文件沒有權限寫入,那麽照樣會輸 出到正常的輸出渠道,那麽也就使得display_errors 這個指定的Off失效,錯誤信息還是打印了出來。於是將log_errors = Off,問題就解決了。 【選擇性設置顯示錯誤】 全局:error_reporting = E_ALL | E_STRICT.... 局部:error_reporting(E_ERROR | E_WARNING | E_PARSE)
E_ERROR 致命的運行錯誤。錯誤無法恢復,暫停執行腳本。 E_WARNING 運行時警告(非致命性錯誤)。非致命的運行錯誤,腳本執行不會停止。 E_PARSE 編譯時解析錯誤。解析錯誤只由分析器產生。 E_NOTICE 運行時提醒(這些經常是你代碼中的bug引起的,也可能是有意的行為造成的。) E_CORE_ERROR PHP 啟動時初始化過程中的致命錯誤。 E_CORE_WARNING PHP啟動時初始化過程中的警告(非致命性錯)。 E_COMPILE_ERROR 編譯時致命性錯。這就像由Zend腳本引擎生成了一個E_ERROR。 E_COMPILE_WARNING 編譯時警告(非致性錯)。這就像由Zend腳本引擎生成了E_WARNING警告。 E_USER_ERROR 自定義錯誤消息。像用PHP函數trigger_error(程序員設置E_ERROR) E_USER_WARNING 自定義警告消息。像用PHP函數trigger_error(程序員設的E_WARNING警告) E_USER_NOTICE 自定義的提醒消息。像由使用PHP函數trigger_error(程序員E_NOTICE集) E_STRICT 編碼標準化警告。允許PHP建議修改代碼以確保最佳的互操作性向前兼容性。 E_RECOVERABLE_ERROR 開捕致命錯誤。像E_ERROR,但可以通過用戶定義的處理捕獲(又見set_error_handler()) E_ALL 所有的錯誤和警告(不包括 E_STRICT) (E_STRICT will be part of E_ALL as of PHP 6.0)14 16384 E_USER_DEPRECATED 15 30719 E_ALL
一共有十五種,使用二進制代替,0000 0000 0000 0011 表示 E_ERROR和E_WARNING 例如: error_reporting(3); //只顯示E_ERROR和E_WARNING錯誤 error_reporting(-1); //只顯示所有錯誤誤 註意: 在開發階段通常是顯示所有錯誤,方便解決問題; 在生產階段通常是隱藏錯誤,並將需錯誤記錄到文件中(錯誤日誌); php.ini中設置:log_error = on/off; //記錄、不記錄 error_log = php_errors.log //設定錯誤日誌文件(此時沒有給定路徑則在當前位置生成) 還可以通過ini_set()進行設置。
3、PHP中的異常處理
3.1、set_error_handler(error_function, error_type)
使用set_error_handler(error_function, error_type)函數設置自定義錯誤處理函數,接管原錯誤處理函數。
比如:
<?php // 方式一 // set_error_handler(‘myError‘); // function myError($errorNum, $errorMs, $errorFile, $errorLine){ // echo(‘set_error_handler: ‘ . $errorNum . ‘:‘ . $errorMs . ‘ in ‘ . $errorFile . ‘ on ‘ . $errorLine . ‘ line ‘); // } // 方式二 class ErrorClass{ // 必須靜態public方法 public static function myError($errorNum, $errorMs, $errorFile, $errorLine){ echo(‘set_error_handler: ‘ . $errorNum . ‘:‘ . $errorMs . ‘ in ‘ . $errorFile . ‘ on ‘ . $errorLine . ‘ line ‘); } } set_error_handler([‘ErrorClass‘, ‘myError‘]); try { $a = 1/0; } catch (Exception $e) { echo "666"; }
輸出:set_error_handler: 2:Division by zero in D:\web\mytest\test.php on 21 line
由結果可知:我們自定義的myError方法截取了錯誤,此時我們可以主動的處理這些錯誤,拋出相應的異常。
但是我們需要註意以下兩點:
第一,如果存在該方法,相應的error_reporting()就不能在使用了。它將接管PHP原生錯誤處理函數,即所有的錯誤都會交給自定義的函數處理。
第二,此方法不能處理以下級別的錯誤:E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,set_error_handler() 函數所在文件中產生的E_STRICT,該函數只能捕獲系統產生的一些Warning、Notice級別的錯誤。
註意:
如果在腳本執行前發生錯誤,由於此時自定義的錯誤處理函數還沒有註冊,因此就用不到這個自定義錯誤處理程序。
3.2、register_shutdown_function(exception_function)
捕獲PHP的錯誤:Fatal Error、Parse Error等,這個方法是PHP腳本執行結束前最後一個調用的函數,比如腳本錯誤、die()、exit、異常、正常結束都會調用。 通過這個函數就可以在腳本結束前判斷這次執行是否有錯誤產生,這時就要借助於一個函數:error_get_last();這個函數可以拿到本次執行產生的所有錯誤。error_get_last();返回的信息: [type] - 錯誤類型 [message] - 錯誤消息 [file] - 發生錯誤所在的文件 [line] - 發生錯誤所在的行 註意:當parse-time出錯時是不會調用本函數的。只有在run-time出錯的時候,才會調用本函數。即需要成功註冊此函數才能使用。【測試3和測試4對比】
比如:
<?php try { $a = 1/0; } catch (Exception $e) { echo "cat not divied by 0"; } register_shutdown_function(‘myshutdownfunc‘); function myshutdownfunc() { if ($error = error_get_last()) { echo "<pre>"; print_r($error); echo "</pre>";die; } }
輸出:
Warning: Division by zero in D:\web\mytest\test.php on line 4
Array ( [type] => 2 [message] => Division by zero [file] => D:\web\mytest\test.php [line] => 4 )回到頂部
3.3、set_exception_handler(exception_function)
參數 | 描述 |
---|---|
error_function | 必需。規定未捕獲的異常發生時調用的函數。 該函數必須在調用 set_exception_handler() 函數之前定義。 這個異常處理函數需要需要一個參數,即拋出的 exception 對象。 |
作用: set_exception_handler() 函數設置用戶自定義的異常處理函數。 該函數用於創建運行時期間的用戶自己的異常處理方法。 該函數會返回舊的異常處理程序,若失敗,則返回 null。 提示:在這個異常處理程序被調用後,腳本會停止執行。
比如:
<?php // 第一種放方法 // function myException($exception) { // echo "<b>Exception:</b> " , $exception->getMessage(); // } // set_exception_handler(‘myException‘); // 第二種方法 class MyError{ //必須是靜態public方法 public static function myException($exception) { echo "<b>Exception:</b> " , $exception->getMessage(); } } set_exception_handler([‘MyError‘, ‘myException‘]); throw new Exception(‘沒有人處理異常‘);
輸出:Exception: 沒有人處理異常
PHP的異常處理機制