0Pricing
PHP Academy · 课时

创建自定义异常

扩展 Exception 类,构建特定领域的错误类型。

创建自定义异常 是 CoddyKit 上的免费 PHP Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 PHP Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 PHP Academy 课程共包含 4 节课。

为什么要使用自定义异常?

自定义异常可以让您:

  • 将应用程序错误与通用 PHP 错误区分开
  • 携带特定领域的数据(订单 ID、用户 ID 等)
  • 在不同层中捕获特定的错误类型
  • 创建有意义且能自我说明的错误层次结构

扩展 Exception

通过扩展 Exception 基类创建自定义异常:

<?php
class ValidationException extends \Exception
{
    private array $errors;

    public function __construct(array $errors, string $message = '', int $code = 0)
    {
        $this->errors = $errors;
        parent::__construct($message ?: implode(', ', $errors), $code);
    }

    public function getErrors(): array
    {
        return $this->errors;
    }
}

使用自定义异常

抛出并捕获您的自定义异常:

<?php
function createUser(array $data): void {
    $errors = [];
    if (empty($data['email'])) $errors[] = 'Email required';
    if (empty($data['name']))  $errors[] = 'Name required';

    if ($errors) {
        throw new ValidationException($errors);
    }
    // save user...
}

try {
    createUser([]);
} catch (ValidationException $e) {
    foreach ($e->getErrors() as $err) {
        echo '- ' . $err . PHP_EOL;
    }
}

异常层次结构设计

为您的领域设计层次结构:

<?php
// Base exception for your app
class AppException extends \RuntimeException {}

// Domain-specific exceptions
class NotFoundException extends AppException {}
class AuthException extends AppException {}
class PaymentException extends AppException {}

// Specific payment errors
class InsufficientFundsException extends PaymentException {
    public function __construct(public readonly float $balance, public readonly float $required) {
        parent::__construct("Insufficient funds: have $balance, need $required");
    }
}

按层次结构捕获

捕获基类即可处理所有派生异常:

<?php
try {
    processPayment($order);
} catch (InsufficientFundsException $e) {
    echo 'Low balance: need ' . $e->required;
} catch (PaymentException $e) {
    echo 'Payment failed: ' . $e->getMessage();
} catch (AppException $e) {
    echo 'App error: ' . $e->getMessage();
} catch (\Throwable $e) {
    // Last resort
    error_log($e);
}

为异常添加上下文

为异常添加额外数据,以便调试:

<?php
class HttpException extends \RuntimeException
{
    public function __construct(
        private int    $statusCode,
        string         $message = '',
        ?\Throwable    $previous = null
    ) {
        parent::__construct($message, $statusCode, $previous);
    }

    public function getStatusCode(): int
    {
        return $this->statusCode;
    }
}

HTTP 异常示例

在 Web 应用程序中使用 HTTP 异常:

<?php
try {
    $user = findUserById($id);
    if (!$user) throw new HttpException(404, 'User not found');
    if (!$user->canAccess($resource)) {
        throw new HttpException(403, 'Access denied');
    }
} catch (HttpException $e) {
    http_response_code($e->getStatusCode());
    echo json_encode(['error' => $e->getMessage()]);
}

基于接口的异常

使用接口将互不相关的异常类归为一组:

<?php
interface UserFacingException
{
    public function getUserMessage(): string;
}

class ValidationException extends \Exception implements UserFacingException
{
    public function getUserMessage(): string
    {
        return 'Please check your input: ' . $this->getMessage();
    }
}

// In controller:
catch (UserFacingException $e) {
    echo $e->getUserMessage();
}

命名构造器模式

使用静态工厂方法处理常见的异常场景:

<?php
class OrderException extends \RuntimeException
{
    public static function notFound(int $id): self
    {
        return new self("Order #$id not found", 404);
    }

    public static function alreadyShipped(int $id): self
    {
        return new self("Order #$id already shipped", 409);
    }
}

throw OrderException::notFound($orderId);

序列化异常

将异常数据转换为数组,用于日志记录或 API 响应:

<?php
function exceptionToArray(\Throwable $e): array {
    return [
        'type'    => get_class($e),
        'message' => $e->getMessage(),
        'code'    => $e->getCode(),
        'file'    => $e->getFile(),
        'line'    => $e->getLine(),
        'trace'   => $e->getTraceAsString(),
        'previous' => $e->getPrevious()
            ? exceptionToArray($e->getPrevious())
            : null,
    ];
}

自定义异常最佳实践

设计自定义异常的指南:

  • 对于意外情况,扩展 RuntimeException
  • 对于程序员错误(无效参数),扩展 LogicException
  • 不要为每种可能的消息都创建一个异常类 — 应改用代码
  • 异常类名称使用 PascalCase,并以 Exception 结尾
  • 记录每个方法可能抛出的异常

快速检查

要创建表示代码中逻辑错误的自定义异常,应扩展哪个 PHP 基类?

回顾:自定义异常

自定义异常总结:

  • 扩展 Exception 或其子类
  • 添加特定领域的属性和方法
  • 设计层次结构 — 捕获父类即可捕获所有子类
  • 使用接口将互不相关的异常归为一组
  • 使用静态命名构造器提高可读性
  • 始终调用 parent::__construct()

常见问题解答

「创建自定义异常」课时是免费的吗?

是的 — 「创建自定义异常」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 PHP Academy 课程的其余内容,请升级到 CoddyKit PRO。 PHP Academy 课程共包含 4 节课。

「创建自定义异常」这节课中我会学到什么?

扩展 Exception 类,构建特定领域的错误类型。 你通过在浏览器中直接运行的动手代码来练习 PHP Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 PHP Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 PHP Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「创建自定义异常」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 PHP Academy 课中编写并运行代码吗?

能。每节 PHP Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. PHP 错误类型与报告
  2. Try、Catch 与 Finally
  3. 创建自定义异常
  4. 记录错误与最佳实践
← 返回 PHP Academy