亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

php中的錯誤處理與異常處理機制介紹

發布時間:2020-06-22 20:44:46 來源:億速云 閱讀:193 作者:Leah 欄目:編程語言

這篇文章將為大家詳細講解有關php中的錯誤處理與異常處理機制,文章內容質量較高,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

PHP 的錯誤和異常

PHP5 已經實現了異常的處理,這和其他語言差別不大,無非就是 try, catch, uncaught,按下不表,先說錯誤。

PHP 的錯誤

除了異常 PHP5 常見的就是拋出錯誤。你可以在官方文檔找到所有的錯誤的定義,這些錯誤可以大致分為 WARNING, ERROR(fatal error), NOTICE 等1。PHP的錯誤機制總結一文中給出了每種錯誤出現的場景。

E_DEPRECATED(8192) 運行時通知,啟用后將會對在未來版本中可能無法正常工作的代碼給出警告。

E_USER_DEPRECATED(16384) 是由用戶自己在代碼中使用PHP函數 trigger_error() 來產生的

E_NOTICE(8) 運行時通知。表示腳本遇到可能會表現為錯誤的情況

E_USER_NOTICE(1024) 是用戶自己在代碼中使用PHP的trigger_error() 函數來產生的通知信息

E_WARNING(2) 運行時警告 (非致命錯誤)

E_USER_WARNING(512) 用戶自己在代碼中使用PHP的 trigger_error() 函數來產生的

E_CORE_WARNING(32) PHP初始化啟動過程中由PHP引擎核心產生的警告

E_COMPILE_WARNING(128) Zend腳本引擎產生編譯時警告

E_ERROR(1) 致命的運行時錯誤

E_USER_ERROR(256) 用戶自己在代碼中使用PHP的 trigger_error()函數來產生的

E_CORE_ERROR(16) 在PHP初始化啟動過程中由PHP引擎核心產生的致命錯誤

E_COMPILE_ERROR(64) Zend腳本引擎產生的致命編譯時錯誤

E_PARSE(4) 編譯時語法解析錯誤。解析錯誤僅僅由分析器產生

E_STRICT(2048) 啟用 PHP 對代碼的修改建議,以確保代碼具有最佳的互操作性和向前兼容性

E_RECOVERABLE_ERROR(4096) 可被捕捉的致命錯誤。 它表示發生了一個可能非常危險的錯誤,但是還沒有導致PHP引擎處于不穩定的狀態。 如果該錯誤沒有被用戶自定義句柄捕獲 (參見 set_error_handler() ),將成為一個 E_ERROR  從而腳本會終止運行。

E_ALL(30719) 所有錯誤和警告信息(手冊上說不包含E_STRICT, 經過測試其實是包含E_STRICT的)。

常見的有:

<?php
// E_ERROR
nonexist(); // PHP Fatal error:  Call to undefined function nonexist()
throw new Exception(''); // 未捕獲異常也是 fatal error

// E_NOTICE
$a = $b; //  PHP Notice:  Undefined variable
$a = []; $a[2]; // PHP Notice:  Undefined offset: 2

// E_WARNNING
require 'nonexist.php' // warning and fatal error

由于歷史原因,這個老舊的 ci2 框架有不少不合理的地方,比如會讀取不存在的 log 文件;我們對 PHP 也有一些不規范的使用,比如:

<?php
$req = [];
$user_id = $req['user_id']; // PHP error:  Undefined offset
if (null === $user_id) { /* do something */}

我們的代碼不少地方較為依賴這種獲取不存在 key 得到 null 的表現,而每次這樣使用都是會有一個 E_NOTICE 錯誤的。雖然可以通過 array_exists 來做 if else,但畢竟比較麻煩。PHP7 之后可以通過數據結構插件來使用 Map, Set, Vector 等明確的數據結構,從而較好的解決這個問題。

PHP 對錯誤的處理

