本文目录导读:

设计一个优秀的 PHP 异常类体系是构建健壮应用的关键,下面提供一个从基础到进阶的完整设计指南。
基础异常类设计
核心异常基类
<?php
namespace App\Exceptions;
use RuntimeException;
use Throwable;
/**
* 应用基础异常类
* 所有业务异常都应继承此类
*/
class AppException extends RuntimeException
{
/**
* 业务错误码
* @var string|int
*/
protected $errorCode;
/**
* 错误上下文数据
* @var array
*/
protected $context = [];
/**
* HTTP 状态码
* @var int
*/
protected $statusCode = 500;
/**
* 是否可重试
* @var bool
*/
protected $retryable = false;
/**
* 重试延迟(秒)
* @var int|null
*/
protected $retryAfter = null;
/**
* @param string $message 错误消息
* @param string|int $errorCode 业务错误码
* @param int $statusCode HTTP状态码
* @param array $context 附加上下文
* @param Throwable|null $previous 前一个异常
*/
public function __construct(
string $message = "",
$errorCode = 'APP_ERROR',
int $statusCode = 500,
array $context = [],
?Throwable $previous = null
) {
$this->errorCode = $errorCode;
$this->statusCode = $statusCode;
$this->context = $context;
parent::__construct($message, $statusCode, $previous);
}
/**
* 获取业务错误码
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* 获取HTTP状态码
*/
public function getStatusCode(): int
{
return $this->statusCode;
}
/**
* 获取上下文数据
*/
public function getContext(): array
{
return $this->context;
}
/**
* 添加上下文数据(链式调用)
*/
public function addContext(string $key, $value): self
{
$this->context[$key] = $value;
return $this;
}
/**
* 获取所有错误信息
*/
public function toArray(): array
{
return [
'message' => $this->getMessage(),
'code' => $this->getErrorCode(),
'status' => $this->getStatusCode(),
'context' => $this->getContext(),
'file' => $this->getFile(),
'line' => $this->getLine(),
'trace' => $this->getTraceAsString(),
];
}
/**
* 设置是否可重试
*/
public function setRetryable(bool $retryable, ?int $retryAfter = null): self
{
$this->retryable = $retryable;
$this->retryAfter = $retryAfter;
return $this;
}
/**
* 是否可重试
*/
public function isRetryable(): bool
{
return $this->retryable;
}
/**
* 重试等待时间
*/
public function getRetryAfter(): ?int
{
return $this->retryAfter;
}
}
分类异常类
业务逻辑异常
<?php
namespace App\Exceptions;
/**
* 业务逻辑异常
* 用于处理业务规则不符的情况
*/
class BusinessException extends AppException
{
/**
* 业务规则名称
*/
protected $rule;
public function __construct(
string $message,
string $rule,
$errorCode = 'BUSINESS_ERROR',
array $context = [],
?Throwable $previous = null
) {
$this->rule = $rule;
parent::__construct($message, $errorCode, 400, $context, $previous);
}
public function getRule(): string
{
return $this->rule;
}
}
/**
* 资源未找到异常
*/
class ResourceNotFoundException extends AppException
{
public function __construct(
string $resource,
$identifier = null,
array $context = []
) {
$message = "Resource '{$resource}'";
if ($identifier !== null) {
$message .= " with identifier '{$identifier}'";
}
$message .= " not found.";
$context['resource'] = $resource;
$context['identifier'] = $identifier;
parent::__construct($message, 'RESOURCE_NOT_FOUND', 404, $context);
}
}
/**
* 权限不足异常
*/
class PermissionDeniedException extends AppException
{
public function __construct(
string $permission,
array $context = []
) {
$message = "Permission denied: {$permission}";
$context['permission'] = $permission;
parent::__construct($message, 'PERMISSION_DENIED', 403, $context);
}
}
数据验证异常
<?php
namespace App\Exceptions;
/**
* 数据验证异常
* 用于处理输入验证失败
*/
class ValidationException extends AppException
{
/**
* 验证错误集合
* @var array
*/
protected $errors = [];
public function __construct(
array $errors = [],
string $message = "Validation failed.",
?Throwable $previous = null
) {
$this->errors = $errors;
parent::__construct($message, 'VALIDATION_ERROR', 422, ['errors' => $errors], $previous);
}
/**
* 获取所有验证错误
*/
public function getErrors(): array
{
return $this->errors;
}
/**
* 获取指定字段的错误
*/
public function getFieldErrors(string $field): array
{
return $this->errors[$field] ?? [];
}
/**
* 添加字段错误
*/
public function addFieldError(string $field, string $error): self
{
$this->errors[$field][] = $error;
$this->context['errors'] = $this->errors;
return $this;
}
}
外部服务异常
<?php
namespace App\Exceptions;
use Throwable;
/**
* 外部服务异常
* 用于处理第三方服务调用失败
*/
class ExternalServiceException extends AppException
{
const RETRYABLE = 'retryable';
const NON_RETRYABLE = 'non_retryable';
/**
* 服务名称
*/
protected $service;
/**
* 请求信息
*/
protected $request;
/**
* 响应信息
*/
protected $response;
public function __construct(
string $service,
string $message = "External service error.",
string $request = null,
string $response = null,
bool $retryable = false,
int $statusCode = 502,
?Throwable $previous = null
) {
$this->service = $service;
$this->request = $request;
$this->response = $response;
parent::__construct(
$message,
'EXTERNAL_SERVICE_ERROR',
$statusCode,
[
'service' => $service,
'request' => $request,
'response' => $response,
],
$previous
);
if ($retryable) {
$this->setRetryable(true);
}
}
/**
* 获取服务名称
*/
public function getService(): string
{
return $this->service;
}
/**
* 获取请求内容
*/
public function getRequest(): ?string
{
return $this->request;
}
/**
* 获取响应内容
*/
public function getResponse(): ?string
{
return $this->response;
}
}
高级异常处理机制
异常处理注册器
<?php
namespace App\Exceptions;
use Throwable;
/**
* 异常处理注册器
* 提供异常处理策略的注册和管理
*/
class ExceptionHandler
{
/**
* 异常处理策略集合
* @var callable[]
*/
protected $handlers = [];
/**
* 默认处理策略
* @var callable
*/
protected $defaultHandler;
/**
* 异常订阅者
* @var array
*/
protected $subscribers = [];
/**
* 注册处理策略
*/
public function register(string $exceptionClass, callable $handler): self
{
$this->handlers[$exceptionClass] = $handler;
return $this;
}
/**
* 注册默认处理策略
*/
public function setDefaultHandler(callable $handler): self
{
$this->defaultHandler = $handler;
return $this;
}
/**
* 注册异常订阅者
*/
public function subscribe(callable $subscriber): self
{
$this->subscribers[] = $subscriber;
return $this;
}
/**
* 执行处理
*/
public function handle(Throwable $exception)
{
// 寻找最匹配的处理策略
$handler = $this->findHandler($exception);
// 执行订阅者,通知异常发生
foreach ($this->subscribers as $subscriber) {
$subscriber($exception);
}
return $handler($exception);
}
/**
* 寻找最合适的处理策略
*/
protected function findHandler(Throwable $exception): callable
{
foreach ($this->handlers as $exceptionClass => $handler) {
if ($exception instanceof $exceptionClass) {
return $handler;
}
}
return $this->defaultHandler ?? function ($e) {
throw $e;
};
}
/**
* 批量注册常用异常处理
*/
public function registerDefaults()
{
$this->register(ValidationException::class, function ($e) {
// 返回422响应
return [
'status' => 422,
'errors' => $e->getErrors(),
];
});
$this->register(ResourceNotFoundException::class, function ($e) {
// 返回404响应
return [
'status' => 404,
'error' => $e->getMessage(),
];
});
// 更多默认处理...
}
}
重试机制
<?php
namespace App\Exceptions;
use Throwable;
/**
* 异常重试执行器
*/
class RetryExecutor
{
/**
* @var int 最大重试次数
*/
protected int $maxAttempts;
/**
* @var int 基础延迟(秒)
*/
protected int $baseDelay;
/**
* @var int 最大延迟(秒)
*/
protected int $maxDelay;
/**
* @var bool 是否指数退避
*/
protected bool $exponentialBackoff;
/**
* @var callable 重试判断回调
*/
protected $shouldRetryCallback;
public function __construct(
int $maxAttempts = 3,
int $baseDelay = 1,
int $maxDelay = 10,
bool $exponentialBackoff = true
) {
$this->maxAttempts = $maxAttempts;
$this->baseDelay = $baseDelay;
$this->maxDelay = $maxDelay;
$this->exponentialBackoff = $exponentialBackoff;
}
/**
* 执行带重试机制的操作
*/
public function execute(callable $operation)
{
$attempts = 0;
while ($attempts < $this->maxAttempts) {
try {
return $operation();
} catch (AppException $exception) {
$attempts++;
// 判断是否应该重试
if (!$this->shouldRetry($exception, $attempts)) {
throw $exception;
}
$this->sleep($this->getDelay($attempts));
}
}
throw new \RuntimeException("Operation failed after {$this->maxAttempts} attempts.");
}
/**
* 判断是否需要重试
*/
protected function shouldRetry(AppException $exception, int $attempts): bool
{
if ($this->shouldRetryCallback) {
return call_user_func($this->shouldRetryCallback, $exception, $attempts);
}
return $exception->isRetryable() && $attempts < $this->maxAttempts;
}
/**
* 设置自定义重试判断函数
*/
public function setShouldRetryCallback(callable $callback): self
{
$this->shouldRetryCallback = $callback;
return $this;
}
/**
* 获取延迟时间
*/
protected function getDelay(int $attempts): int
{
$delay = $this->baseDelay;
if ($this->exponentialBackoff) {
$delay = min(
$this->maxDelay,
$this->baseDelay * pow(2, $attempts - 1)
);
}
return $delay;
}
/**
* 延迟执行,支持纳秒级精度
*/
protected function sleep(int $seconds): void
{
usleep($seconds * 1000000);
}
/**
* 带抖动的延迟执行
*/
protected function sleepWithJitter(int $seconds): void
{
$jitter = random_int(0, $seconds * 100) / 100;
usleep(($seconds + $jitter) * 1000000);
}
}
使用示例
<?php
// 创建自定义业务异常
class UserNotFoundException extends ResourceNotFoundException
{
public function __construct(int $userId)
{
parent::__construct('user', $userId);
}
}
// 在业务逻辑中使用
class UserService
{
public function getUser(int $id): User
{
try {
$user = $this->userRepository->find($id);
if (!$user) {
throw new UserNotFoundException($id);
}
if (!$user->isActive()) {
throw new BusinessException(
'User is not active.',
'user.inactive'
);
}
return $user;
} catch (UserNotFoundException $e) {
// 记录日志,可能重新包装异常
throw $e->addContext('operation', 'getUser');
} catch (\Exception $e) {
// 处理意外异常
throw $e;
}
}
public function updateUser(int $id, array $data): User
{
// 验证数据
$validator = new Validator();
if (!$validator->validate($data)) {
throw new ValidationException($validator->getErrors());
}
// ...
}
public function callExternalApi()
{
// 使用重试机制
$executor = new RetryExecutor(3, 1, 10);
return $executor->execute(function () {
try {
$this->externalApi->request();
} catch (ExternalServiceException $e) {
// 设置可重试标志
throw $e->setRetryable(true, 5);
}
});
}
}
// 应用入口处理
$handler = new ExceptionHandler();
$handler->registerDefaults();
try {
$app->run();
} catch (Throwable $e) {
$handler->handle($e);
}
最佳实践
异常类设计原则
- 层次化设计 - 异常类应该有清晰的继承层次
- 语义清晰 - 异常名称应该准确描述问题
- 携带上下文 - 异常应该包含足够的调试信息
- 一致性 - 错误码和格式应该统一
- 可预测性 - 避免在构造中抛出异常
错误处理策略
- 快速失败:验证失败立即抛出异常
- 优雅降级:外部服务失败时提供降级方案
- 重试机制:对可恢复错误提供重试支持
- 日志记录:所有异常都应该有完整的日志记录
- 监控告警:关键异常应该触发告警
性能考虑
- 避免使用异常作为控制流
- 在异常中只包含必要的信息
- 考虑使用异常池或复用对象
- 评估异常处理性能开销
这个设计提供了一个全面、健壮的异常处理体系,能够满足从简单到复杂的大部分应用需求,核心优势在于:
- 清晰的分层结构 - 容易扩展和维护
- 丰富的上下文信息 - 便于调试和日志记录
- 灵活的处理机制 - 支持自定义处理和重试策略
- 良好的可测试性 - 易于单元测试和集成测试
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播