PHP 异常处理

在本教程中,您将学习如何在PHP中引发和捕获异常。

什么是异常

异常是表示发生某种异常事件或错误的信号。可能由于多种原因导致异常,例如,数据库连接或查询失败,您尝试访问的文件不存在等等。

PHP提供了强大的异常处理机制,使您能够以优美的方式处理异常。与PHP的传统错误处理系统相反,异常处理是一种用于处理错误面向对象的方法,它提供了更具指定性和灵活性的错误报告形式。异常模型最早是在PHP 5中引入的。

使用Throw 和 Try ... Catch语句

在基于异常的方法中,程序代码编写在try块中,当在try块中执行代码期间发生异常事件时,可以使用throw语句引发异常。 然后由一个或多个捕获块捕获并解析它。

下面的示例演示异常处理如何工作:

<?php
function division($dividend, $divisor){
    //如果除数为零,则抛出异常
    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);
    
    //如果抛出异常,后面的行将不会执行
    echo '<p>所有的均成功执行。</p>';
} catch(Exception $e){
    //处理异常
    echo "<p>捕获的异常: " . $e->getMessage() . "</p>";
}
 
// 继续执行
echo "<p>Hello World!</p>";
?>

您可能想知道这段代码是关于什么的。好吧,让我们逐一遍历此代码的每个部分,以更好地理解。

代码的用法解释

在PHP的异常处理系统有四种基本部分:try,throw,catch,和Exception类。以下列表描述了各个部分的工作原理。

  • 上例中的division()函数检查除数是否等于零。如果是,则通过PHP的throw语句抛出异常。否则,此函数使用给定的数字执行除法并显示结果。

  • 然后,在try块中使用不同的参数调用division()函数。如果在try块中执行代码时生成异常,PHP将在该点停止执行并尝试查找相应的catch块。如果找到,则执行catch块中的代码,否则生成致命错误。

  • catch块通常捕获try块中抛出的异常,并创建一个包含异常信息的对象($e)。可以使用异常的getMessage()方法检索来自此对象的错误消息。

在PHP的异常类也提供了getCode(),getFile(),getLine()和getTraceAsString()可用于生成详细的调试信息的方法。

<?php
//关闭默认错误报告
error_reporting(0);
 
try{
    $file = "somefile.txt";
    
    //尝试打开文件
    $handle = fopen($file, "r");
    if(!$handle){
        throw new Exception("无法打开文件!", 5);
    }
    
    //尝试读取文件内容
    $content = fread($handle, filesize($file));
    if(!$content){
        throw new Exception("Could not read file!", 10);
    }
    
    //关闭文件句柄
    fclose($handle);
    
    //显示文件内容
    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的getCode()方法检索提供的异常代码。

提示:异常仅应用于表示特殊情况;它们不应用于指定正常的应用程序流程,例如,在特定位置跳转到脚本中的其他位置。这样做会不利地影响应用程序的性能。

定义自定义异常

您甚至可以定义自己的自定义异常处理程序,以不同的方式处理不同类型的异常。 它允许您为每种异常类型使用单独的catch块。
您可以通过扩展Exception类来定义自定义异常,因为Exception是所有异常的基类。 自定义异常类继承了PHP Exception类的所有属性和方法。 您还可以将自定义方法添加到自定义异常类。 让我们看一下以下示例:

<?php
//扩展Exception类
class EmptyEmailException extends Exception {}
class InvalidEmailException extends Exception {}
 
$email = "someuser@example..com";
 
try{
    //如果电子邮件为空,则抛出异常
    if($email == ""){
        throw new EmptyEmailException("<p>请输入您的电子邮件地址!</p>");
    }
    
    //如果电子邮件无效,则抛出异常
    if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {           
        throw new InvalidEmailException("<p><b>$email</b> 不是有效的电子邮件地址!</p>");
    }
    
    // 如果电子邮件有效,则显示成功消息
    echo "<p>成功:电子邮件验证成功.</p>";
} catch(EmptyEmailException $e){
    echo $e->getMessage();
} catch(InvalidEmailException $e){
    echo $e->getMessage();
}
?>

在上面的示例中,我们从Exception基类派生了两个新的异常类:EmptyEmailExceptionInvalidEmailException。 多个捕获块用于显示不同的错误消息,具体取决于生成的异常类型。

由于这些自定义异常类继承了Exception类的属性和方法,所以我们可以使用异常的类方法,如getMessage(),getLine(),getFile(),等来检索异常对象的错误信息。

设置全局异常处理程序

如本章前面所讨论的,如果未捕获到异常,则PHP会生成一条致命错误,并带有“ Uncaught Exception ...”消息。 此错误消息可能包含敏感信息,例如出现问题的文件名和行号。 如果您不想向用户公开此类信息,则可以创建一个自定义函数,并向set_exception_handler()函数注册该函数以处理所有未捕获的异常。

<?php
function handleUncaughtException($e){
    //向用户显示一般错误消息
    echo "哎呀!出了点问题。请重试,如果问题仍然存在,请与我们联系。";
    
    // 构造错误字符串
    $error = "Uncaught Exception: " . $message = date("Y-m-d H:i:s - ");
    $error .= $e->getMessage() . " in file " . $e->getFile() . " on line " . $e->getLine() . "\n";
    
    //在文件中记录错误的详细信息
    error_log($error, 3, "var/log/exceptionLog.log");
}
 
//注册自定义异常处理程序
set_exception_handler("handleUncaughtException");
 
// 抛出异常
throw new Exception("Testing Exception!");
?>

注意:未捕获的异常总是会导致脚本终止。 因此,如果希望脚本在异常发生点之后继续执行,则每个try块必须至少有一个对应的catch块。