如果沒有做任何配置,PHP 的錯誤是會直接打印出來的。古老的 PHP 應用也確實有這么做的。但現代應用顯然不能這樣,現代應用的錯誤應該遵循一下規則2

一定要讓 PHP 報告錯誤;

在開發環境中要顯示錯誤;

在生產環境中不能顯示錯誤;

在開發和生產環境中都要記錄錯誤。

在生產環境下,錯誤不能直接打印出來,應該記到 log 文件中,并返回用戶一個籠統的錯誤信息。set_error_handler 函數就是設置用戶自定義的錯誤處理函數,以處理腳本中出現的錯誤。我們可以在這個函數中將錯誤信息打到 log 文件中,并統一返回錯誤信息。

本來這個函數是搭配 trigger_error 函數使用的。用戶通過 trigger_error 產生 error,然后用 error_handler 來處理錯誤。只是在這種場景下往往「異常」更好用,所以這么用的并不多。

在前述的系統自帶的 16 種錯誤中,有一部分相當重要的錯誤并不能被 error_handler 捕獲3

以下級別的錯誤不能由用戶定義的函數來處理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、E_COMPILE_WARNING,和在調用 set_error_handler() 函數所在文件中產生的大多數 E_STRICT。

這些錯誤將無法記錄下來,同時也不方便統一處理4。在 PHP7 之前的 PHP 版本一個很大的痛點就是:發生了 E_ERROR 錯誤,無法捕獲,導致數據庫的事務無法回滾造成數據不一致5

另外一個需要注意的是, error_handler 處理完畢,腳本將會繼續執行發生錯誤的后一行。在某些情況下,你可能希望遇到某些錯誤可以中斷腳本的執行。在官方文檔中已說明,

同時注意,在需要時你有責任使用 die()。 如果錯誤處理程序返回了,腳本將會繼續執行發生錯誤的后一行。

也就是說,某些情況下,我們處理完 E_WARNING 之后,需要及時退出腳本(即 die() 或者 exit())。

PHP 異常

異常是對程序錯誤的一種優秀的處理方式,較于錯誤,異常的優點是默認打印調用棧,便于調試,可控等,可以參考一下鳥哥的文章我們什么時候應該使用異常,清晰的點明了錯誤碼和異常的優缺點。

對異常的處理也要遵循前述的錯誤處理規則2。在我們的日常開發中,不可能保證可以 catch 所有的異常,而未被 catch 的異常將以 fatal error 的形式中斷腳本的執行并輸出錯誤信息。所以要借助 set_exception_handler,統一處理所有未被        catch 的異常。我們可以像 error_handler 那樣,在 exception_handler 中處理 log,將數據庫的事務回滾。

前面提到,error_handler 需要在必要的時候手動中斷腳本, PHP 文檔中給出的一種實踐是,在 error_handler 中 throw ErrorException,代碼示例如下:

<?php
function exception_error_handler($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // This error code is not included in error_reporting
        return;
    }
    throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");

/* Trigger exception */
strpos();

這樣凡是不想忽略的 error,都會以 Uncaught ErrorException 的形式返回并中斷腳本。

PHP 異常機制

鳥哥通過一個例子講解了 PHP 的異常的處理機制,在這里轉述一下。

<?php
function onError($errCode, $errMesg, $errFile, $errLine) {
    echo "Error Occurred\n";
    throw new Exception($errMesg);
}
 
function onException($e) {
    echo '********exception: ' . $e->getMessage();
}
 
set_error_handler("onError");
 
set_exception_handler("onException");

require("nonexist.php");

其運行結果為

  1. Error Occurred
  2. PHP Fatal error

而 onException 并沒有執行到,說明在 error_handler 中 throw exception 不會被 exception_handler 截獲。

require 不存在的文件會拋出兩個錯誤,

  1. WARNING : 在PHP試圖打開這個文件的時候拋出
  2. E_COMPILE_ERROR : 從PHP打開文件的函數返回失敗以后拋出

