0Pricing
PHP Academy · Урок

Создание пользовательских исключений

Расширяйте класс Exception, чтобы создавать типы ошибок для конкретной предметной области

«Создание пользовательских исключений» — бесплатный урок PHP Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения PHP Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс PHP Academy содержит 4 уроков всего.

Зачем нужны собственные исключения?

Собственные исключения позволяют:

  • Отличать ошибки приложения от общих ошибок PHP
  • Хранить данные, относящиеся к предметной области (идентификатор заказа, идентификатор пользователя и т. д.)
  • Перехватывать конкретные типы ошибок на разных уровнях
  • Создавать понятную и самодокументируемую иерархию ошибок

Расширение 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-исключения

Использование 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()

Часто задаваемые вопросы

Урок «Создание пользовательских исключений» бесплатный?

Да — полный текст урока «Создание пользовательских исключений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс PHP Academy, подпишись на CoddyKit PRO. Курс PHP Academy содержит 4 уроков всего.

Чему я научусь в уроке «Создание пользовательских исключений»?

Расширяйте класс Exception, чтобы создавать типы ошибок для конкретной предметной области Ты практикуешь PHP Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать PHP Academy?

Предыдущий опыт не требуется. PHP Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Создание пользовательских исключений»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке PHP Academy?

Да. Каждый урок PHP Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Типы ошибок PHP и их отчётность
  2. Try, Catch и Finally
  3. Создание пользовательских исключений
  4. Журналирование ошибок и лучшие практики
← Назад к PHP Academy