try
Узнайте, как PHP try/catch/finally обрабатывает исключения. Несколько блоков catch, union-типы, повторный выброс и типичные ошибки.
Ключевое слово PHP try
Ключевое слово try помечает блок кода, который может выбросить исключение — объект, сигнализирующий о том, что что-то пошло не так и нормальное выполнение невозможно. Когда внутри блока try выбрасывается исключение, PHP прекращает выполнение оставшейся части этого блока и ищет подходящий блок catch для его обработки. Необязательный блок finally выполняется в любом случае, независимо от произошедшего.
На этой странице рассматриваются синтаксис try/catch/finally, правила сопоставления нескольких блоков catch, перехват нескольких типов исключений одновременно, повторный выброс и типичные подводные камни. Если вы только знакомитесь с понятием выброса ошибок, сначала прочитайте Исключения в PHP.
Синтаксис
try {
// Code that may throw an exception
} catch (ExceptionType $e) {
// Code that runs if an exception of ExceptionType (or a subclass) is thrown
} finally {
// Optional: always runs, whether or not an exception was thrown
}За блоком try должен следовать хотя бы один блок catch, или блок finally, или оба — try без них является синтаксической ошибкой. Переменная в catch (ExceptionType $e) содержит объект выброшенного исключения, который можно опросить методами, например $e->getMessage().
Первый пример
Функция ниже выбрасывает исключение при попытке делить на ноль. Блок try вызывает её, блок catch сообщает о проблеме, а finally выполняется всегда:
<?php
function divide($dividend, $divisor)
{
if ($divisor == 0) {
throw new Exception("Cannot divide by zero.");
}
return $dividend / $divisor;
}
try {
$result = divide(10, 0);
echo $result; // skipped — divide() threw before returning
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
} finally {
echo "Done." . PHP_EOL;
}Вывод:
Error: Cannot divide by zero.
Done.Обратите внимание, что echo $result; никогда не выполняется: как только divide(10, 0) выбрасывает исключение, управление немедленно переходит к блоку catch. Блок finally выполняется после него. Если вызвать divide(10, 2), исключение не выбрасывается, блок try выводит 5, и finally всё равно выводит Done..
Несколько блоков catch
За одним блоком try может следовать несколько блоков catch. PHP проверяет их сверху вниз и выполняет первый, тип которого совпадает с выброшенным исключением (совпадение учитывает подклассы). Размещайте более специфичные типы перед более общими — ведущий catch (Exception $e) перехватит всё, что идёт ниже.
<?php
try {
$value = "5";
if (!is_int($value)) {
throw new TypeError("Expected an integer.");
}
} catch (TypeError $e) {
echo "Type problem: " . $e->getMessage() . PHP_EOL;
} catch (Exception $e) {
echo "Other problem: " . $e->getMessage() . PHP_EOL;
}Вывод:
Type problem: Expected an integer.Перехват нескольких типов в одном блоке
Когда два типа исключений должны обрабатываться одинаково, объедините их вертикальной чертой (|) — функция, добавленная в PHP 7.1, — вместо дублирования блока:
<?php
try {
throw new RuntimeException("Network timed out.");
} catch (RuntimeException | LogicException $e) {
echo "Handled: " . $e->getMessage() . PHP_EOL;
}Вывод:
Handled: Network timed out.Поведение finally
Блок finally выполняется даже если в блоке try или catch есть оператор return. Это делает его идеальным местом для очистки ресурсов — закрытия файлов, снятия блокировок, отката транзакций — которая должна происходить при любом сценарии:
<?php
function readConfig()
{
try {
return "config loaded";
} finally {
echo "Cleanup ran." . PHP_EOL;
}
}
echo readConfig() . PHP_EOL;Вывод:
Cleanup ran.
config loadedБлок finally выполняется до того, как функция фактически возвращает своё значение. Избегайте возврата из самого finally: оператор return там переопределяет значение из try/catch и молча игнорирует любое выброшенное исключение.
Повторный выброс исключения
Блок catch может выполнить частичную работу — записать ошибку в журнал, добавить контекст — и затем повторно выбросить исключение, чтобы вызывающий код выше мог решить, что с ним делать. Используйте throw $e; для повторного выброса того же объекта:
<?php
try {
try {
throw new Exception("Disk full.");
} catch (Exception $e) {
echo "Logging: " . $e->getMessage() . PHP_EOL;
throw $e; // hand it to the outer handler
}
} catch (Exception $e) {
echo "Outer handler: " . $e->getMessage() . PHP_EOL;
}Вывод:
Logging: Disk full.
Outer handler: Disk full.Типичные подводные камни
tryнужен партнёр. Блокtryсам по себе не разберётся — дополните его хотя бы однимcatchили блокомfinally.- Не все ошибки — исключения. Многие runtime-предупреждения (неопределённая переменная, неудачный
fopen()) не выбрасываются как исключения, поэтомуcatchих не увидит. Используйтеset_error_handler()или проверяйте возвращаемые значения. Фатальные объектыError(например,TypeError) можно перехватить, поскольку они реализуютThrowable. - Располагайте блоки
catchот частного к общему. Широкийcatch (Exception $e), стоящий первым, скрывает все последующие блоки. - Неглотайте ошибки молча. Пустой блок
catchскрывает сбой; как минимум записывайте$e->getMessage()в журнал, чтобы проблема была видна. finallyможет маскировать исключения. Возврат изfinallyотбрасывает и значение, возвращаемое блокомtry, и любое летящее исключение.
Когда использовать try?
Прибегайте к try/catch, когда сбой является исключительным и вызывающий код может разумно на него отреагировать: потеря соединения с базой данных, ошибка в ответе API, некорректные данные пользователя, которые нужно отклонить. Для обычного управления потоком (был ли ключ найден в массиве? пуста ли строка?) используйте обычные условные операторы — исключения предназначены для нештатных ситуаций, а не для повседневного ветвления.
Связанные темы
catch— блок, обрабатывающий выброшенное исключение.finally— код, который всегда выполняется послеtry/catch.throw— самостоятельный выброс исключения.- Класс Exception — базовый тип и его методы.
- Исключения в PHP — общая картина обработки ошибок.
set_exception_handler()— перехват исключений, вышедших за пределы каждого блокаtry.