PHP 中的異常處理機制如下:

php中的錯誤處理與異常處理機制介紹    

而PHP在遇到 Fatal Error 的時候,會直接 zend_bailout,而 zend_bailout 會導致程序流程直接跳過上面代碼段,也可以理解為直接 exit 了(longjmp),這就導致了 user_exception_handler 沒有機會發生作用。

PHP 錯誤分類

綜上所述,在 PHP 中,錯誤和異常可以分為以下 3 個類別:異常,可截獲錯誤,不可截獲錯誤。異常和可截獲錯誤雖然機理不同,但可以當做是同一種處理方式,而不可截獲錯誤是另一種,是一種較為棘手的錯誤類型。馬上將會講到,PHP7 中的 fatal error 是一種繼承自 Throwable 的 Error,是可以被 try catch 住的。通過這一方式 PHP7 解決了這一難題。

PHP7 的錯誤和異常

PHP 7 改變了大多數錯誤的報告方式。不同于傳統(PHP 5)的錯誤報告機制,現在大多數錯誤被作為 Error 異常拋出(在 PHP7 中,只有 fatal error 和 recoverable error 拋出異常,其他 error 比如 warning 和 notice 的表現不變6)。PHP7 中的 Error 和 Exception 的關系如圖        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 類表現上和 Exception 基本一致,可以像 Exception 異常一樣被第一個匹配的 try / catch 塊所捕獲,如果沒有匹配的 catch 塊,則調用異常處理函數(事先通過        set_exception_handler() 注冊7)進行處理。 如果尚未注冊異常處理函數,則按照傳統方式處理,被報告為一個致命錯誤(Fatal Error)。但并非繼承自 Exception 類(要考慮到和 PHP5 的兼容性),所以不能用 catch (Exception        $e) { ... } 來捕獲,而需要使用 catch (Error $e) { ... },當然,也可以使用 set_exception_handler 來捕獲。

但是,用戶不能自己定義類實現 Throwable,這是為了保證只有 Exception 和 Error 才可以拋出。

PHP7 的 ERROR 處理

PHP7 中的 fatal error 會拋出 Error,且可以被正常 catch 到:

<?php
$a = 1;
try {
  $a->nonexist();
} catch (Error $e) {
  // Handle error
}

也有些錯誤場景下會拋出更加詳細的錯誤,比如:

<?php
// TypeError
function test(int $i) {
  echo $i;
}
try {
  test('test');
} catch (TypeError $e) {
  // Handle error
}

