PHP 異常處理

在本教程中,你將學習如何在 PHP 中丟擲和捕獲異常。

什麼是例外

異常是指示某種異常事件或錯誤發生的訊號。由於各種原因可能導致異常,例如,資料庫連線或查詢失敗,你嘗試訪問的檔案不存在,等等。

PHP 提供了一種強大的異常處理機制,允許你以優雅的方式處理異常。與 PHP 的傳統錯誤處理系統相反,異常處理是物件導向的處理錯誤的方法,它提供了更加可控和靈活的錯誤報告形式。PHP 5 中首次引入了異常模型。

使用 throwtry...catch 語句

在基於異常的方法中,程式程式碼是在 try 塊中編寫的,當在 try 塊中執行程式碼期間發生異常事件時,可以使用 throw 語句丟擲異常。然後由一個或多個 catch 塊捕獲並解決它。

以下示例演示了異常處理的工作原理:

<?php
function division($dividend, $divisor){
    // Throw exception if divisor is zero
    if($divisor == 0){
        throw new Exception('Division by zero.');
    } else{
        $quotient = $dividend / $divisor;
        echo "<p>$dividend / $divisor = $quotient</p>";
    }
}
 
try{
    division(10, 2);
    division(30, -4);
    division(15, 0);
    
    // If exception is thrown following line won't execute
    echo '<p>All divisions performed successfully.</p>';
} catch(Exception $e){
    // Handle the exception
    echo "<p>Caught exception: " . $e->getMessage() . "</p>";
}
 
// Continue execution
echo "<p>Hello World!</p>";
?>

你可能想知道這段程式碼到底是什麼。好吧,讓我們逐個瀏覽這段程式碼的每一部分,以便更好地理解。

程式碼說明

在 PHP 的異常處理系統有四種基本部分: trythrowcatch ,和 Exception 類。以下列表描述了每個部件的確切工作方式。

  • 上例中的 division() 函式檢查除數是否等於零。如果是,則通過 PHP throw 語句丟擲異常。否則,此功能使用給定的數字執行除法並顯示結果。
  • 稍後,在具有不同引數 division()try 塊內呼叫該函式。如果在 try 塊內執行程式碼時生成異常,PHP 將在此時停止執行並嘗試查詢相應的 catch 塊。如果找到, catch 則執行該塊中的程式碼,否則,將生成致命錯誤。
  • catch 塊通常捕獲 try 塊中丟擲的異常並建立包含異常資訊的物件($e)。可以使用 Exception 的 getMessage() 方法檢索此物件的錯誤訊息。

在 PHP 的異常類也提供了 getCode()getFile()getLine()getTraceAsString() 可用於生成詳細的除錯資訊的方法。

<?php
// Turn off default error reporting
error_reporting(0);
 
try{
    $file = "somefile.txt";
    
    // Attempt to open the file
    $handle = fopen($file, "r");
    if(!$handle){
        throw new Exception("Cannot open the file!", 5);
    }
    
    // Attempt to read the file contents
    $content = fread($handle, filesize($file));
    if(!$content){
        throw new Exception("Could not read file!", 10);
    }
    
    // Closing the file handle
    fclose($handle);
    
    // Display file contents
    echo $content;
} catch(Exception $e){
    echo "<h3>Caught Exception!</h3>";
    echo "<p>Error message: " . $e->getMessage() . "</p>";    
    echo "<p>File: " . $e->getFile() . "</p>";
    echo "<p>Line: " . $e->getLine() . "</p>";
    echo "<p>Error code: " . $e->getCode() . "</p>";
    echo "<p>Trace: " . $e->getTraceAsString() . "</p>";
}
?>

Exception 的建構函式可選地接受異常訊息和異常程式碼。雖然異常訊息通常用於顯示出錯的通用資訊,但異常程式碼可用於對錯誤進行分類。提供的異常程式碼可以稍後通過 Exception 的 getCode() 方法檢索。

提示: 例外僅應用於表示特殊情況; 它們不應該用於控制正常的應用程式流,例如,在特定點跳轉到指令碼中的另一個位置。這樣做會對應用程式的效能產生負面影響。

定義自定義異常

你甚至可以定義自己的自定義異常處理程式,以不同的方式處理不同型別的異常。它允許你為每種異常型別使用單獨的 catch 塊。

你可以通過擴充套件 Exception 類來定義自定義異常,因為 Exception 是所有異常的基類。自定義異常類從 PHP 的 Exception 類繼承所有屬性和方法。你還可以將自定義方法新增到自定義異常類。我們來看看下面的例子:

<?php
// Extending the Exception class
class EmptyEmailException extends Exception {}
class InvalidEmailException extends Exception {}
 
$email = "someuser@example..com";
 
try{
    // Throw exception if email is empty
    if($email == ""){
        throw new EmptyEmailException("<p>Please enter your E-mail address!</p>");
    }
    
    // Throw exception if email is not valid
    if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {           
        throw new InvalidEmailException("<p><b>$email</b> is not a valid E-mail address!</p>");
    }
    
    // Display success message if email is valid
    echo "<p>SUCCESS: Email validation successful.</p>";
} catch(EmptyEmailException $e){
    echo $e->getMessage();
} catch(InvalidEmailException $e){
    echo $e->getMessage();
}
?>

在上面的例子中,我們已經得出了兩個新的異常類:從 Exception 基類來的 EmptyEmailException,和 InvalidEmailException。多個 catch 塊用於顯示不同的錯誤訊息,具體取決於生成的異常型別。

由於這些自定義異常類繼承了 Exception 類的屬性和方法,所以我們可以使用異常的類方法,如 getMessage()getLine()getFile() ,等來檢索異常物件的錯誤資訊。

設定全域性異常處理程式

正如我們在本章前面討論的那樣,如果沒有捕獲到異常,PHP 會生成帶有 Uncaught Exception ... 訊息的致命錯誤。此錯誤訊息可能包含出現問題的敏感資訊,如檔名和行號。如果你不希望向使用者公開此類資訊,則可以建立自定義函式並將其註冊到 set_exception_handler() 函式以處理所有未捕獲的異常。

<?php
function handleUncaughtException($e){
    // Display generic error message to the user
    echo "Opps! Something went wrong. Please try again, or contact us if the problem persists.";
    
    // Construct the error string
    $error = "Uncaught Exception: " . $message = date("Y-m-d H:i:s - ");
    $error .= $e->getMessage() . " in file " . $e->getFile() . " on line " . $e->getLine() . "\n";
    
    // Log details of error in a file
    error_log($error, 3, "var/log/exceptionLog.log");
}
 
// Register custom exception handler
set_exception_handler("handleUncaughtException");
 
// Throw an exception
throw new Exception("Testing Exception!");
?>

注意: 未捕獲的異常將始終導致指令碼終止。因此,如果你希望指令碼繼續執行超出發生異常的點,則每個 try 塊必須至少有一個相應的 catch 塊。