Исключения
Модель исключений 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;.
&reftitle.notes;
Внутренние функции PHP чаще сообщают об ошибках через
отчёт об ошибках,
только современные объектно-ориентированные
модули работают с исключениями. При этом ошибки легко переводятся в исключения
через класс ErrorException.
Эта техника, однако, работает только с нефатальными ошибками.
Преобразование отчётов об ошибках в исключения
]]>
Стандартная библиотека PHP (SPL)
предлагает много встроенных исключений.
&reftitle.examples;
Выброс исключения
getMessage(), "\n";
}
// Продолжить выполнение
echo "Привет, мир\n";
?>
]]>
&example.outputs;
Обработка исключений в блоке &finally;
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";
?>
]]>
&example.outputs;
Взаимодействие между блоками &finally; и &return;
]]>
&example.outputs;
Вложенные исключения
getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
]]>
&example.outputs;
Обработка нескольких исключений в одном блоке catch
testing();
?>
]]>
&example.outputs;
Пример блока &catch; без переменной
Разрешено только в PHP 8.0.0 и более поздних версиях.
]]>
Throw как выражение
Разрешено только в PHP 8.0.0 и более поздних версиях.
getMessage();
}
?>
]]>
Наследование исключений
Пользовательский класс исключения определяют путём расширения
встроенного класса Exception. Ниже показаны методы и свойства класса Exception,
которые доступны дочерним классам.
Встроенный класс Exception
]]>
В конструкторе класса-наследника нужно также вызвать конструктор родительского класса —
parent::__construct(),
когда класс расширяет класс Exception и переопределяет конструктор,
чтобы гарантировать, что родительский класс правильно присвоил значения доступным данным.
Метод __toString() допустимо переопределять,
чтобы настроить пользовательский вывод, когда с объектом исключения работают как со строкой.
Исключения нельзя клонировать.
Попытка клонировать
исключение приведёт к фатальной ошибке E_ERROR.
Наследование класса Exception
code}]: {$this->message}\n";
}
public function customFunction() {
echo "Пользовательская функция для этого типа исключения\n";
}
}
/**
* Создадим класс для тестирования исключения
*/
class TestException
{
public $var;
const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;
function __construct($avalue = self::THROW_NONE) {
switch ($avalue) {
case self::THROW_CUSTOM:
// Выбрасываем собственное исключение
throw new MyException('1 — неправильный параметр', 5);
break;
case self::THROW_DEFAULT:
// Выбрасываем встроеное исключение
throw new Exception('2 — недопустимый параметр', 6);
break;
default:
// Без исключения, PHP создаст объект
$this->var = $avalue;
break;
}
}
}
// Пример 1
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) { // Будет перехвачено
echo "Поймано собственное переопределённое исключение\n", $e;
$e->customFunction();
} catch (Exception $e) { // Будет пропущено
echo "Поймано встроенное исключение\n", $e;
}
// Продолжить выполнение
var_dump($o); // Null
echo "\n\n";
// Пример 2
try {
$o = new TestException(TestException::THROW_DEFAULT);
} catch (MyException $e) { // Тип исключения не совпадёт
echo "Поймано переопределённое исключение\n", $e;
$e->customFunction();
} catch (Exception $e) { // Будет перехвачено
echo "Перехвачено встроенное исключение\n", $e;
}
// Продолжить выполнение
var_dump($o); // Null
echo "\n\n";
// Пример 3
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (Exception $e) { // Будет перехвачено
echo "Поймано встроенное исключение\n", $e;
}
// Продолжить выполнение
var_dump($o); // Null
echo "\n\n";
// Пример 4
try {
$o = new TestException();
} catch (Exception $e) { // Будет пропущено, т.к. исключение не выбрасывается
echo "Поймано встроенное исключение\n", $e;
}
// Продолжить выполнение
var_dump($o); // TestException
echo "\n\n";
?>
]]>