Исключения
Содержание
Модель исключений (exceptions) в PHP похожа с используемыми в других языках программирования.
Исключение можно сгенерировать (выбросить) при помощи оператора
throw
, и можно перехватить (поймать)
оператором catch
. Код генерирующий исключение, должен
быть окружен блоком try
, для того, чтобы можно было
перехватить исключение. Каждый блок try
должен иметь как минимум один соответствующий ему блок catch
или finally
.
В случае, если выброшено исключение, для которого нет блока catch
в текущей функции,
это исключение будет "всплывать" по стеку вызова, пока не будет найден
подходящий блок catch
. При этом, все встреченные блоки finally
будут исполнены.
Если стек вызовов раскрутится до глобальной области видимости, не встретив подходящего
блока catch
, программа завершит работу с фатальной ошибкой, если только у вас
не настроен глобальный обработчик исключений.
Генерируемый объект должен принадлежать классу Exception или наследоваться от Exception. Попытка сгенерировать исключение другого класса приведет к фатальной ошибке PHP.
Начиная с PHP 8.0.0, ключевое слово throw
является выражением и может использоваться
в контексте других выражений. В более ранних версиях оно являлось оператором и требовало
размещения в отдельной строке.
catch
Блок catch
определяет то, как следует реагировать на выброшенное исключение.
В блоке catch
указывается один или более типов исключений или ошибок(Error), которые он
будет обрабатывать. Также указывается и переменная, которой будет присвоено
пойманное исключение (начиная с 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. Однако это не сработает для фатальных ошибок.
Пример #3 Преобразование сообщения об ошибках в исключение
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
Стандартная библиотека PHP (SPL) предоставляет хороший набор встроенных классов исключений.
Примеры
Пример #4 Выбрасывание исключений
<?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 Выброшено исключение: Деление на ноль. Привет, мир
Пример #5 Обработка исключений с помощью блока 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. Привет, мир
Пример #6 Взаимодействие между блоками finally
и return
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>
Результат выполнения данного примера:
finally
Пример #7 Вложенные исключения
<?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!"
Пример #8 Обработка нескольких исключений в одном блоке 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"
Пример #9 Пример блока catch
без указания переменной
Допустимо начиная с PHP 8.0.0
<?php
class SpecificException extends Exception {}
function test() {
throw new SpecificException('Ой!');
}
try {
test();
} catch (SpecificException) {
print "Было поймано исключение SpecificException, но нам безразлично, что у него внутри.";
}
?>
Пример #10 Throw как выражение
Only permitted in PHP 8.0.0 and later.
<?php
class SpecificException extends Exception {}
function test() {
do_something_risky() or throw new Exception('Всё сломалось');
}
try {
test();
} catch (Exception $e) {
print $e->getMessage();
}
?>