Исключения
Содержание
В PHP реализована модель исключений, аналогичная тем, что используются в других языках программирования.
Исключение в PHP может быть выброшено (throw
) и поймано (catch
).
Код может быть заключён в блок try
, чтобы облегчить обработку потенциальных исключений.
У каждого блока try
должен быть как минимум один соответствующий блок catch
или finally
.
Если выброшено исключение, а в текущей области видимости функции нет блока catch
,
исключение будет "подниматься" по стеку вызовов к вызывающей функции, пока не найдёт подходящий блок catch
.
Все блоки finally
, которые встретятся на этом пути, будут выполнены.
Если стек вызовов разворачивается до глобальной области видимости, не встречая подходящего блока catch
,
программа завершается с неисправимой ошибкой, если не был установлен глобальный обработчик исключений.
Выброшенный объект должен наследовать (instanceof
) интерфейс Throwable.
Попытка выбросить объект, который таковым не является, приведёт к неисправимой ошибке PHP.
Начиная с PHP 8.0.0, ключевое слово throw
является выражением и может быть использовано
в любом контексте выражения. В предыдущих версиях оно было утверждением
и должно было располагаться в отдельной строке.
catch
Блок catch
определяет, как реагировать на выброшенное исключение.
Блок catch
определяет один или несколько типов исключений или ошибок, которые он может обработать,
и, по желанию, переменную, которой можно присвоить исключение
(указание переменной было обязательно до версии PHP 8.0.0).
Первый блок catch
, с которым столкнётся выброшенное исключение или ошибка
и соответствует типу выброшенного объекта, обработает объект.
Несколько блоков catch
могут быть использованы для перехвата различных классов исключений.
Нормальное выполнение (когда исключение не выброшено в блоке try
)
будет продолжаться после последнего блока catch
, определённого в последовательности.
Исключения могут быть выброшены (throw
) (или повторно выброшены) внутри блока catch
.
В противном случае выполнение будет продолжено после блока catch
, который был вызван.
При возникновении исключения, код, следующий за утверждением, не будет выполнен,
а PHP попытается найти первый подходящий блок catch
.
Если исключение не поймано, будет выдана неисправимая ошибка PHP
с сообщением "Uncaught Exception ...
",
если только обработчик не был определён с помощью функции set_exception_handler().
Начиная с версии PHP 7.1.0, в блоке catch
можно указывать несколько исключений,
используя символ |
. Это полезно, когда разные исключения
из разных иерархий классов обрабатываются одинаково.
Начиная с версии PHP 8.0.0, имя переменной для пойманного исключения является необязательным.
Если оно не указано, блок catch
будет выполнен,
но не будет иметь доступа к выброшенному объекту.
finally
Блок finally
также может быть указан после или вместо блоков catch
.
Код в блоке finally
всегда будет выполняться после блоков try
и catch
,
независимо от того, было ли выброшено исключение
и до возобновления нормального выполнения.
Одно из заметных взаимодействий происходит между блоком finally
и оператором return
.
Если оператор return
встречается внутри блоков try
или catch
, блок finally
всё равно будет выполнен. Более того, оператор return
выполнится, когда встретится,
но результат будет возвращён после выполнения блока finally
.
Кроме того, если блок finally
также содержит оператор return
,
возвращается значение из блока finally
.
Глобальный обработчик исключений
Если исключению разрешено распространяться на глобальную область видимости,
оно может быть перехвачено глобальным обработчиком исключений, если он установлен.
Функция set_exception_handler() может задать функцию,
которая будет вызвана вместо блока 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 'Выброшено исключение: ', $e->getMessage(), "\n";
}
// Продолжение выполнения
echo "Привет, мир\n";
?>
Результат выполнения приведённого примера:
0.2
Выброшено исключение: Деление на ноль.
Привет, мир
Пример #3 Обработка исключений с помощью блока finally
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Деление на ноль.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'Поймано исключение: ', $e->getMessage(), "\n";
} finally {
echo "Первый блок finally.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Поймано исключение: ', $e->getMessage(), "\n";
} finally {
echo "Второй блок finally.\n";
}
// Продолжение нормального выполнения
echo "Привет, мир\n";
?>
Результат выполнения приведённого примера:
0.2
Первый блок finally.
Поймано исключение: Деление на ноль.
Второй блок 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();
}
?>