Исключения

Содержание

Модель исключений 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();

?>

Результат выполнения приведённого примера:

finally

Пример #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();

?>

Результат выполнения приведённого примера:

string(4) "foo!"

Пример #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();

?>

Результат выполнения приведённого примера:

string(11) "MyException"

Пример #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();
}

?>