本文目录导读:

- 基础:try-catch-finally 结构
- 自定义异常类
- set_exception_handler 全局异常处理
- 借助 Error 类处理致命错误
- 真实项目中的最佳实践
- PHP 8 的新特性:Match 表达式与异常的配合
- 推荐的处理流程
在PHP项目中处理异常是一个非常重要的技能,它能帮助你的代码更健壮、更易维护,下面我会从基础到实践,系统地讲解PHP异常处理的几种方式。
基础:try-catch-finally 结构
这是最核心的异常处理方式。
try {
// 可能抛出异常的代码
$result = 10 / 0; // 除零错误
} catch (DivisionByZeroError $e) {
// 捕获特定类型的异常
echo "数学错误: " . $e->getMessage();
} catch (Exception $e) {
// 捕获其他所有异常(更宽泛的类型)
echo "其他异常: " . $e->getMessage();
} finally {
// 无论是否发生异常都会执行(可选)
echo "清理资源...";
}
重要提示:PHP 7.1+ 支持 finally 块,用于释放资源(关闭文件、数据库连接等)。
自定义异常类
在实际项目中,你可能会需要创建自己的异常类型,方便区分不同的错误场景。
class ValidationException extends Exception {}
class DatabaseException extends Exception {}
class UserService {
public function createUser($data) {
if (empty($data['email'])) {
throw new ValidationException('邮箱不能为空');
}
try {
// 数据库操作...
} catch (PDOException $e) {
// 将底层异常包装成业务异常
throw new DatabaseException('用户创建失败', 0, $e);
}
}
}
为什么这样做?
- 业务逻辑层不需要知道底层数据库的细节
- 上层代码可以按业务异常类型统一处理
set_exception_handler 全局异常处理
当你没有使用 try-catch 捕获异常时,PHP会调用这个全局处理器。
// 在项目入口文件(如 index.php)设置
set_exception_handler(function($exception) {
// 记录错误日志
error_log($exception->getMessage() . ' in ' . $exception->getFile() . ':' . $exception->getLine());
// 根据环境返回不同响应
if (getenv('APP_ENV') === 'production') {
http_response_code(500);
echo json_encode(['error' => '服务器内部错误']);
} else {
echo "未捕获的异常: " . $exception->getMessage();
}
});
// 故意抛出一个未捕获的异常
throw new Exception("测试全局异常处理器");
适用场景:
- API接口的统一错误响应
- 生产环境的错误屏蔽和日志记录
借助 Error 类处理致命错误
传统 Exception 无法捕获 PHP 的致命错误(如内存耗尽、类型错误等),从 PHP 7 开始,Error 类可以处理这些。
try {
// 故意触发类型错误
$obj = new stdClass();
$obj->method(); // 调用不存在的方法
} catch (Error $e) {
echo "捕获到致命错误: " . $e->getMessage();
}
注意:Error 和 Exception 都实现了 Throwable 接口,你可以统一捕获 Throwable。
try {
// 任何异常或错误
} catch (Throwable $t) {
echo "捕获所有错误/异常: " . $t->getMessage();
}
真实项目中的最佳实践
1 分层处理
Controller层 → Service层 → Repository层
- Repository层:捕获底层数据库异常,抛出自定义
DatabaseException - Service层:捕获业务逻辑异常,抛出
BusinessException或ValidationException - Controller层:使用
try-catch捕获所有异常,返回友好的HTTP响应
// Controller示例
public function createUserAction(Request $request) {
try {
$userService = new UserService();
$user = $userService->createUser($request->getBody());
return $this->json(['data' => $user], 201);
} catch (ValidationException $e) {
return $this->json(['error' => $e->getMessage()], 400);
} catch (DatabaseException $e) {
// 记录详细日志给运维,但返回通用信息给用户
Logger::error($e->getMessage(), ['trace' => $e->getTraceAsString()]);
return $this->json(['error' => '服务暂时不可用'], 500);
} catch (Throwable $t) {
Logger::critical($t->getMessage());
return $this->json(['error' => '未知错误'], 500);
}
}
2 日志记录
无论是否在开发环境,都应该记录异常日志。
try {
// 业务逻辑
} catch (Exception $e) {
// 使用 Monolog 或其他日志库
$logger->error($e->getMessage(), [
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
]);
// 重新抛出或返回错误响应
}
3 不要吞没异常
// ❌ 错误:空 catch 块会隐藏问题
try {
riskyOperation();
} catch (Exception $e) {
// 什么都不做——这样很难调试
}
// ✅ 正确:至少记录日志
try {
riskyOperation();
} catch (Exception $e) {
error_log($e->getMessage());
// 如果无法恢复,应该重新抛出或返回错误
throw $e;
}
4 使用异常链保留上下文
try {
connectDatabase();
queryUsers();
} catch (PDOException $e) {
// 把底层异常作为第三个参数传入,保留调用栈
throw new ServiceException('数据库操作失败', 500, $e);
}
这样上层可以同时看到业务错误信息和底层技术细节(在日志中)。
PHP 8 的新特性:Match 表达式与异常的配合
PHP 8 的 match 表达式在异常处理中也很有用:
function handleHttpCode(int $code): string {
return match ($code) {
400 => throw new BadRequestException(),
404 => throw new NotFoundException(),
500 => throw new InternalServerErrorException(),
default => '正常'
};
}
推荐的处理流程
- 定义自定义异常类:
ValidationException、DatabaseException、AuthException等 - 在业务层抛出特定异常,不要抛出通用
Exception - 在控制器或入口用
try-catch捕获,并决定是继续抛出还是返回HTTP响应 - 设置全局异常处理器 兜底未捕获的异常
- 始终记录日志,尤其是在生产环境
这样,你的PHP项目无论在开发还是生产环境,都能清晰、优雅地处理各种异常情况。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播