Обработка исключений в прикладном коде PHP
Статья про как писать код с исключениями. Дерево встроенных классов Throwable — в материале Иерархия исключений в PHP.
Исключение и ошибка в PHP 7+
Любой сбой, который нужно обработать или залогировать, представлен объектом Throwable:
Exception— ожидаемые сбои бизнес-логики и библиотек (InvalidArgumentException,PDOException).Error— ошибки уровня языка (TypeError,DivisionByZeroError), раньше обрывавшие скрипт.
Оба типа перехватываются одинаково. Для «поймать всё» используют catch (Throwable $e).
Базовая конструкция try / catch / finally
<?php
declare(strict_types=1);
function loadConfig(string $path): array
{
if (!is_readable($path)) {
throw new \RuntimeException("Config not readable: {$path}");
}
$json = file_get_contents($path);
$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
return $data;
}
try {
$config = loadConfig(__DIR__ . '/config.json');
} catch (\JsonException $e) {
fwrite(STDERR, "Invalid JSON: " . $e->getMessage() . PHP_EOL);
$config = [];
} catch (\RuntimeException $e) {
fwrite(STDERR, $e->getMessage() . PHP_EOL);
$config = [];
} finally {
// Выполняется всегда: и после успеха, и после catch, и перед exit в catch
// Закрытие ресурсов, сброс флагов — сюда
}
Порядок catch — от конкретного к общему. Сначала JsonException, потом RuntimeException, в конце при необходимости Throwable.
finally не подавляет исключение: если в catch снова выбросить ошибку или не обработать её, поведение сохраняется. finally удобен для закрытия файлов и транзакций (см. PDO).
throw — когда бросать
Бросать исключение уместно, когда:
- Метод не может выполнить контракт при текущих данных (неверный email, пустой файл).
- Вызывающий код должен решить, что делать (повторить, показать форму, откатить транзакцию).
- Ошибка не является нормальным ветвлением (
if (!$user) throw ...вместо тысячи вложенныхif).
function withdraw(Account $account, float $sum): void
{
if ($sum <= 0) {
throw new \InvalidArgumentException('Sum must be positive');
}
if ($account->balance < $sum) {
throw new \DomainException('Insufficient funds');
}
$account->debit($sum);
}
Не стоит бросать исключения для обычного потока («пользователь не найден» в API иногда возвращают как null или Option, а в веб-форме — как сообщение без stack trace).
Перехват Exception и Error
try {
processOrder($id);
} catch (\PDOException $e) {
// База недоступна — отдельная ветка
logError('db', $e);
showServiceUnavailable();
} catch (\TypeError $e) {
// Ошибка типов — чаще баг в коде
logError('bug', $e);
throw $e; // проброс после логирования
} catch (\Throwable $e) {
logError('general', $e);
showGenericError();
}
Проброс — throw $e или throw new WrapperException('...', 0, $e). Второй аргумент 0 — код; третий — previous exception для цепочки в логах.
Пользовательские исключения
Наследуйте от Exception или от подходящего встроенного класса:
namespace App\Exception;
class ValidationException extends \InvalidArgumentException
{
/** @param array<string, string> $errors */
public function __construct(
public readonly array $errors,
string $message = 'Validation failed',
) {
parent::__construct($message);
}
}
Использование:
if ($email === '') {
throw new ValidationException(
['email' => 'Укажите email'],
);
}
Не наследуйте бизнес-исключения от Error — это тип для сбоев движка PHP.
PDO и json с исключениями
$pdo = new \PDO($dsn, $user, $pass, [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
]);
// Любая ошибка SQL → PDOException
$data = json_decode($input, true, flags: JSON_THROW_ON_ERROR);
// Ошибка формата → JsonException
Режим ERRMODE_EXCEPTION предпочтительнее ручной проверки после каждого вызова. Подробнее: PDO.
Антипаттерны
| Плохо | Лучше |
|---|---|
Пустой catch (Exception $e) {} | Логировать и/или пробрасывать |
die($e->getMessage()) в продакшене | Страница ошибки без деталей, детали в лог |
@file_get_contents(...) | try/catch или проверка + исключение |
Исключение для каждого if (!$row) | Явный возврат null / Result-объект |
| Показ stack trace пользователю | Только в dev (display_errors=On) |
Логирование и HTTP-ответ
Минимальный шаблон границы приложения:
try {
$response = $router->dispatch($_SERVER['REQUEST_URI'], $_SERVER['REQUEST_METHOD']);
echo $response;
} catch (ValidationException $e) {
http_response_code(422);
echo json_encode(['errors' => $e->errors], JSON_THROW_ON_ERROR);
} catch (\Throwable $e) {
error_log($e->__toString());
http_response_code(500);
echo 'Внутренняя ошибка'; // без $e->getMessage() наружу
}
В CLI тот же Throwable пишут в STDERR и завершают процесс ненулевым кодом.
Связь с иерархией типов
При выборе класса исключения ориентируйтесь на семантику из справочника иерархии:
InvalidArgumentException— неверный аргумент функции.DomainException— нарушение правила предметной области.RuntimeException— сбой среды (файл, сеть), который нельзя предусмотреть при компиляции.
Что изучить дальше
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). PHP как язык веб-разработки - роль серверного исполнения, базовый синтаксис и место в современном стеке. Экосистема PHP-приложений - фреймворки, инструменты, пакеты и типовые архитектурные подходы веб-разработки. PHP — это язык программирования общего назначения, который изначально создавался для динамической генерации веб-страниц. Модель исполнения PHP - жизненный цикл запроса, серверный контекст и экосистема популярных CMS. История PHP - эволюция языка от простых скриптов до зрелой платформы серверной веб-разработки. Composer в PHP - управление зависимостями, автозагрузка классов и воспроизводимая сборка проектов. Параметр opcache.fast_shutdown ускоряет завершение работы скрипта за счёт пропуска стандартной процедуры освобождения памяти. Вместо этого используется механизм сборщика мусора операционной системы. Локальная среда разработки на PHP - настройка стека, запуск приложения и отладка без продакшен-сервера. Набор советов, правил, принципов и обычаев в разработке на этом языке. Фреймворки и библиотеки PHP - организация сервисов, вспомогательные компоненты и ускорение разработки веб-приложений. Гайд по установке и настройке с написанием первой программы и её запуском. Примеры простых и полезных консольных приложений с демонстрацией концепций языка.PHP - язык веб-разработки
Экосистема PHP-приложений
Что требуется знать перед началом изучения языка программирования PHP
Модель исполнения PHP
История языка PHP
Composer - управление зависимостями в PHP
Настройка веб-сервера для работы с PHP
Локальная среда разработки на PHP
Рекомендации по разработке на PHP
Фреймворки и библиотеки PHP
Первая программа на PHP
Простые приложения на PHP