В прошлом, обрабатывать фатальные ошибки было практически невозможно. Обработчик, установленный
В PHP 7 при возникновении фатальных ошибок (E_ERROR) и фатальных ошибок с возможностью обработки (E_RECOVERABLE_ERROR) будет выброшен exception, а не произойдет завершение скрипта. Но определенные ошибки, например «out of memory», по прежнему приведут к остановке. Не перехваченные ошибки в PHP 7, будут «фатальны», так же как и в php 5.*.
Исключения выброшенные из E_ERROR и E_RECOVERABLE_ERROR не наследуются от Exception. Это разделение было сделано, чтобы предотвратить обработку этих ошибок кодом, написанным под 5.*. Исключения для фатальных ошибок теперь являются экземпляром нового класса: Error. Как и любые другие исключения, Error может отловлен, обработан и выполнен finally блок.
Оба класса, и Error и Exception реализуют новый интерфейс Throwable.
Новая иерархия исключения состоит в следующем:
Если 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. В пользовательских классах нет необходимых параметров, для хранения этой информации.
Практически все ошибки (E_ERROR, E_RECOVERABLE_ERROR) в PHP 5.x, в PHP 7 выбрасывается экземпляром Error. Как и любые другие исключения, Error может быть пойман используя try/catch блок.
Большинство ошибок, которые были «фатальны» в PHP 5.x в PHP 7 буду выбрасывать простые Error объекты, но некоторые будут выбрасывать объекты подклассов: TypeError, ParseError и AssertionError.
Экземпляр TypeError выбрасывается, когда аргументы метода или возвращаемое значение не совпадает с объявленным типом.
ParseError выбрасывается, когда подключаемый (путем include/require) файл или код в eval содержит ошибки синтаксиса.
Когда условие, заданное методом assert() не выполняется, выбрасывается AssertionError:
Метод assert() выполняется и выбрасывается AssertionError только, если они включены в настройках: zend.assertions = 1 и assert.exception = 1.
Мы можем использовать класс Error, а также расширить Error, создав собственную иерархию класса Error. Это порождает вопрос: какие исключение должен выбрасывать Exception, а какие Error?
Error должен использоваться для указания проблем в коде, требующих внимания программиста (такие как неправильный тип входящих данных и синтаксические ошибки). Exception должен использоваться, когда исключение может «безопасно» обработаться, и выполнение программы может продолжиться.
Поскольку, объекты Error не могут быть обработаны во время выполнения программы, «ловля» Error должна быть редкостью. В целом, Error должны быть пойманы только для логирования их, необходимой «чистки данных», и отображения ошибки для пользователя.
Чтобы поймать исключения и в php 5.x и в php 7, используя один код, используем несколько блоков catch, ловим Throwable первым, затем Exception. После того, как поддержка PHP 5.x не потребуется, можно просто удалить блок ловли Exception.
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, то выглядит это так:
interface Throwable
{
public function getMessage(): string;
public function getCode(): int;
public function getFile(): string;
public function getLine(): int;
public function getTrace(): array;
public function getTraceAsString(): string;
public function getPrevious(): Throwable;
public function __toString(): string;
}
Этот интерфейс должен быть знаком. Методы Throwable практически идентичны методам Exception. Разница лишь в том, что Throwable::getPrevious() может вернуть любой экземпляр Throwable, а не просто Exception. Конструкторы Exception и Error принимают любой экземпляр Throwable как предыдущее исключение.
Throwable может быть использован в блоке try/catch для отлова и Exception и Error (и любых других возможных в будущем исключений). Помните, что хорошей практикой является «ловля» исключений определенным классом исключений и обработка каждого типа отдельно. Но и иногда требуется отлавливать любое исключение. В PHP 7 try/catch блок для всех исключений должен использовать Throwable вместо Exception.
try {
// Code that may throw an Exception or Error.
} catch (Throwable $t) {
// Handle 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
Когда условие, заданное методом assert() не выполняется, выбрасывается 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
}
Комментарии (8)
skobkin
01.07.2015 11:08+4Хорошо что они отошли от путающих BaseException, его наследника EngineException и пришли к Throwable и Error. Главное чтобы ещё сообщество ими правильно воспользовалось и тогда работать с PHP станет гораздо удобнее.
korchasa
02.07.2015 19:29-1Ошибки можно было ловить начиная с 5.2, используя register_shutdown_function + error_get_last.
cyberorg
PHP7 становится очень похож на Java. Например, если в последнем фрагменте кода убрать $ перед переменными, то будет код на Java (который, кстати, не скомпилируется из-за exception has already been caught)
Lailore
Если так судить, то последний блок это c#. Только $ убрать
FractalizeR
Разве C# и Java не похожи? :) Конечно, можно долго спорить и том, что именно можно называть «похожестью», но так уж сложилось, что многие конструкции почти во всех ООП языках выглядят «похожим» образом.
cyberorg
А в C# оно скомпилируется?
Prototik
По секрету: в java в именах переменных разрешено использовать $. И юникод.
cyberorg
Можно, но мало кто так делает. Плюс в Java $ есть при обозначении inner class'ов