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 块。