這篇文章將為大家詳細(xì)講解有關(guān)php中的錯誤處理與異常處理機(jī)制,文章內(nèi)容質(zhì)量較高,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
潁泉網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)公司等網(wǎng)站項目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)公司于2013年成立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運(yùn)維經(jīng)驗,來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)公司。
PHP5 已經(jīng)實現(xiàn)了異常的處理,這和其他語言差別不大,無非就是 try, catch, uncaught,按下不表,先說錯誤。
除了異常 PHP5 常見的就是拋出錯誤。你可以在官方文檔找到所有的錯誤的定義,這些錯誤可以大致分為 WARNING, ERROR(fatal error), NOTICE 等1。PHP的錯誤機(jī)制總結(jié)一文中給出了每種錯誤出現(xiàn)的場景。
E_DEPRECATED(8192) 運(yùn)行時通知,啟用后將會對在未來版本中可能無法正常工作的代碼給出警告。
E_USER_DEPRECATED(16384) 是由用戶自己在代碼中使用PHP函數(shù) trigger_error() 來產(chǎn)生的
E_NOTICE(8) 運(yùn)行時通知。表示腳本遇到可能會表現(xiàn)為錯誤的情況
E_USER_NOTICE(1024) 是用戶自己在代碼中使用PHP的trigger_error() 函數(shù)來產(chǎn)生的通知信息
E_WARNING(2) 運(yùn)行時警告 (非致命錯誤)
E_USER_WARNING(512) 用戶自己在代碼中使用PHP的 trigger_error() 函數(shù)來產(chǎn)生的
E_CORE_WARNING(32) PHP初始化啟動過程中由PHP引擎核心產(chǎn)生的警告
E_COMPILE_WARNING(128) Zend腳本引擎產(chǎn)生編譯時警告
E_ERROR(1) 致命的運(yùn)行時錯誤
E_USER_ERROR(256) 用戶自己在代碼中使用PHP的 trigger_error()函數(shù)來產(chǎn)生的
E_CORE_ERROR(16) 在PHP初始化啟動過程中由PHP引擎核心產(chǎn)生的致命錯誤
E_COMPILE_ERROR(64) Zend腳本引擎產(chǎn)生的致命編譯時錯誤
E_PARSE(4) 編譯時語法解析錯誤。解析錯誤僅僅由分析器產(chǎn)生
E_STRICT(2048) 啟用 PHP 對代碼的修改建議,以確保代碼具有最佳的互操作性和向前兼容性
E_RECOVERABLE_ERROR(4096) 可被捕捉的致命錯誤。 它表示發(fā)生了一個可能非常危險的錯誤,但是還沒有導(dǎo)致PHP引擎處于不穩(wěn)定的狀態(tài)。 如果該錯誤沒有被用戶自定義句柄捕獲 (參見 set_error_handler() ),將成為一個 E_ERROR 從而腳本會終止運(yùn)行。
E_ALL(30719) 所有錯誤和警告信息(手冊上說不包含E_STRICT, 經(jīng)過測試其實是包含E_STRICT的)。
常見的有:
由于歷史原因,這個老舊的 ci2 框架有不少不合理的地方,比如會讀取不存在的 log 文件;我們對 PHP 也有一些不規(guī)范的使用,比如:
我們的代碼不少地方較為依賴這種獲取不存在 key 得到 null 的表現(xiàn),而每次這樣使用都是會有一個 E_NOTICE 錯誤的。雖然可以通過 array_exists 來做 if else,但畢竟比較麻煩。PHP7 之后可以通過數(shù)據(jù)結(jié)構(gòu)插件來使用 Map, Set, Vector 等明確的數(shù)據(jù)結(jié)構(gòu),從而較好的解決這個問題。
PHP 對錯誤的處理
如果沒有做任何配置,PHP 的錯誤是會直接打印出來的。古老的 PHP 應(yīng)用也確實有這么做的。但現(xiàn)代應(yīng)用顯然不能這樣,現(xiàn)代應(yīng)用的錯誤應(yīng)該遵循一下規(guī)則2:
一定要讓 PHP 報告錯誤;
在開發(fā)環(huán)境中要顯示錯誤;
在生產(chǎn)環(huán)境中不能顯示錯誤;
在開發(fā)和生產(chǎn)環(huán)境中都要記錄錯誤。
在生產(chǎn)環(huán)境下,錯誤不能直接打印出來,應(yīng)該記到 log 文件中,并返回用戶一個籠統(tǒng)的錯誤信息。set_error_handler 函數(shù)就是設(shè)置用戶自定義的錯誤處理函數(shù),以處理腳本中出現(xiàn)的錯誤。我們可以在這個函數(shù)中將錯誤信息打到 log 文件中,并統(tǒng)一返回錯誤信息。
本來這個函數(shù)是搭配 trigger_error 函數(shù)使用的。用戶通過 trigger_error 產(chǎn)生 error,然后用 error_handler 來處理錯誤。只是在這種場景下往往「異?!垢糜?,所以這么用的并不多。
在前述的系統(tǒng)自帶的 16 種錯誤中,有一部分相當(dāng)重要的錯誤并不能被 error_handler 捕獲3:
以下級別的錯誤不能由用戶定義的函數(shù)來處理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、E_COMPILE_WARNING,和在調(diào)用 set_error_handler() 函數(shù)所在文件中產(chǎn)生的大多數(shù) E_STRICT。
這些錯誤將無法記錄下來,同時也不方便統(tǒng)一處理4。在 PHP7 之前的 PHP 版本一個很大的痛點(diǎn)就是:發(fā)生了 E_ERROR 錯誤,無法捕獲,導(dǎo)致數(shù)據(jù)庫的事務(wù)無法回滾造成數(shù)據(jù)不一致5。
另外一個需要注意的是, error_handler 處理完畢,腳本將會繼續(xù)執(zhí)行發(fā)生錯誤的后一行。在某些情況下,你可能希望遇到某些錯誤可以中斷腳本的執(zhí)行。在官方文檔中已說明,
同時注意,在需要時你有責(zé)任使用 die()。 如果錯誤處理程序返回了,腳本將會繼續(xù)執(zhí)行發(fā)生錯誤的后一行。
也就是說,某些情況下,我們處理完 E_WARNING 之后,需要及時退出腳本(即 die() 或者 exit())。
PHP 異常
異常是對程序錯誤的一種優(yōu)秀的處理方式,較于錯誤,異常的優(yōu)點(diǎn)是默認(rèn)打印調(diào)用棧,便于調(diào)試,可控等,可以參考一下鳥哥的文章我們什么時候應(yīng)該使用異常,清晰的點(diǎn)明了錯誤碼和異常的優(yōu)缺點(diǎn)。
對異常的處理也要遵循前述的錯誤處理規(guī)則2。在我們的日常開發(fā)中,不可能保證可以 catch 所有的異常,而未被 catch 的異常將以 fatal error 的形式中斷腳本的執(zhí)行并輸出錯誤信息。所以要借助 set_exception_handler,統(tǒng)一處理所有未被 catch 的異常。我們可以像 error_handler 那樣,在 exception_handler 中處理 log,將數(shù)據(jù)庫的事務(wù)回滾。
前面提到,error_handler 需要在必要的時候手動中斷腳本, PHP 文檔中給出的一種實踐是,在 error_handler 中 throw ErrorException,代碼示例如下:
這樣凡是不想忽略的 error,都會以 Uncaught ErrorException 的形式返回并中斷腳本。
PHP 異常機(jī)制
鳥哥通過一個例子講解了 PHP 的異常的處理機(jī)制,在這里轉(zhuǎn)述一下。
getMessage(); } set_error_handler("onError"); set_exception_handler("onException"); require("nonexist.php");其運(yùn)行結(jié)果為
- Error Occurred
- PHP Fatal error
而 onException 并沒有執(zhí)行到,說明在 error_handler 中 throw exception 不會被 exception_handler 截獲。
require 不存在的文件會拋出兩個錯誤,
- WARNING : 在PHP試圖打開這個文件的時候拋出
- E_COMPILE_ERROR : 從PHP打開文件的函數(shù)返回失敗以后拋出
PHP 中的異常處理機(jī)制如下:
而PHP在遇到 Fatal Error 的時候,會直接 zend_bailout,而 zend_bailout 會導(dǎo)致程序流程直接跳過上面代碼段,也可以理解為直接 exit 了(longjmp),這就導(dǎo)致了 user_exception_handler 沒有機(jī)會發(fā)生作用。
PHP 錯誤分類
綜上所述,在 PHP 中,錯誤和異??梢苑譃橐韵?3 個類別:異常,可截獲錯誤,不可截獲錯誤。異常和可截獲錯誤雖然機(jī)理不同,但可以當(dāng)做是同一種處理方式,而不可截獲錯誤是另一種,是一種較為棘手的錯誤類型。馬上將會講到,PHP7 中的 fatal error 是一種繼承自 Throwable 的 Error,是可以被 try catch 住的。通過這一方式 PHP7 解決了這一難題。
PHP7 的錯誤和異常
PHP 7 改變了大多數(shù)錯誤的報告方式。不同于傳統(tǒng)(PHP 5)的錯誤報告機(jī)制,現(xiàn)在大多數(shù)錯誤被作為 Error異常拋出(在 PHP7 中,只有 fatal error 和 recoverable error 拋出異常,其他 error 比如 warning 和 notice 的表現(xiàn)不變6)。PHP7 中的 Error 和 Exception 的關(guān)系如圖 6:
interface Throwable |- Exception implements Throwable |- ... |- Error implements Throwable |- TypeError extends Error |- ParseError extends Error |- ArithmeticError extends Error |- pisionByZeroError extends ArithmeticError |- AssertionError extends Error值得注意的是,Error 類表現(xiàn)上和 Exception 基本一致,可以像 Exception 異常一樣被第一個匹配的 try / catch 塊所捕獲,如果沒有匹配的 catch 塊,則調(diào)用異常處理函數(shù)(事先通過 set_exception_handler() 注冊7)進(jìn)行處理。 如果尚未注冊異常處理函數(shù),則按照傳統(tǒng)方式處理,被報告為一個致命錯誤(Fatal Error)。但并非繼承自 Exception 類(要考慮到和 PHP5 的兼容性),所以不能用 catch (Exception $e) { ... } 來捕獲,而需要使用 catch (Error $e) { ... },當(dāng)然,也可以使用 set_exception_handler 來捕獲。
但是,用戶不能自己定義類實現(xiàn) Throwable,這是為了保證只有 Exception 和 Error 才可以拋出。
PHP7 的 ERROR 處理
PHP7 中的 fatal error 會拋出 Error,且可以被正常 catch 到:
nonexist(); } catch (Error $e) { // Handle error }也有些錯誤場景下會拋出更加詳細(xì)的錯誤,比如:
getMessage(), "\n"; } // ArithmeticError try { $value = 1 << -1; } catch (ArithmeticError $e) { echo $e->getMessage(), "\n"; } // pisionByZeroError try { $value = 1 % 0; } catch (pisionByZeroError $e) { echo $e->getMessage(), "\n"; }Error 和 Exception 的選擇
當(dāng)需要自定義處理錯誤的時候,應(yīng)該選擇繼承 Error 還是 Exception 呢?
我們注意到,PHP7 中是將曾經(jīng)的 fatal error 變成了 Error 拋出,而 fatal error 一般都是一些不需要在運(yùn)行時處理的錯誤,這種錯誤旨在提醒程序員,這里的代碼寫的有問題,需要修復(fù),而不是邏輯上要 catch 它做某些業(yè)務(wù)。
因此,絕大多數(shù)情況下,我們并不需要繼承 Error,甚至 catch Error 也不常見,只在某些需要 log,回滾數(shù)據(jù)庫,清理現(xiàn)場等場合才需要這樣做。
對錯誤和異常的一種實踐
根據(jù)以上所述,我們提煉了一個對錯誤和異常處理較好的實踐。
- 對于業(yè)務(wù)中不應(yīng)該出現(xiàn)錯誤的地方,拋出 InternalException,而不是 Error
- 只在需要清理現(xiàn)場的時候 catch Error
- 未捕獲的 Error 和 Exception 通過 set_exception_handler 做后續(xù)清理和 log
- 其他錯誤仍然通過 set_error_handler 來處理,在處理的時候使用更加明確的 FriendlyErrorType,并拋出 ErrorException 記錄調(diào)用棧
FriendlyErrorType:
error_handler:
PHP中的錯誤級別與具體報錯信息分類 ?
PHP 最佳實踐之異常和錯誤 ? ?2
E_ERROR 無法捕獲,E_RECOVERABLE_ERROR 可以,后者默認(rèn)輸出 Catachable fatal error ?
fatal error 會記錄到 web 服務(wù)器的 error.log,這一點(diǎn)需要注意,因為這個 log 的位置往往不是 PHP 應(yīng)用定義的,而是 web 服務(wù)器定義的。 ?
PHP 中還有一個 register_shutdown_function 函數(shù),它允許注冊一個會在 PHP 中止時執(zhí)行的函數(shù),這個函數(shù)可以捕獲 fatal error,畢竟是只要是腳本中斷就可以捕獲的。ci2 并沒有使用這個方法,所以相關(guān)問題一直沒有得到很好的解決,當(dāng)時也沒有意識到這個函數(shù)的存在,升級 PHP7 之后可以通過 catch Error 來解決,便不再需要這樣處理了。 ?
Throwable Exceptions and Errors in PHP 7 ? ?2
在 PHP7 中,傳入 exception_handler 的參數(shù)從 Exception 改為 Throwable,這意味著 exception_handler 可以截獲 Error。 ?
以上就是php中的錯誤處理與異常處理機(jī)制介紹,看完之后是否有所收獲呢?如果想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊,感謝各位的閱讀。
文章名稱:php中的錯誤處理與異常處理機(jī)制介紹
文章起源:http://weahome.cn/article/gjpgcj.html