Ответ
В современных 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.