PHP 異常處理
在本教程中,你將學習如何在 PHP 中丟擲和捕獲異常。
什麼是例外
異常是指示某種異常事件或錯誤發生的訊號。由於各種原因可能導致異常,例如,資料庫連線或查詢失敗,你嘗試訪問的檔案不存在,等等。
PHP 提供了一種強大的異常處理機制,允許你以優雅的方式處理異常。與 PHP 的傳統錯誤處理系統相反,異常處理是物件導向的處理錯誤的方法,它提供了更加可控和靈活的錯誤報告形式。PHP 5 中首次引入了異常模型。
使用 throw
和 try...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 的異常處理系統有四種基本部分: try
, throw
, catch
,和 Exception 類。以下列表描述了每個部件的確切工作方式。
- 上例中的
division()
函式檢查除數是否等於零。如果是,則通過 PHPthrow
語句丟擲異常。否則,此功能使用給定的數字執行除法並顯示結果。 - 稍後,在具有不同引數
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
塊。