Throwable exception и ошибки в php7
В прошлом, обрабатывать фатальные ошибки было практически невозможно. Обработчик, установленный set_error_handler вызван не будет, скрипт просто будет завершен.
В PHP 7 при возникновении фатальных ошибок (E_ERROR) и фатальных ошибок с возможностью обработки (E_RECOVERABLE_ERROR) будет выброшен exception, а не произойдет завершение скрипта. Но определенные ошибки, например «out of memory», по прежнему приведут к остановке. Не перехваченные ошибки в PHP 7, будут «фатальны», так же как и в php 5.*.
Обратите внимание, что другие виды ошибок, такие как warinng и notice остаются без изменения в php 7.
Исключения выброшенные из E_ERROR и E_RECOVERABLE_ERROR не наследуются от Exception. Это разделение было сделано, чтобы предотвратить обработку этих ошибок кодом, написанным под 5.*. Исключения для фатальных ошибок теперь являются экземпляром нового класса: Error. Как и любые другие исключения, Error может отловлен, обработан и выполнен finally блок.
Throwable
Оба класса, и Error и Exception реализуют новый интерфейс Throwable.
Новая иерархия исключения состоит в следующем:
interface Throwable |- Exception implements Throwable |- . |- Error implements Throwable |- TypeError extends Error |- ParseError extends Error |- AssertionError extends Error
Если Throwable определить в коде PHP 7, то выглядит это так:
Этот интерфейс должен быть знаком. Методы Throwable практически идентичны методам Exception. Разница лишь в том, что Throwable::getPrevious() может вернуть любой экземпляр Throwable, а не просто Exception. Конструкторы Exception и Error принимают любой экземпляр Throwable как предыдущее исключение.
Throwable может быть использован в блоке try/catch для отлова и Exception и Error (и любых других возможных в будущем исключений). Помните, что хорошей практикой является «ловля» исключений определенным классом исключений и обработка каждого типа отдельно. Но и иногда требуется отлавливать любое исключение. В PHP 7 try/catch блок для всех исключений должен использовать Throwable вместо Exception.
Пользовательские классы не могут реализовывать Throwable. Это было сделано для предсказуемости: только экземпляры Exception или Error могут быть брошены. Кроме того, исключения содержат информацию о том, где объект был создан в stack trace. В пользовательских классах нет необходимых параметров, для хранения этой информации.
Error
Практически все ошибки (E_ERROR, E_RECOVERABLE_ERROR) в PHP 5.x, в PHP 7 выбрасывается экземпляром Error. Как и любые другие исключения, Error может быть пойман используя try/catch блок.
try < $undefined->method(); // Throws an Error object in PHP 7. > catch (Error $e) < // Handle error >
Большинство ошибок, которые были «фатальны» в PHP 5.x в PHP 7 буду выбрасывать простые Error объекты, но некоторые будут выбрасывать объекты подклассов: TypeError, ParseError и AssertionError.
TypeError
Экземпляр TypeError выбрасывается, когда аргументы метода или возвращаемое значение не совпадает с объявленным типом.
function add(int $left, int $right) < return $left + $right; >try < $value = add('left', 'right'); >catch (TypeError $e) < echo $e->getMessage(), "\n"; > //Result: //Argument 1 passed to add() must be of the type integer, string given
ParseError
ParseError выбрасывается, когда подключаемый (путем include/require) файл или код в eval содержит ошибки синтаксиса.
try < require 'file-with-parse-error.php'; >catch (ParseError $e) < echo $e->getMessage(), "\n"; >
AssertionError
ini_set('zend.assertions', 1); ini_set('assert.exception', 1); $test = 1; assert($test === 0);
Fatal error: Uncaught AssertionError: assert($test === 0)
Метод assert() выполняется и выбрасывается AssertionError только, если они включены в настройках: zend.assertions = 1 и assert.exception = 1.
Использование Error в своём коде
Мы можем использовать класс Error, а также расширить Error, создав собственную иерархию класса Error. Это порождает вопрос: какие исключение должен выбрасывать Exception, а какие Error?
Error должен использоваться для указания проблем в коде, требующих внимания программиста (такие как неправильный тип входящих данных и синтаксические ошибки). Exception должен использоваться, когда исключение может «безопасно» обработаться, и выполнение программы может продолжиться.
Поскольку, объекты Error не могут быть обработаны во время выполнения программы, «ловля» Error должна быть редкостью. В целом, Error должны быть пойманы только для логирования их, необходимой «чистки данных», и отображения ошибки для пользователя.
Ловим исключения и в PHP 5.x и в PHP 7
Чтобы поймать исключения и в php 5.x и в php 7, используя один код, используем несколько блоков catch, ловим Throwable первым, затем Exception. После того, как поддержка PHP 5.x не потребуется, можно просто удалить блок ловли Exception.
try < // Code that may throw an Exception or Error. >catch (Throwable $t) < // Executed only in PHP 7, will not match in PHP 5.x >catch (Exception $e) < // Executed only in PHP 5.x, will not be reached in PHP 7 >