Исключения в PHP 7
С PHP 7 к нам приходит новая эпоха обработки исключений. Впервые PHP стал выкидывать исключения для фатальных и восстанавливаемых фатальных ошибок. Теперь мы можем их обрабатывать, как захотим. Больше никаких @, include, file_exists и прочей не логичности в коде.
В корне исключений в PHP 7 теперь лежит интерфейс Throwable от которого происходит реализация (implementations) всех корневых исключений и исключений для фатальных и восстанавливаемых фатальных ошибок (если не обработать, ошибки останутся).
Throwable (интерфейс) ├── Exception (реализует Throwable) │ ├── LogicException (расширяет Exception) │ │ ├── BadFunctionCallException (расширяет LogicException) │ │ │ └── BadMethodCallException (расширяет BadFunctionCallException) │ │ ├── DomainException (расширяет LogicException) │ │ ├── InvalidArgumentException (расширяет LogicException) │ │ ├── LengthException (расширяет LogicException) │ │ └── OutOfRangeException (расширяет LogicException) │ └── RuntimeException (расширяет Exception) │ ├── OutOfBoundsException (расширяет RuntimeException) │ ├── OverflowException (расширяет RuntimeException) │ ├── RangeException (расширяет RuntimeException) │ ├── UnderflowException (расширяет RuntimeException) │ └── UnexpectedValueException (расширяет RuntimeException) └── Error (реализует Throwable) ├── AssertionError (расширяет Error) ├── ParseError (расширяет Error) └── TypeError (расширяет Error)
Новые исключения Error
Как вы уже смогли заметить из таблицы иерархий, в PHP 7 добавились новые исключения для отлавливания фатальных (E_ERROR) и восстанавливаемых ошибок (E_RECOVERABLE_ERROR).
Но к сожалению, определенные ошибки, например «out of memory», по прежнему приведут к остановке, как их вообще обрабатывать? 😀
Error
Все фатальные ошибки будут выбрасывать исключение расширяющие Error исключения. Ошибки при этом, будут выведены как и раньше, если исключение не обработано.
AssertionError
В PHP 7, есть усовершенствование утверждений, используя assert() функцию, с добавлением нулевой стоимостью утверждений, и выбрасыванием исключений. Для включения данной возможности, нужно установить параметр assert.exception = 1 в php.ini (или с использованием ini_set()).
ini_set('zend.assertions', 1); ini_set('assert.exception', 1); $a = 1; assert($a === 0); // Result Fatal error: Uncaught AssertionError: assert($a === 0)
ParseError
Благодаря выкидыванию исключений ParseError при ошибке включения файла, теперь можно обработать поведение:
<?php try { include 'parse-error.php'; } catch (ParseError $e) { // parse error! }
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
Отлавливаем фатальные ошибки в PHP7
Еще одно важное изменение PHP7 в отлавливании фатальных ошибок. Раньше, они ловились и обрабатывались с использованием set_error_handler()
. Теперь, в PHP 7, они выкидывают Error
исключения, и если, исключения не будут обработаны, то выбрасывается реальная фатальная ошибка, которая больше не будет доступна в set_error_handler()
.
Это было сделано для обратной совместимости и для работы в PHP 5.x и 7, вы должны использовать bothset_error_handler()
и try... catch
.
Использование Throwable
В PHP 7, у нас есть общий интерфейс для исключений, и мы могли бы создать наши собственные исключения в иерархии исключений для полной настройки исключений (извиняюсь за тавтологию), просто реализуя Throwable интерфейс. Но к сожалению, мы этого сделать не можем, это означает, что мы все еще должны по-прежнему расширять либо Exception, либо Error, и не можем непосредственно реализовывать Throwable в пользовательских классах.
<?php class MyException implements Throwable { } ?> Fatal error: Class MyException cannot implement interface Throwable, extend Exception or Error instead
Но это еще не вся история, есть лайфхак, который не рекомендуется к использованию. Вы можете расширить интерфейс Throwable
— а уже потом, реализовать свои исключения типов Error
или Exception используя свой расширенный интерфейс.
<?php namespace MyLibrary; interface MyPackageException extends Throwable { public function someMethod(); public function someOtherMethod(); } class MyException extends Exception implements MyLibraryMyPackageException { } ?> Fatal error: Class MyLibraryMyException contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (MyLibraryMyPackageException::someMethod, MyLibraryMyPackageException::someOtherMethod)
Заключение
Изменения в исключениях на самом деле довольно значительные, теперь PHP позволяет нам изящно обрабатывать почти все фатальные ошибки. Тот факт, что разработчики PHP смогли сохранить почти полную обратную совместимость, не может не радовать!