本文目录导读:

在 PHP 项目中,异常(Exception) 和 finally 是错误处理的关键机制,以下是它们的核心概念、用法及最佳实践。
异常(Exception)
基本结构
try {
// 可能抛出异常的代码
if ($somethingWrong) {
throw new Exception("Something went wrong");
}
} catch (Exception $e) {
// 处理异常
echo "Caught: " . $e->getMessage();
}
异常类层级
- PHP 内置异常:
Exception、RuntimeException、InvalidArgumentException等 - 自定义异常类(推荐继承
Exception)
class ValidationException extends Exception {}
class DatabaseException extends Exception {}
多 catch 捕获
try {
// ...
} catch (ValidationException $e) {
// 处理验证错误
} catch (DatabaseException $e) {
// 处理数据库错误
} catch (Exception $e) {
// 兜底处理
}
finally 块
作用
无论是否抛出异常,finally 块中的代码必定执行,常用于:
- 关闭数据库连接
- 释放文件句柄
- 恢复全局状态
- 记录日志
语法
try {
// 可能异常的代码
} catch (Exception $e) {
// 异常处理
} finally {
// 总是执行,用于清理资源
$db->close();
}
示例:文件操作
function readFile(string $path): string {
$handle = fopen($path, 'r');
if (!$handle) {
throw new RuntimeException("Cannot open file");
}
try {
$content = fread($handle, filesize($path));
return $content;
} finally {
fclose($handle); // 确保文件关闭
}
}
finally 与 return 的交互
关键规则
- finally 中的 return 会覆盖 try/catch 中的 return
- finally 中无 return,则返回 try/catch 中的值
function test(): string {
try {
return "from try";
} finally {
return "from finally"; // 最终返回这个
}
}
echo test(); // 输出: from finally
最佳实践
- 不要在 finally 中使用 return(除非有明确意图)
- 避免在 finally 中抛出异常(会覆盖原始异常)
异常与 finally 的典型场景
数据库事务
$db->beginTransaction();
try {
$db->query("UPDATE users SET ...");
$db->query("INSERT INTO logs ...");
$db->commit();
} catch (Exception $e) {
$db->rollBack();
throw $e; // 重新抛出
} finally {
$db->close(); // 确保连接释放
}
API 响应封装
function apiCall(string $url): array {
$ch = curl_init($url);
try {
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new CurlException(curl_error($ch));
}
return json_decode($response, true);
} finally {
curl_close($ch); // 总是关闭 curl 句柄
}
}
PHP 8+ 新特性
匹配表达式(Match Expression)
- 不直接处理异常,但可用于条件判断后抛异常
构造器属性提升 + 异常
class UserController {
public function __construct(
private UserRepository $repo
) {}
public function show(int $id): User {
$user = $this->repo->find($id);
if (!$user) {
throw new NotFoundException("User not found");
}
return $user;
}
}
常见陷阱与最佳实践
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 吞掉异常(catch 空) | catch (Exception $e) {} 隐藏错误 |
至少记录日志或重新抛出 |
| finally 中抛出异常 | 会覆盖原始异常 | 用 try-catch 包裹 finally 代码 |
| 过度使用异常做流程控制 | 性能差且难以维护 | 只用于真正的错误情况 |
| 未捕获的异常导致 500 | 生产环境应设置全局异常处理器 | 使用 set_exception_handler() |
全局异常处理器示例
set_exception_handler(function (Throwable $e) {
// 生产环境记录日志,返回友好错误页面
error_log($e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Internal Server Error']);
});
- 异常:用于处理运行时错误,通过 try-catch 捕获
- finally:无论是否异常,保证资源清理代码执行
- 组合使用:数据库事务、文件操作、API 请求等需要资源管理的场景
- 避免陷阱:不在 finally 中 return/throw,不吞掉异常
合理使用异常和 finally,能让 PHP 项目更加健壮、可维护。