With PHP 7 errors and exceptions are undergoing major changes. For the first time, the PHP engine will start to emit exceptions instead of standard PHP errors for (previously) fatal, and catchable fatal errors. This means that we can now handle them much more gracefully with try... catch
.
But with this change, comes a whole new exception hierarchy:
View this code snippet on GitHub.
At the top we now have an interface, Throwable
, which the original Exception
implements. Earlier versions did not have the interface and the root of the hierarchy was Exception
. We then have the new Error
exception, which is a sibling of Exception
as opposed to extending it, which also implements the new interface.
The reason Error
does not extend Exception
is so that the new exceptions will not get accidentally caught by legacy catch-all statements (catch (Exception $e) { }
) — and just like in older PHP versions, an uncaught exception is still a regular fatal error, preserving backwards compatibility.
If the ability to create a real catch-all is desired, you can catch the Throwable
interface. This means that to catch both regular exceptions, and engine exceptions, you would use catch (Throwable $e) { }
instead.
Error Exceptions
As you can see above, there are four new error exceptions, each one used for a different purpose:
Error
Standard PHP fatal, and catchable-fatal are now thrown as Error
exceptions. These will continue to cause a “traditional” fatal error if they are uncaught.
AssertionError
With PHP 7, we also have enhancements to assertions, using the assert()
function, with the addition of zero-cost assertions, and the ability to have them throw exceptions. To enable this, you should simply set assert.exception
to 1
in your php.ini (or via ini_set()
).
These exceptions are (you guessed it) AssertionError
exceptions.
ParseError
Thanks to error exceptions, you can now handle includes with parse errors, and eval()
parse errors, as both now throw ParseError
exceptions:
View this code snippet on GitHub.
TypeError
With the introduction of scalar, and (especially) strict types in PHP 7, these will also throw exceptions when a type mis-match occurs. It is important to understand that this does not apply only to scalar type hints, but to traditional type hints such as class/interface names, callable
and array
.
Catchable Fatal Errors
Another important change in PHP 7 is with catchable fatal errors. Previously, these would have been caught and handled using set_error_handler()
. However, with PHP 7, they are now Error
exceptions, which, because an uncaught exception is now a real fatal error, will no-longer be catchable in set_error_handler()
.
This is a backwards compatibility break and means that to work in both PHP 5.x and 7, you need to use both set_error_handler()
and try... catch
.
This is considered a minor BC break due to limited usage.
Throwable and Userland
It would not be a big jump to conclude that now we have a common interface, we could create our own branches in the exception hierarchy for completely custom exceptions by simply implementing the Throwable
interface. Unfortunately, due to the fact that exceptions are magical under the hood, to be able to do things like capture line/file and stack trace information — this means that you still must still extend either Exception
or Error
, and cannot directly implement Throwable
alone.
Trying to implement Throwable
results in the following:
View this code snippet on GitHub.
However, this is not the full story. You can extend Throwable
and then — while still extending Error
or Exception
— you can implement your extended interface:
View this code snippet on GitHub.
Fin
As alluded to in the (pun intended) title of this post, these changes are actually quite big, allowing us to gracefully handle almost all previously fatal errors. The fact that the core team were able to maintain almost complete backwards compatibility while doing so is astounding. Kudos to them!
Source: Davey Shaifk