1. 程式人生 > >PHP的異常處理機制

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的異常處理機制