Исключения
Содержание
Модель исключений PHP напоминает модель исключений других языков программирования.
PHP умеет выбрасывать — throw
— и ловить — catch
— исключения.
Код заключают в блок try
, чтобы упростить обработку вероятных исключений.
Каждому блоку try
указывают как минимум один блок catch
или finally
.
Исключение будет «всплывать» по стеку вызовов функций, пока не найдёт
блок catch
, если функция выбросила исключение, а в текущей области
видимости функции, которая её вызвала, нет блока catch
. PHP выполнит
каждый блок finally
, который встретит по пути. Программа завершается
фатальной ошибкой, когда стек вызовов не встречает блок catch
и разворачивается до глобальной области видимости, если разработчик не установил
глобальный обработчик исключений.
В коде допустимо выбрасывать только объект исключения, тип которого при проверке
оператором instanceof
соответствует интерфейсу Throwable.
Попытка выбросить объект, который не выполняет это условие, приведёт к фатальной ошибке PHP.
С PHP 8.0.0 ключевое слово throw
стало выражением, которое разрешили записывать
в контексте выражения. В предыдущих версиях это слово было инструкцией
и её записывали в отдельной строке.
catch
Блок catch
определяет, как реагировать на исключение, которое выбросил код.
Блок catch
определяет один или больше типов исключений или ошибок, которые он обрабатывает,
и необязательную переменную, которой блок присвоит исключение.
До PHP 8.0.0 переменную требовалось указывать.
Объект исключения обработает первый блок catch
, с которым столкнутся
исключение или ошибка того типа или подтипа, который ожидает блок.
Блоки catch
записывают один за другим, чтобы перехватывать исключения разных классов.
Нормальное выполнение, когда блок try
не выбросил исключение,
продолжится после последнего блока catch
, который определили в последовательности.
Внутри блока catch
допустимо выбрасывать, а точнее — повторно выбрасывать исключения
через ключевое слово throw
. PHP продолжит выполнение кода после блока catch
,
который сработал, если внутри блока не выбросили исключение.
При появлении исключения PHP не выполнит код, который идёт за инструкцией,
а попытается найти первый подходящий блок catch
. PHP выдаст фатальную ошибку
с сообщением Uncaught Exception ...
, если исключение не поймали
и через функцию set_exception_handler() не определили обработчик исключений.
С PHP 7.1.0 в блоке catch
допустимо указывать исключения
через символ вертикальной черты |
. Это полезно, когда разные исключения
из разных иерархий классов обрабатывают одинаково.
С PHP 8.0.0 имя переменной для исключения, которое поймал блок, необязательно. PHP выполнит
блок catch
, но у блока не будет доступа к объекту, который выбросил код, если переменную не указали.
finally
Блок finally
также допустимо указывать после или вместо блоков catch
.
PHP выполнит код в блоке finally
после блоков try
и catch
,
независимо от того, выбросил ли код исключение, и до возобновления нормального выполнения.
Заслуживает внимания взаимодействие между блоком finally
и инструкцией return
.
PHP выполнит блок finally
, даже если встретит внутри блоков try
или catch
инструкцию return
. Больше того, когда PHP встречает инструкцию return
,
он вычисляет её, но вернёт результат после выполнения блока finally
.
Кроме того, PHP вернёт значение из блока finally
, если блок finally
тоже
содержит инструкцию return
.
Глобальный обработчик исключений
Глобальный обработчик исключений, если обработчик установили, перехватит исключение,
если исключению разрешили всплывать до глобальной области видимости.
Функция set_exception_handler() устанавливает функцию,
которую PHP вызовет вместо блока catch
, если в коде не вызвали другие блоки.
Эффект по существу такой же, как если бы всю программу обернули в блок try
-catch
с этой функцией в качестве catch
.
Примечания
Замечание:
Внутренние функции PHP чаще сообщают об ошибках через
отчёт об ошибках,
только современные объектно-ориентированные
модули работают с исключениями. При этом ошибки легко переводятся в исключения
через класс ErrorException.
Эта техника, однако, работает только с нефатальными ошибками.
Пример #1 Преобразование отчётов об ошибках в исключения
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
Примеры
Пример #2 Выброс исключения
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Деление на ноль.');
}
return 1 / $x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'PHP перехватил исключение: ', $e->getMessage(), "\n";
}
// Продолжить выполнение
echo "Привет, мир\n";
?>
Результат выполнения приведённого примера:
0.2
PHP перехватил исключение: Деление на ноль.
Привет, мир
Пример #3 Обработка исключений в блоке finally
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Деление на ноль.');
}
return 1 / $x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'PHP перехватил исключение: ', $e->getMessage(), "\n";
} finally {
echo "Первый блок finally.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'PHP перехватил исключение: ', $e->getMessage(), "\n";
} finally {
echo "Второй блок finally.\n";
}
// Продолжить выполнение
echo "Привет, мир\n";
?>
Результат выполнения приведённого примера:
0.2
Первый блок finally.
PHP перехватил исключение: Деление на ноль.
Второй блок finally.
Привет, мир
Пример #4 Взаимодействие между блоками finally
и return
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>
Результат выполнения приведённого примера:
Пример #5 Вложенные исключения
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
// Повторный выброс исключения
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
Результат выполнения приведённого примера:
Пример #6 Обработка нескольких исключений в одном блоке catch
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
public function testing() {
try {
throw new MyException();
} catch (MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();
?>
Результат выполнения приведённого примера:
Пример #7 Пример блока catch
без переменной
Разрешено только в PHP 8.0.0 и более поздних версиях.
<?php
class SpecificException extends Exception {}
function test() {
throw new SpecificException('Ой!');
}
try {
test();
} catch (SpecificException) {
print "Функция выбросила исключение SpecificException, но детали исключения неважны.";
}
?>
Пример #8 Throw как выражение
Разрешено только в PHP 8.0.0 и более поздних версиях.
<?php
function test() {
do_something_risky() or throw new Exception('Всё сломалось');
}
try {
test();
} catch (Exception $e) {
print $e->getMessage();
}
?>