当前位置:首页>php>PHP 异常体系设计:别再 try-catch 一把梭了

PHP 异常体系设计:别再 try-catch 一把梭了

  • 2026-10-21 13:26:58
PHP 异常体系设计:别再 try-catch 一把梭了

很多人写 PHP 异常处理就是 try-catch 一把梭,catch 里面 echo 个错误信息就完事了。

今天聊聊如何设计一个合理的异常体系。


PHP 内置异常层级

Throwable (interface)├── Error│   ├── ArithmeticError│   ├── AssertionError│   ├── TypeError│   └── ValueError└── Exception    ├── LogicException    │   ├── InvalidArgumentException    │   ├── OutOfRangeException    │   └── ...    └── RuntimeException        ├── OutOfBoundsException        ├── UnexpectedValueException        └── ...
  • Error:程序错误,通常不应该捕获
  • Exception:可预期的异常,应该捕获处理

设计自定义异常体系

基础异常类

namespaceApp\Exceptions;useException;useThrowable;abstractclassBaseExceptionextendsException{protectedarray $context = [];protected string $errorCode = '';publicfunction__construct(        string $message = '',        string $errorCode = '',        array $context = [],        int $code = 0,        ?Throwable $previous = null    ){parent::__construct($message, $code, $previous);$this->errorCode = $errorCode;$this->context = $context;    }publicfunctiongetErrorCode(): string{return$this->errorCode;    }publicfunctiongetContext(): array{return$this->context;    }publicfunctiontoArray(): array{return ['error_code' => $this->errorCode,'message' => $this->getMessage(),'context' => $this->context,        ];    }}

业务异常

// 业务逻辑异常classBusinessExceptionextendsBaseException{publicfunction__construct(        string $message,        string $errorCode = 'BUSINESS_ERROR',        array $context = []    ){parent::__construct($message, $errorCode, $context, 400);    }}// 资源不存在classNotFoundExceptionextendsBaseException{publicfunction__construct(string $resource, mixed $id = null){        $message = $id ? "{$resource} #{$id} 不存在" : "{$resource} 不存在";parent::__construct($message, 'NOT_FOUND', ['resource' => $resource, 'id' => $id], 404);    }}// 验证异常classValidationExceptionextendsBaseException{protectedarray $errors = [];publicfunction__construct(array $errors, string $message = '数据验证失败'){$this->errors = $errors;parent::__construct($message, 'VALIDATION_ERROR', ['errors' => $errors], 422);    }publicfunctiongetErrors(): array{return$this->errors;    }}// 认证异常classAuthenticationExceptionextendsBaseException{publicfunction__construct(string $message = '请先登录'){parent::__construct($message, 'UNAUTHENTICATED', [], 401);    }}// 授权异常classAuthorizationExceptionextendsBaseException{publicfunction__construct(string $message = '没有权限执行此操作'){parent::__construct($message, 'UNAUTHORIZED', [], 403);    }}

使用异常

classUserService{publicfunctionfind(int $id): User{        $user = $this->repository->find($id);if ($user === null) {thrownew NotFoundException('用户', $id);        }return $user;    }publicfunctioncreate(array $data): User{        $errors = $this->validate($data);if (!empty($errors)) {thrownew ValidationException($errors);        }if ($this->repository->existsByEmail($data['email'])) {thrownew BusinessException('邮箱已被注册','EMAIL_EXISTS',                ['email' => $data['email']]            );        }return$this->repository->create($data);    }publicfunctiondelete(int $id, User $operator): void{        $user = $this->find($id);if (!$operator->canDelete($user)) {thrownew AuthorizationException('您没有权限删除此用户');        }$this->repository->delete($id);    }}

全局异常处理器

classExceptionHandler{publicfunctionhandle(Throwable $e): void{$this->log($e);if ($e instanceof ValidationException) {$this->respondValidationError($e);        } elseif ($e instanceof NotFoundException) {$this->respondNotFound($e);        } elseif ($e instanceof AuthenticationException) {$this->respondUnauthenticated($e);        } elseif ($e instanceof BusinessException) {$this->respondBusinessError($e);        } else {$this->respondInternalError($e);        }    }privatefunctionrespondValidationError(ValidationException $e): void{$this->jsonResponse(['code' => $e->getErrorCode(),'message' => $e->getMessage(),'errors' => $e->getErrors(),        ], 422);    }privatefunctionrespondInternalError(Throwable $e): void{        $message = getenv('APP_DEBUG') ? $e->getMessage() : '服务器内部错误';$this->jsonResponse(['code' => 'INTERNAL_ERROR','message' => $message,        ], 500);    }privatefunctionjsonResponse(array $data, int $statusCode): void{        http_response_code($statusCode);        header('Content-Type: application/json; charset=utf-8');echo json_encode($data, JSON_UNESCAPED_UNICODE);exit;    }}// 注册全局异常处理set_exception_handler([new ExceptionHandler(), 'handle']);

异常处理最佳实践

❌ 不要吞掉异常

// 错误:吞掉异常try {$this->doSomething();} catch (Exception $e) {// 什么都不做}// 正确:至少记录日志try {$this->doSomething();} catch (Exception $e) {$this->logger->error($e->getMessage());throw $e;}

❌ 不要用异常控制流程

// 错误:用异常控制流程try {    $user = $this->repository->find($id);} catch (NotFoundException $e) {    $user = $this->createDefaultUser();}// 正确:用返回值$user = $this->repository->find($id);if ($user === null) {    $user = $this->createDefaultUser();}

✅ 捕获具体异常

// 错误:捕获所有异常try {$this->process();} catch (Exception $e) {// 处理}// 正确:捕获具体异常try {$this->process();} catch (ValidationException $e) {// 处理验证错误} catch (NotFoundException $e) {// 处理资源不存在}

✅ 使用 finally 清理资源

$file = fopen('data.txt', 'r');try {$this->processFile($file);} catch (Exception $e) {$this->logger->error($e->getMessage());throw $e;} finally {    fclose($file); // 无论是否异常都会执行}

总结

异常体系设计要点:

  • ✅ 建立清晰的异常层级
  • ✅ 异常携带足够的上下文信息
  • ✅ 使用错误码便于前端处理
  • ✅ 全局异常处理器统一处理
  • ✅ 生产环境不暴露敏感信息

下一篇我们聊聊生产环境的错误日志最佳实践。

最新文章

随机文章