Как происходит обработка исключений (exceptions) в PHP-приложении?

«Как происходит обработка исключений (exceptions) в PHP-приложении?» — вопрос из категории PHP Core, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В современных PHP-приложениях (Laravel/Symfony) обработка исключений строится на централизованном механизме через глобальный обработчик. Вот как я это реализовывал:

1. Базовый перехват с try-catch: Для локальной обработки известных ошибок:

try {
    $user = User::findOrFail($request->user_id);
    $invoice = $billingService->createInvoice($user);
} catch (ModelNotFoundException $e) {
    // Ловим конкретное исключение фреймворка
    Log::warning('User not found for invoice', ['user_id' => $request->user_id]);
    return response()->json(['error' => 'User not found'], 404);
} catch (BillingServiceException $e) {
    // Ловим кастомное исключение бизнес-логики
    report($e); // Отправляем в Sentry/Log
    return response()->json(['error' => 'Billing error'], 502);
}

2. Глобальный обработчик (AppExceptionsHandler): В Laravel все непойманные исключения попадают в этот класс. Я настраивал его для преобразования исключений в JSON-ответы для API:

class Handler extends ExceptionHandler
{
    public function render($request, Throwable $e)
    {
        if ($request->expectsJson()) {
            if ($e instanceof ValidationException) {
                return response()->json([
                    'message' => 'Validation failed',
                    'errors' => $e->errors()
                ], 422);
            }

            // Все остальные исключения -> 500
            return response()->json([
                'message' => 'Server Error',
                'trace' => config('app.debug') ? $e->getTrace() : null
            ], 500);
        }
        return parent::render($request, $e);
    }
}

3. Кастомные исключения для бизнес-логики: Создавал свои классы исключений для четкого разделения ошибок:

namespace AppExceptionsPayment;

class InsufficientFundsException extends Exception
{
    protected $message = 'Not enough funds on the balance';
    protected $code = 409; // Conflict
}
// Использование:
throw new InsufficientFundsException();

4. Логирование и мониторинг: В методе report() глобального обработчика все исключения логировались в Sentry, что позволяло быстро реагировать на ошибки в production.