← Назад к вопросам

Какие знаешь способы обработки исключений?

2.0 Middle🔥 141 комментариев
#PHP Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы обработки исключений в PHP

Обработка исключений — это критически важная часть разработки надежных PHP-приложений. Механизм исключений позволяет структурированно управлять ошибками и нештатными ситуациями, отделяя нормальный поток выполнения от обработки ошибок. Вот основные способы и лучшие практики.

Базовый механизм: try-catch-finally

Основой является конструкция try-catch-finally, которая появилась в PHP 5 и стала стандартом.

try {
    // Код, который может вызвать исключение
    $processor = new PaymentProcessor();
    $processor->process($transaction);
} catch (PaymentFailedException $e) {
    // Ловим конкретное исключение
    Log::error('Payment failed: ' . $e->getMessage());
    $user->notify('Payment failed, please try again');
} catch (InvalidArgumentException $e) {
    // Ловим другое исключение - порядок важен!
    Log::warning('Invalid argument: ' . $e->getMessage());
    throw new ProcessingException('Invalid input data', 0, $e);
} catch (Exception $e) {
    // Общий обработчик (ловит все остальные исключения)
    Log::critical('Unexpected error: ' . $e->getMessage());
    throw $e; // Пробрасываем дальше
} finally {
    // Этот код выполнится ВСЕГДА, даже если было исключение
    $processor->cleanup();
    DB::closeConnection();
}

1. Иерархия исключений и множественные catch-блоки

PHP поддерживает наследование исключений, что позволяет создавать четкую иерархию:

class AppException extends Exception {}
class ValidationException extends AppException {}
class DatabaseException extends AppException {}

try {
    validateInput($data);
    saveToDatabase($data);
} catch (ValidationException $e) {
    // Обработка ошибок валидации
    return response()->json(['error' => $e->getMessage()], 422);
} catch (DatabaseException $e) {
    // Обработка ошибок БД
    Log::error('DB error: ' . $e->getMessage());
    return response()->json(['error' => 'Database error'], 500);
} catch (AppException $e) {
    // Общий обработчик для всех исключений приложения
    return response()->json(['error' => $e->getMessage()], 400);
}

2. Проброс исключений (re-throwing)

Иногда нужно перехватить исключение, добавить контекст и пробросить дальше:

try {
    $this->processOrder($order);
} catch (InventoryException $e) {
    // Добавляем контекст и пробрасываем
    Log::error('Inventory issue for order ' . $order->id);
    throw new OrderProcessingException(
        'Failed to process order due to inventory: ' . $e->getMessage(),
        $order->id,
        $e // Предыдущее исключение как причина
    );
}

3. Глобальный обработчик исключений

Для обработки непойманных исключений используется set_exception_handler():

set_exception_handler(function (Throwable $exception) {
    // Логирование
    error_log('Uncaught exception: ' . $exception->getMessage());
    
    // Пользовательский вывод (в зависимости от среды)
    if (php_sapi_name() === 'cli') {
        echo "Error: " . $exception->getMessage() . PHP_EOL;
    } else {
        http_response_code(500);
        echo json_encode(['error' => 'Internal server error']);
    }
    
    // Важно: после обработки скрипт завершается
});

// Восстановление предыдущего обработчика
// restore_exception_handler();

4. Обработка ошибок через ErrorException

Можно преобразовывать ошибки в исключения для единообразной обработки:

set_error_handler(function ($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // Эта ошибка не попадает в error_reporting
        return;
    }
    throw new ErrorException($message, 0, $severity, $file, $line);
});

try {
    // Теперь ошибки типа E_WARNING превратятся в исключения
    include 'non_existent_file.php';
} catch (ErrorException $e) {
    echo 'Caught error as exception: ' . $e->getMessage();
}

5. Собственные классы исключений

Создание специализированных классов исключений улучшает читаемость и обработку:

class ApiException extends RuntimeException {
    private $statusCode;
    private $errorCode;
    
    public function __construct(string $message, int $statusCode = 500, string $errorCode = 'INTERNAL_ERROR', Throwable $previous = null) {
        parent::__construct($message, 0, $previous);
        $this->statusCode = $statusCode;
        $this->errorCode = $errorCode;
    }
    
    public function getStatusCode(): int {
        return $this->statusCode;
    }
    
    public function getErrorCode(): string {
        return $this->errorCode;
    }
    
    public function toArray(): array {
        return [
            'error' => $this->errorCode,
            'message' => $this->getMessage(),
            'trace' => DEBUG_MODE ? $this->getTrace() : null
        ];
    }
}

// Использование
throw new ApiException('User not found', 404, 'USER_NOT_FOUND');

6. Исключения в современных фреймворках

В Laravel, Symfony и других фреймворках есть встроенные механизмы:

// Laravel: обработчики в App\Exceptions\Handler
public function render($request, Throwable $exception) {
    if ($exception instanceof ModelNotFoundException) {
        return response()->json(['error' => 'Resource not found'], 404);
    }
    
    if ($exception instanceof ValidationException) {
        return response()->json(['errors' => $exception->errors()], 422);
    }
    
    return parent::render($request, $exception);
}

// Глобальная обработка в middleware

7. Исключения и PSR-стандарты

PSR-3 (логирование) и PSR-15 (middleware) определяют стандарты обработки:

// PSR-3 совместимое логирование исключений
$logger->error('Exception occurred', [
    'exception' => $exception,
    'context' => ['user_id' => $userId]
]);

Ключевые принципы обработки исключений:

  • Ловите исключения максимально конкретно, начиная с самых специфичных
  • Всегда логируйте исключения с контекстом для отладки
  • Не подавляйте исключения без веской причины (пустой catch-блок — антипаттерн)
  • Используйте finally для освобождения ресурсов (файлы, соединения с БД)
  • Создавайте информативные сообщения об ошибках, но не раскрывайте чувствительную информацию
  • Различайте исключения приложения (ValidationException) и системные ошибки (RuntimeException)

Правильная обработка исключений значительно повышает надежность и отлаживаемость приложений, позволяя четко разделить нормальную бизнес-логику и обработку ошибок.