Исключения в PHP 8: try/catch, finally и грамотная обработка ошибок

Запрос, который чаще всего набирают начинающие: «исключения в PHP как использовать». Это руководство закрывает вопрос: разберём try, catch, finally, иерархию Throwable, глобальные обработчики ошибок и лучшие практики для продакшена в PHP 8.
Что такое исключения в PHP 8
Исключение — это объект, сигнализирующий об ошибочной ситуации. В PHP вся иерархия ошибок/исключений восходит к интерфейсу Throwable:
- Exception — «прикладные» ошибки (ошибки домена, валидации, бизнес-логики).
- Error — фатальные ошибки движка (TypeError, ValueError, ParseError и др.).
Ловить можно и конкретные типы, и общий Throwable — в зависимости от задач.
Базовый шаблон try/catch/finally
<?php
function readConfig(string $path): string {
if (!is_readable($path)) {
throw new RuntimeException("Файл конфигурации недоступен: $path");
}
$h = fopen($path, 'r');
if ($h === false) {
throw new RuntimeException("Не удалось открыть файл: $path");
}
try {
return stream_get_contents($h) ?: '';
} finally {
// finally выполнится всегда — даже при throw в try
fclose($h);
}
}
try {
$content = readConfig(__DIR__ . '/config.json');
echo "OK\n";
} catch (RuntimeException $e) {
error_log($e->getMessage());
http_response_code(500);
echo "Произошла ошибка. Попробуйте позже.";
}
Здесь finally гарантирует освобождение ресурса. Это критично для файлов, сокетов и соединений.
Множественные блоки catch и объединённые типы
В PHP 8 можно ловить разные типы в отдельных блоках или объединять их в один catch через «|».
<?php
function parsePositiveInt(string $value): int {
if (!ctype_digit($value)) {
throw new InvalidArgumentException("Ожидали целое число, получили: $value");
}
$n = (int)$value;
if ($n <= 0) {
throw new DomainException("Число должно быть > 0, получено: $n");
}
return $n;
}
try {
$n = parsePositiveInt($_GET['limit'] ?? '0');
echo "Будем обрабатывать $n элементов";
} catch (InvalidArgumentException|DomainException $e) {
http_response_code(400);
echo "Неверный параметр: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
} catch (Throwable $e) {
// Запасной вариант: логируем всё прочее
error_log($e);
http_response_code(500);
echo "Внутренняя ошибка сервера";
}
Exception vs Error: что ловить в продакшене
Ошибка уровня Error (например, TypeError, ValueError) сигнализирует о проблемах на уровне кода/типов. Их полезно логировать и отображать понятный ответ пользователю, не «падая» белым экраном. В front controller (index.php) обычно ловят общий Throwable.
<?php
// index.php — пример глобального «щитка»
set_exception_handler(function (Throwable $e) {
error_log($e);
http_response_code(500);
echo "Что-то пошло не так. Мы уже чиним.";
});
// Дальше — ваш роутер/приложение
require __DIR__ . '/app.php';
Собственные классы исключений
Создавайте доменные исключения, чтобы точно различать ситуации и упрощать обработку:
<?php
class PaymentException extends RuntimeException {}
class PaymentDeclined extends PaymentException {}
class PaymentGatewayDown extends PaymentException {}
function charge(int $amount): void {
// Демонстрация: разный тип ошибок для разных сценариев
if ($amount > 10_000) {
throw new PaymentDeclined("Лимит превышен");
}
if (!rand(0, 3)) {
throw new PaymentGatewayDown("Шлюз недоступен");
}
}
try {
charge(12000);
} catch (PaymentDeclined $e) {
// Можно предложить другой способ оплаты
echo "Платёж отклонён: {$e->getMessage()}";
} catch (PaymentGatewayDown $e) {
// Сообщить о технических работах
echo "Проблема со шлюзом. Попробуйте позже.";
}
Перевод предупреждений PHP в исключения (ErrorException)
Чтобы не пропускать предупреждения/notice, можно конвертировать их в исключения через set_error_handler:
<?php
set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
// E_DEPRECATED и др. — на ваше усмотрение
if (!(error_reporting() & $severity)) {
return false; // игнорируем отключённые уровни
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
try {
// Вызовет E_WARNING -> будет брошен ErrorException
file_get_contents('/path/does/not/exist');
} catch (ErrorException $e) {
error_log($e);
}
JSON и исключения: JSON_THROW_ON_ERROR
В PHP 8 удобно парсить JSON, не проверяя json_last_error вручную — используйте флаг JSON_THROW_ON_ERROR:
<?php
$json = '{"limit": 10}';
try {
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
echo $data['limit'];
} catch (JsonException $e) {
error_log("Некорректный JSON: {$e->getMessage()}");
}
Глобальные обработчики и завершение скрипта
Для перехвата фатальных ошибок на завершении скрипта используйте register_shutdown_function. Это не полноценная замена исключениям, но поможет зафиксировать критический сбой:
<?php
register_shutdown_function(function () {
$error = error_get_last();
if ($error) {
error_log('[FATAL] ' . json_encode($error, JSON_UNESCAPED_UNICODE));
}
});
Настройки окружения для продакшена
- error_reporting(E_ALL) — логируйте всё.
- display_errors = Off — не показывайте стек-трейсы пользователю.
- log_errors = On, укажите error_log — централизованный лог ошибок.
- Разделяйте конфиги для dev/prod, используйте переменные окружения.
Лучшие практики обработки исключений
- Бросайте исключения по факту исключительных ситуаций, не для обычного потока управления.
- Ловите как можно ближе к месту, где вы можете корректно восстановиться или вернуть понятный ответ.
- Используйте собственные исключения для домена, а встроенные — для общих ошибок (InvalidArgumentException, RuntimeException и т. п.).
- Всегда логируйте неожиданные Throwable на уровне входной точки приложения.
- В finally освобождайте ресурсы: файлы, локи, временные данные.
- Добавляйте в исключения контекст: идентификаторы запросов, пользователя, важные параметры (без секретов).
- При пробросе исключения сохраняйте «предыдущее»: throw new RuntimeException('msg', 0, $e).
Короткая шпаргалка ошибок и исключений
- Throwable — общий предок, можно ловить всё сразу.
- Exception — прикладные ошибки; Error — ошибки уровня движка (TypeError, ValueError).
- JSON_THROW_ON_ERROR — удобный и безопасный разбор JSON.
- set_error_handler + ErrorException — перевод предупреждений в исключения.
- set_exception_handler — централизованная обработка и логирование.
Хочется системно прокачать PHP?
Разобраться глубже с PHP и базами данных поможет практический курс с проектами и обратной связью. Рекомендую посмотреть программу и стартовать: Пройти интенсив "PHP и MySQL с Нуля до Гуру 3.0" →
Теперь вы знаете, как устроены исключения в PHP 8 и как строить надёжную обработку ошибок без «белых экранов». Возьмите примеры из статьи за основу и адаптируйте под архитектуру вашего проекта.
-
-
Михаил Русаков
Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.