PHP 异常处理机制详解:Try-Catch 实战与最佳实践
在 PHP 开发中,异常(Exception)是处理运行时错误的标准方式,它允许程序在遇到错误时改变正常流程并优雅地恢复或终止。本文将介绍如何使用 try-catch 结构捕获异常,如何扩展 Exception 类创建自定义异常处理器,以及如何设置全局异常处理函数。
什么是 PHP 异常
异常用于在指定的错误发生时改变脚本的正常流程。与传统的错误警告不同,异常提供了一种面向对象的错误处理方法。当异常被触发时,当前代码状态会被保存,执行流会切换到预定义的异常处理器。如果异常未被捕获且未设置顶层处理器,将导致严重的致命错误(Fatal Error)并输出 "Uncaught Exception" 消息。
基本异常处理:Try、Throw 和 Catch
要避免未捕获异常导致的致命错误,必须使用适当的代码来处理异常。核心结构包括:Try 包含可能抛出异常的代码;Throw 用于在特定条件下触发异常;Catch 用于捕获异常并处理错误信息。每个 throw 必须对应至少一个 catch。
第一步:实现基础 Try-Catch 逻辑
目的:创建一个检查数字范围的函数,并在数值不合法时抛出异常,随后在调用处捕获该异常以防止脚本崩溃。
<?php
//创建可抛出一个异常的函数
function checkNum($number) {
if($number > 1) {
throw new Exception("Value must be 1 or below");
}
return true;
}
//在 “try” 代码块中触发异常
try {
checkNum(2);
// If the exception is thrown, this text will not be shown
echo 'If you see this, the number is 1 or below';
}
//捕获异常
catch(Exception $e) {
echo 'Message: ' .$e->getMessage();
}
?>
上述代码将输出 Message: Value must be 1 or below。执行流程为:checkNum(2) 检测到值大于 1,抛出异常;try 块中断执行,转入 catch 块;catch 块获取异常对象 $e 并通过 getMessage() 输出错误信息。
创建自定义异常类
虽然可以使用内置的 Exception 类,但在实际应用中,通常需要根据业务逻辑定制异常行为。可以通过创建一个专门的新类来扩展 Exception 类。该类继承所有父级属性和方法,并可添加自定义函数以提供更详细的错误上下文。
第二步:定义自定义异常类并处理无效邮件地址
目的:创建一个 customException 类,添加 errorMessage() 方法以格式化包含文件名、行号和具体消息的错误输出。
<?php
class customException extends Exception {
public function errorMessage() {
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile() .': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = "someone@example…com";
try {
//check if
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
//throw exception if email is not valid
throw new customException($email);
}
}
catch (customException $e)
{
//display custom message
echo $e->errorMessage();
}
?>
这里 customException 继承了 Exception 的所有方法,如 getLine()、getFile() 和 getMessage()。当 $email 不合法时,抛出 customException 并被捕获,显示格式化的错误消息。
处理多个异常
一段脚本可能需要检测多种情况。可以使用多个 catch 代码块来捕获不同类型的异常。PHP 会按顺序匹配 catch 块,一旦匹配成功,其他 catch 块将被忽略。此外,也可以嵌套 try-catch 结构或使用 switch/if-else 组合判断。
第三步:嵌套异常与多重捕获
目的:测试两个条件,分别使用不同的异常类型,演示如何区分处理。
<?php
class customException extends Exception{
public function errorMessage(){
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile().': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = "someone@example.com";
try {
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
throw new customException($email);
}
//check for “example” in mail address
if(strpos($email, “example”) !== FALSE) {
throw new Exception("$email is an example e-mail");
}
}catch (customException $e) {
echo $e->errorMessage();
}
catch(Exception $e) {
echo $e->getMessage();
}
?>
由于 $email 不包含非法字符但含有 “example”,第一个 if 不触发,第二个 if 触发基础 Exception。因为 customException 未被触发,所以进入第二个 catch 块,输出基础异常消息。若没有捕获 customException,仅捕获 base exception,则在那里处理异常。
重新抛出异常
有时需要在 catch 块中对异常进行二次处理(即重新抛出),例如将底层的系统错误转换为对用户更友好的业务错误提示。这通常在嵌套的 try-catch 结构中完成。
第四步:嵌套 Try-Catch 实现异常重抛
目的:内部 try 捕获普通 Exception 并重抛为 customException,外层再捕获 customException 显示友好消息。
<?php
class customException extends Exception {
public function errorMessage() {
$errorMsg = $this->getMessage().' is not a valid E-Mail address.';
return $errorMsg;
}
}
$email = “someone@example.com”;
try {
try {
if(strpos($email, “example”) !== FALSE) {
throw new Exception($email);
}
} catch(Exception $e) {
//re-throw exception
throw new customException($email);
}
}catch (customException $e) {
//display custom message
echo $e->errorMessage();
}
?>
内部 try 捕获到 $email 包含 “example” 引发的异常,随即将其包装为 customException 重抛。外层 catch 捕获该重抛的异常并执行 errorMessage()。如果内层未被捕获,异常将向更高层级查找 catch 块。
设置顶层异常处理器
为了兜底,可以设置一个用户定义的函数来处理所有未被捕获的异常。使用 set_exception_handler() 函数可以实现这一点。这在第三方库或未预期错误场景中尤为重要,能防止脚本直接崩溃而不留痕迹。
第五步:配置全局未捕获异常处理函数
目的:定义 myException 函数,使其成为所有未捕获异常的默认处理入口。
<?php
function myException($exception){
echo “<b>Exception:</b> ” , $exception->getMessage();
}
set_exception_handler('myException');
throw new Exception('Uncaught Exception occurred');
?>
运行此代码,虽然不存在 catch 块,但触发的异常会被 myException 处理,输出 Exception: Uncaught Exception occurred。这是捕获所有漏掉错误的最后防线。
关键规则总结
- 所有可能抛出异常的代码必须放入
try代码块。 - 每个
try或throw必须至少拥有一个对应的catch代码块。 - 可以使用多个
catch块来捕获不同种类的异常,按顺序匹配。 - 可以在
catch块中再次抛出(re-thrown)异常,以改变处理方式。 - 简而言之:如果抛出了异常,就必须捕获它,否则应由顶层处理器接管。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













