Исключения Модель исключений PHP напоминает модель исключений других языков программирования. PHP умеет выбрасывать — &throw; — и ловить — &catch; — исключения. Код заключают в блок &try;, чтобы упростить обработку вероятных исключений. Каждому блоку &try; указывают как минимум один блок &catch; или &finally;. Исключение будет «всплывать» по стеку вызовов функций, пока не найдёт блок &catch;, если функция выбросила исключение, а в текущей области видимости функции, которая её вызвала, нет блока &catch;. PHP выполнит каждый блок &finally;, который встретит по пути. Программа завершается фатальной ошибкой, когда стек вызовов не встречает блок &catch; и разворачивается до глобальной области видимости, если разработчик не установил глобальный обработчик исключений. В коде допустимо выбрасывать только объект исключения, тип которого при проверке оператором &instanceof; соответствует интерфейсу Throwable. Попытка выбросить объект, который не выполняет это условие, приведёт к фатальной ошибке PHP. С PHP 8.0.0 ключевое слово &throw; стало выражением, которое разрешили записывать в контексте выражения. В предыдущих версиях это слово было инструкцией и её записывали в отдельной строке. <literal>catch</literal> Блок &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;, но у блока не будет доступа к объекту, который выбросил код, если переменную не указали. <literal>finally</literal> Блок &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"; ?> ]]>