本篇文章給大家介紹一下PHP7使用set_error_handler和set_exception_handler處理異常機制的方法。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。
由于歷史原因,php一開始被設計為一門面向過程的語言,所以異常處理沒有使用像Java一樣的 try / catch 機制,出錯時直接顯示到頁面上,或者記錄到web服務器的錯誤日志中,并且php的錯誤分成了很多的級別,例如E_ERROR、E_WARNING、E_PARSE、E_NOTICE等等,對于像E_ERROR、E_PARSE這樣的嚴重錯誤,php會直接終止腳本的運行。
雖然對于php5版本,我們可以使用set_error_handler來注冊自己的錯誤處理方法來代替php的標準錯誤處理方式(輸出到頁面或者記錄到日志),但是一些嚴重錯誤是無法通過這種方式來處理的,具體我們來看手冊對該方法的介紹:
mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )
設置一個用戶的函數(shù)(error_handler)來處理腳本中出現(xiàn)的錯誤。
本函數(shù)可以用你自己定義的方式來處理運行中的錯誤, 例如,在應用程序中嚴重錯誤發(fā)生時,或者在特定條件下觸發(fā)了一個錯誤(使用 trigger_error()),你需要對數(shù)據(jù)/文件做清理回收。
重要的是要記住 error_types 里指定的錯誤類型都會繞過 PHP 標準錯誤處理程序, 除非回調函數(shù)返回了 FALSE。 error_reporting() 設置將不會起到作用而你的錯誤處理函數(shù)繼續(xù)會被調用 —— 不過你仍然可以獲取 error_reporting 的當前值,并做適當處理。 需要特別注意的是帶 @ error-control operator 前綴的語句發(fā)生錯誤時,這個值會是 0。
同時注意,在需要時你有責任使用 die()。 如果錯誤處理程序返回了,腳本將會繼續(xù)執(zhí)行發(fā)生錯誤的后一行。
以下級別的錯誤不能由用戶定義的函數(shù)來處理:
E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 調用 set_error_handler() 函數(shù)所在文件中產生的大多數(shù) E_STRICT。
手冊上說的很清楚,對于E_ERROR、E_PARSE之類的錯誤并不能被用戶處理,我們來看代碼演示(以下示例用php5.6運行)
<?php //自定義的錯誤處理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "錯誤編號errno: $errno<br>"; echo "錯誤信息errstr: $errstr<br>"; echo "出錯文件errfile: $errfile<br>"; echo "出錯行號errline: $errline<br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注冊錯誤處理方法來處理所有錯誤 echo $foo['bar']; // 由于數(shù)組未定義,會產生一個notice級別的錯誤
運行結果:
錯誤編號errno: 8 錯誤信息errstr: Undefined variable: foo 出錯文件errfile: D:\project\demo\demo.php 出錯行號errline: 16
這時錯誤信息并沒有像往常一樣直接輸出到頁面上,而是按照我們自己的方式來處理了,如果不使用set_error_handler函數(shù),錯誤信息會是常見的這樣展示,當然我們可以關閉掉php的錯誤顯示,這樣錯誤就不會直接顯示到頁面上了。
Notice: Undefined variable: foo in D:\project\demo\demo.php on line 15
這樣的處理機制看似也還不錯,但上面提到不是所有錯誤都可以這樣處理,修改一下上面的代碼如下:
<?php //自定義的錯誤處理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "錯誤編號errno: $errno<br>"; echo "錯誤信息errstr: $errstr<br>"; echo "出錯文件errfile: $errfile<br>"; echo "出錯行號errline: $errline<br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注冊錯誤處理方法來處理所有錯誤 echo $foo['bar']; // 由于數(shù)組未定義,會產生一個notice級別的錯誤 trigger_error('人為觸發(fā)一個錯誤', E_USER_ERROR); //人為觸發(fā)錯誤 foobar(3, 5); //調用未定義的方法將會產生一個Error級別的錯誤
再來運行:
錯誤編號errno: 8 錯誤信息errstr: Undefined variable: foo 出錯文件errfile: D:\project\demo\demo.php 出錯行號errline: 15 錯誤編號errno: 256 錯誤信息errstr: 人為產生觸發(fā)一個錯誤 出錯文件errfile: D:\project\demo\demo.php 出錯行號errline: 17 Fatal error: Call to undefined function foobar() in D:\project\demo\demo.php on line 19
正如我們所料,前兩個錯誤被我們“捕獲”處理了,而最后的Fatal error并沒有按照我們注冊的錯誤函數(shù)來處理,還是使用的默認的處理方式,這也是php5版本的錯誤處理的一大缺陷。PHP7之后的異常處理方式有了一些新的變化,來看看手冊上的介紹:
PHP 7 改變了大多數(shù)錯誤的報告方式。不同于傳統(tǒng)(PHP 5)的錯誤報告機制,現(xiàn)在大多數(shù)錯誤被作為 Error 異常拋出。
這種 Error 異??梢韵?Exception 異常一樣被第一個匹配的 try / catch 塊所捕獲。如果沒有匹配的 catch 塊,則調用異常處理函數(shù)(事先通過 set_exception_handler() 注冊)進行處理。 如果尚未注冊異常處理函數(shù),則按照傳統(tǒng)方式處理:被報告為一個致命錯誤(Fatal Error)。
Error 類并非繼承自 Exception 類,所以不能用 catch (Exception $e) { ... } 來捕獲 Error。你可以用 catch (Error $e) { ... },或者通過注冊異常處理函數(shù)( set_exception_handler())來捕獲 Error。
php7的這種錯誤處理機制有像java學習的意味,這樣使得我們可以自己來處理大多數(shù)的異常,下面看代碼示例(以下代碼使用php7運行)
<?php //自定義的錯誤處理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "錯誤編號errno: $errno<br>"; echo "錯誤信息errstr: $errstr<br>"; echo "出錯文件errfile: $errfile<br>"; echo "出錯行號errline: $errline<br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注冊錯誤處理方法來處理所有錯誤 try { echo $foo['bar']; // 由于數(shù)組未定義,會產生一個notice級別的錯誤 trigger_error('人為產生觸發(fā)一個錯誤', E_USER_ERROR); //人為觸發(fā)錯誤 foobar(3, 5); //調用未定義的方法將會產生一個Error級別的錯誤 } catch (Error $e) { echo "Error code: " . $e->getCode() . '<br>'; echo "Error message: " . $e->getMessage() . '<br>'; echo "Error file: " . $e->getFile() . '<br>'; echo "Error fileline: " . $e->getLine() . '<br>'; }
運行結果:
錯誤編號errno: 8 錯誤信息errstr: Undefined variable: foo 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 17 錯誤編號errno: 256 錯誤信息errstr: 人為產生觸發(fā)一個錯誤 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 19 Error code: 0 Error message: Call to undefined function foobar() Error file: E:\project\demo\demo.php Error fileline: 21
這樣不同類型的錯誤都可以被我們自己處理了,包括致命錯誤。如果不使用 try / catch , php7的報錯信息和php5還是有一些不同:
錯誤編號errno: 8 錯誤信息errstr: Undefined variable: foo 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 17 錯誤編號errno: 256 錯誤信息errstr: 人為觸發(fā)一個錯誤 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 19 Fatal error: Uncaught Error: Call to undefined function foobar() in E:\project\demo\demo.php:21 Stack trace: #0 {main} thrown in E:\project\demo\demo.php on line 21
致命錯誤的描述變成: 拋出的一個Error沒有被捕獲。
注意這里的catch限定的只能捕獲Error類的錯誤,并且手冊上明確說了 Error類并不是Exception類的子類,那我同時想捕獲代碼中的Exception錯誤不是做不到了嗎,請看代碼:
<?php //自定義的錯誤處理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "錯誤編號errno: $errno<br>"; echo "錯誤信息errstr: $errstr<br>"; echo "出錯文件errfile: $errfile<br>"; echo "出錯行號errline: $errline<br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注冊錯誤處理方法來處理所有錯誤 try { echo $foo['bar']; // 由于數(shù)組未定義,會產生一個notice級別的錯誤 trigger_error('人為觸發(fā)一個錯誤', E_USER_ERROR); //人為觸發(fā)錯誤 throw new Exception('This is a exception', 400); //拋出一個Exception,看是否可以被catch foobar(3, 5); //調用未定義的方法將會產生一個Error級別的錯誤 } catch (Error $e) { echo "Error code: " . $e->getCode() . '<br>'; echo "Error message: " . $e->getMessage() . '<br>'; echo "Error file: " . $e->getFile() . '<br>'; echo "Error fileline: " . $e->getLine() . '<br>'; }
運行結果:
錯誤編號errno: 8 錯誤信息errstr: Undefined variable: foo 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 17 錯誤編號errno: 256 錯誤信息errstr: 人為觸發(fā)一個錯誤 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 19 Fatal error: Uncaught Exception: This is a exception in E:\project\demo\demo.php:21 Stack trace: #0 {main} thrown in E:\project\demo\demo.php on line 21
那有沒有什么辦法呢,其實看手冊上的繼承關系圖
可以看出,Error類和Exception類都是Throwable的子類(實際上是Error類和Exception類都實現(xiàn)了Throwable接口),所以上面的代碼可以優(yōu)化為:
<?php //自定義的錯誤處理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "錯誤編號errno: $errno<br>"; echo "錯誤信息errstr: $errstr<br>"; echo "出錯文件errfile: $errfile<br>"; echo "出錯行號errline: $errline<br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注冊錯誤處理方法來處理所有錯誤 try { echo $foo['bar']; // 由于數(shù)組未定義,會產生一個notice級別的錯誤 trigger_error('人為觸發(fā)一個錯誤', E_USER_ERROR); //人為觸發(fā)錯誤 if (mt_rand(1, 10) > 5) { throw new Exception('This is a exception', 400); //拋出一個Exception,看是否可以被catch } else { foobar(3, 5); //調用未定義的方法將會產生一個Error級別的錯誤 } } catch (Throwable $e) { echo "Error code: " . $e->getCode() . '<br>'; echo "Error message: " . $e->getMessage() . '<br>'; echo "Error file: " . $e->getFile() . '<br>'; echo "Error fileline: " . $e->getLine() . '<br>'; }
多次運行可以看到,不管是Exception異常還是Error異常,都可以被捕獲處理了。
如果不想所有的錯誤都用 try / catch 處理,還可以使用set_exception_handler注冊異常處理函數(shù),這樣當有未被catch的異常產生時,系統(tǒng)會為我們自動調用注冊的處理函數(shù)來處理。
<?php //自定義的錯誤處理方法 function _error_handler($errno, $errstr ,$errfile, $errline) { echo "錯誤編號errno: $errno<br>"; echo "錯誤信息errstr: $errstr<br>"; echo "出錯文件errfile: $errfile<br>"; echo "出錯行號errline: $errline<br>"; } set_error_handler('_error_handler', E_ALL | E_STRICT); // 注冊錯誤處理方法來處理所有錯誤 function _exception_handler(Throwable $e) { if ($e instanceof Error) { echo "catch Error: " . $e->getCode() . ' ' . $e->getMessage() . '<br>'; } else { echo "catch Exception: " . $e->getCode() . ' ' . $e->getMessage() . '<br>'; } } set_exception_handler('_exception_handler'); // 注冊異常處理方法來捕獲異常 echo $foo['bar']; // 由于數(shù)組未定義,會產生一個notice級別的錯誤 trigger_error('人為觸發(fā)一個錯誤', E_USER_ERROR); //人為觸發(fā)錯誤 if (mt_rand(1, 10) > 5) { throw new Exception('This is a exception', 400); //拋出一個Exception,看是否可以被catch } else { foobar(3, 5); //調用未定義的方法將會產生一個Error級別的錯誤 }
錯誤編號errno: 8 錯誤信息errstr: Undefined variable: foo 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 29 錯誤編號errno: 256 錯誤信息errstr: 人為觸發(fā)一個錯誤 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 31 catch Error: 0 Call to undefined function foobar() 錯誤編號errno: 8 錯誤信息errstr: Undefined variable: foo 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 29 錯誤編號errno: 256 錯誤信息errstr: 人為觸發(fā)一個錯誤 出錯文件errfile: E:\project\demo\demo.php 出錯行號errline: 31 catch Exception: 400 This is a exception
這時我們可能又會被PHP7弄暈,哪些被set_error_handler處理,哪些被set_exception_handler處理,手冊上也沒有明確說明這塊,根據(jù)我的總結,大致上不會導致腳本終止運行的錯誤會被set_error_handler處理,而會終止腳本運行的嚴重錯誤會被當作Error拋出,但不是絕對,上面人為觸發(fā)的
E_USER_ERROR就是一個會打斷腳本運行的錯誤,但是并沒有當作Error異常拋出,而是交由set_error_handler注冊的方法處理,這可能是因為這類錯誤是我們自己人為產生的有關,所以PHP7的錯誤處理還是有一些含糊不清,對于我們自己處理時要多加小心。