Конструкция try catch finally
В процессе работы программы могут возникать различные ошибки, которые могут прервать работу программы. Например, рассмотрим следующую ситуацию:
<?
$a = 5;
$b = 0;
$result = $a / $b;
echo $result;
echo "Конец работы программы";
Программа выводит результат деления. Поскольку делитель равен 0, а на ноль делить нельзя, то при выполнении деления программа завершится, и в браузере мы увидим что-то типа следующего:
Fatal error: Uncaught DivisionByZeroError: Division by zero in D:\localhost\hello.php:11 Stack trace: #0 {main} thrown in D:\localhost\hello.php on line 11
Браузер отобразит нам произошедшую ошибку, причем дальше после строки с делением программа даже не будет выполняться.
Кто-то может сказать, что ситуация искуственная, так как мы сами определили делитель равный нулю. Но данные могут передаваться извне. Кроме того, кроме деления на ноль есть различные ситуации, при которых могут происходить ошибки. Но PHP предоставляет ряд возможностей для обработки подобных ситуаций.
Блоки try, catch
Для обработки исключений в PHP применяется конструкция try-catch:
try {
// код, который может вызвать исключение
}
catch(Тип_исключения $ex) {
// обработка исключения
}
Эта конструкция в общем варианте состоит из двух блоков — try и catch. В блок try помещается код, который потенциально может вызвать исключение. А в блоке catch помещается обработка возникшего исключения. Причем каждого типа исключения мы можем определить свою логику обработки. Конкретный тип исключения, который мы хотим обработать, указывается в круглых скобках после оператора catch:
catch(Тип_исключения $ex)
После названия типа указывается переменная этого типа (в данном случае $ex), которая будет хранить информацию об исключении и которую мы можем использовать при обработке исключения.
Если в блоке try при выполнении кода возникает ошибка, то блок try прекращает выполнение и передает управление блоку catch, который обрабатывает ошибку. А после завершения выполнения кода в блоке catch программа продолжает выполнять инструкции, которые размещены после блока catch.
Если в блоке try при выполнении кода не возникает ошибок, то блок catch не выполняется, а после завершения блока try программа продолжает выполнять инструкции, которые размещены после блока catch.
Обработаем ошибку с делением на ноль
В данном случае код деления на ноль, поскольку он может потенциально вызвать ошибку, помещен в блок try.
В блоке catch обрабатывается ошибка типа DivisionByZeroError, которая генерируется при делении на ноль. Вся обработка сводится к выводу информации на экран.
<?php
try {
// код, который может вызвать исключение
$a = 5;
$b = 0;
$result = $a / $b;
echo $result;
} catch (DivisionByZeroError $ex) {
// обработка исключения
echo "Произошло исключение:<br>";
echo $ex . "<br>";
}
echo "Конец работы программы";
В итоге при выполнении программа выведет следующее:
Произошло исключение:
DivisionByZeroError: Division by zero in D:\localhost\hello.php:14 Stack trace: #0 {main}
Конец работы программы
Как видно из вывода программы, она не завершается аварийно при делении на ноль, а продолжает работу.
Обработаем ошибку с делением на ноль и выведем свой текст ошибки
<?php
try {
// код, который может вызвать исключение
$a = 5;
$b = 0;
// проверка переменной
if ($b == 0) {
// немедленный вызов catch
throw new Error("Переменная $b равна нулю");
}
$result = $a / $b;
} catch (Error $ex) {
// обработка исключения
echo "Произошло исключение:<br>";
echo $ex . "<br>";
}
echo "Конец работы программы";
В данном случае код деления на ноль, поскольку он может потенциально вызвать ошибку, помещен в блок try.
В блоке catch обрабатывается ошибка типа Error, которая возникае при проверке переменной $b. Вся обработка сводится к выводу информации на экран.
В итоге при выполнении программа выведет следующее:
Произошло исключение:
Error: Переменная 0 равна нулю in D:\localhost\hello.php:14 Stack trace: #0 {main}
Конец работы программы
Несколько блоков catch
Конструкция try..catch позволяет определить несколько блоков catch — для обработки различных типов ошибок и исключений:
<?
try {
$result = 5 / 0;
echo $result;
} catch (ParseError $p) {
echo "Произошла ошибка парсинга";
} catch (DivisionByZeroError $d) {
echo "На ноль делить нельзя";
}
При возникновении ошибки будет для ее обработки будет выбираться тот блок catch, который соответствует вошникшей ошибки. Так, в данном случае при делении на ноль будет выполняться второй блок catch.
Если бы в блоке try возникла бы ошибка, которая бы не соответствовала типам из блоков catch (в данном случае — типам DivisionByZeroError и ParseError), то такая ошибка не была бы обработана, и соответственно программа бы аварийно завершила свое выполнение.
Блоки catch с более конкретными типами ошибок и исключений должны идти в начале, а более с более общими типа — в конце:
<?
try {
$result = 5 / 0;
echo $result;
} catch (DivisionByZeroError $ex) {
echo "На ноль делить нельзя";
} catch (ArithmeticError $ex) {
echo "Ошибка при выполнении арифметической операции";
} catch (Error $ex) {
echo "Произошла ошибка";
} catch (Throwable $ex) {
echo "Ошибка при выполнении программы";
}
Класс DivisionByZeroError унаследован от ArithmeticError, который, в свою очередь, унаследован от Error, реализующего интерфейс Throwable. Поэтому класс DivisionByZeroError представляет более конкретный тип и представляемые им ошибки должны обрабатываться в первую очередь. А тип Throwable представляет наиболее общий тип, так как ему соответствуют все возможные ошибки и исключения, поэтому блоки catch с таким типом должны идти в
конце.
В данном случае опять же в блоке try происходит ошибка деления на ноль. Но этой ошибке соответствуют все четыре блока catch. Для обработки PHP будет выбирать первый попавшийся, который соответствует типу ошибки. В данном случае это блок для обработки ошибки типа DivisionByZeroError.
Если нам надо обрабатывать в принципе все ошибки и исключения, то мы можем определить только обработку общего для всех них типа Throwable:
<?
try {
$result = 5 / 0;
echo $result;
}
catch(Throwable $ex) {
echo "Ошибка при выполнении программы";
}
Начиная с версии PHP 8.0 в блоке catch можно просто указать тип обрабатываемого исключения, не определяя переменную:
catch(DivisionByZeroError) {
echo "Произошло исключение: деление на ноль";
}
Блок finally
Конструкция try..catch также может определять блок finally. Этот блок выполняется в конце — после блока try и catch вне зависимости, возникла или нет ошибка. Нередко блок finally используется для закрытия ресурсов, которые применяются в блоке try:
<?
try {
$result = 5 / 0;
echo $result . "<br>";
}
catch(Throwable $ex) {
echo "Ошибка при выполнении программы<br>";
}
finally {
echo "Блок finally<br>";
}
echo "Конец работы программы";
Вывод программы:
Ошибка при выполнении программы
Блок finally
Конец работы программы
Конструкция try..catch..finally может содержать либо все три блока, либо только два блока try и либо блок catch, либо блок finally.
Типы ошибок и исключений
В PHP для разных ситуаций есть множество типов, которые описывают ошибки. Все эти встроенные типы применяют интерфейс Throwable:
Все типы делятся на три группы:
Throwableвключает в себя и ошибкиErrorи исключенияExceptiongetMessage()возвращает сообщение об ошибкеgetCode()возвращает код исключенияgetFile()возвращает название файла, в котором возникла ошибкаgetLine()возвращает номер строки, в которой возникла ошибкаgetTrace()возвращает трассировку стекаgetTraceAsString()возвращает трассировку стека в виде строки
ErrorошибкаParseErrorвозникают из-за ошибок в синтаксисе кода, например, отсутствия точки с запятой или неправильного имени функцииFatal errorпрерывают выполнение скрипта из-за критических проблем, например, вызова неопределённой функции или классаWarningErrorнефатальные ошибки, PHP продолжает выполнение скрипта даже после предупреждения. Обычно возникают при проблемах, которые не предотвращают выполнение скрипта, но могут привести к неожиданному поведениюNoticeErrorменее серьёзные ошибки, обычно используются для уведомления разработчиков о незначительных проблемах в коде, например, об обращении к неопределённой переменной. Не прерывают выполнение скрипта и часто не замечаются, если не явно не регистрируютсяTypeErrorвыбрасывается, когда аргументы метода или возвращаемое значение не совпадает с объявленным типомParseErrorвыбрасывается, когда подключаемый (путем include/require) файл или код в eval содержит ошибки синтаксисаAssertionErrorвыбрасывается, когда возникает ошибка в коде
ExceptionисключениеArithmeticErrorописывает ошибки, возникающие при выполнении арифметических операцийDivisionByZeroErrorпредставляет ошибку при делении на нольLogicExceptionописывает ошибки, возникающие в логике кода, например, при попытке считать несуществующий файл, подключиться к базе данных с некорректными учётными даннымиRuntimeExceptionописывает ошибки, возникающие во время выполнения кода, например, при попытке выйти за пределы массиваFileNotFoundExceptionвыбрасывается, когда файл не найденJsonParseExceptionвыбрасывается, когда данные были загружены, обработаны, сохранены в файл, но почему-то сохранились неправильноBadLogicExceptionвыбрасывается, когда должен сработать один из блоков if, но не сработал
Классы Error и Exception наследуются классы ошибок и исключений, которые описывают конкретные ситуации. Например, от класса Error наследуется класс ArithmeticError, который описывает ошибки, возникающие при выполнении арифметических операций. А от класса ArithmeticError наследуется класс DivisionByZeroError, который представляют ошибку при делении на ноль.
Применим некоторые из этих методов:
<?
try {
$result = 5 / 0;
echo $result;
}
catch(DivisionByZeroError $ex) {
echo "Сообщение об ошибке: " . $ex->getMessage() . "<br>";
echo "Файл: " . $ex->getFile() . "<br>";
echo "Номер строки: " . $ex->getLine() . "<br>";
}
Результат работы:
Сообщение об ошибке: Division by zero
Файл: D:\localhost\hello.php
Номер строки: 11