// ParseError
try{
  eval('i=1;');
} catch (ParseError $e) { 
  echo $e->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 的選擇

當需要自定義處理錯誤的時候,應該選擇繼承 Error 還是 Exception 呢?

我們注意到,PHP7 中是將曾經的 fatal error 變成了 Error 拋出,而 fatal error 一般都是一些不需要在運行時處理的錯誤,這種錯誤旨在提醒程序員,這里的代碼寫的有問題,需要修復,而不是邏輯上要 catch 它做某些業務。

因此,絕大多數情況下,我們并不需要繼承 Error,甚至 catch Error 也不常見,只在某些需要 log,回滾數據庫,清理現場等場合才需要這樣做。

對錯誤和異常的一種實踐

根據以上所述,我們提煉了一個對錯誤和異常處理較好的實踐。

  1. 對于業務中不應該出現錯誤的地方,拋出 InternalException,而不是 Error
<?php
class InternalException extends Exception { /*...*/ }

function find(Array $ids) {
  if (empty($ids)) {
    throw new InternalException('ids should not be empty');
  }
  ...
}
  1. 只在需要清理現場的時候 catch Error
<?php
try { /*...*/ }
catch (Throwable $t) {
  // log, transaction rollback, cleanup...
}
  1. 未捕獲的 Error 和 Exception 通過 set_exception_handler 做后續清理和 log
  2. 其他錯誤仍然通過 set_error_handler 來處理,在處理的時候使用更加明確的 FriendlyErrorType,并拋出 ErrorException 記錄調用棧

FriendlyErrorType:

<?php
function FriendlyErrorType($type) 
{ 
    switch($type) 
    { 
        case E_ERROR: // 1 // 
            return 'E_ERROR'; 
        case E_WARNING: // 2 // 
            return 'E_WARNING'; 
        case E_PARSE: // 4 // 
            return 'E_PARSE'; 
        case E_NOTICE: // 8 // 
            return 'E_NOTICE'; 
        case E_CORE_ERROR: // 16 // 
            return 'E_CORE_ERROR'; 
        case E_CORE_WARNING: // 32 // 
            return 'E_CORE_WARNING'; 
        case E_COMPILE_ERROR: // 64 // 
            return 'E_COMPILE_ERROR'; 
        case E_COMPILE_WARNING: // 128 // 
            return 'E_COMPILE_WARNING'; 
        case E_USER_ERROR: // 256 // 
            return 'E_USER_ERROR'; 
        case E_USER_WARNING: // 512 // 
            return 'E_USER_WARNING'; 
        case E_USER_NOTICE: // 1024 // 
            return 'E_USER_NOTICE'; 
        case E_STRICT: // 2048 // 
            return 'E_STRICT'; 
        case E_RECOVERABLE_ERROR: // 4096 // 
            return 'E_RECOVERABLE_ERROR'; 
        case E_DEPRECATED: // 8192 // 
            return 'E_DEPRECATED'; 
        case E_USER_DEPRECATED: // 16384 // 
            return 'E_USER_DEPRECATED'; 
    } 
    return ""; 
}

error_handler:

<?php
function exception_error_handler($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // This error code is not included in error_reporting
        return;
    }
 	log FriendlyErrorType($severity);
    throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");
  1. PHP中的錯誤級別與具體報錯信息分類 ?

  2. PHP 最佳實踐之異常和錯誤 ? ?2

  3. E_ERROR 無法捕獲,E_RECOVERABLE_ERROR 可以,后者默認輸出 Catachable fatal error ?

  4. fatal error 會記錄到 web 服務器的 error.log,這一點需要注意,因為這個 log 的位置往往不是 PHP 應用定義的,而是 web 服務器定義的。 ?

  5. PHP 中還有一個 register_shutdown_function 函數,它允許注冊一個會在 PHP 中止時執行的函數,這個函數可以捕獲 fatal error,畢竟是只要是腳本中斷就可以捕獲的。ci2 并沒有使用這個方法,所以相關問題一直沒有得到很好的解決,當時也沒有意識到這個函數的存在,升級 PHP7 之后可以通過                catch Error 來解決,便不再需要這樣處理了。 ?

  6. Throwable Exceptions and Errors in PHP 7 ? ?2

  7. 在 PHP7 中,傳入 exception_handler 的參數從 Exception 改為 Throwable,這意味著 exception_handler 可以截獲 Error。 ?

以上就是php中的錯誤處理與異常處理機制介紹,看完之后是否有所收獲呢?如果想了解更多相關內容,歡迎關注億速云行業資訊,感謝各位的閱讀。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

山西省| 衡阳县| 肥城市| 乌苏市| 农安县| 秀山| 同仁县| 宁城县| 平陆县| 和硕县| 互助| 穆棱市| 南皮县| 吉首市| 肥城市| 科技| 怀宁县| 吉林市| 隆昌县| 阿拉善右旗| 平南县| 台北县| 广西| 安远县| 华宁县| 耿马| 泗洪县| 郸城县| 湟源县| 东乡族自治县| 赣榆县| 资源县| 新余市| 高要市| 信宜市| 兴仁县| 宁明县| 淅川县| 仪征市| 太仆寺旗| 佛坪县|