Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы обработки исключений в 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)
Правильная обработка исключений значительно повышает надежность и отлаживаемость приложений, позволяя четко разделить нормальную бизнес-логику и обработку ошибок.