Handling PHP errors paves the way for resilient code
For the past 72 articles, we’ve lived life on the optimistic side. Never once did we stop to think that errors might be occurring. We are programmers and we are great at what we do. Why would we think that we should ever worry about error handling? Oh, the users that use our application. Who cares? They should know how to enter valid data when prompted. When we say that they should enter their age, clearly we mean that this should be a number (integer) and not a string. If they don’t know how to follow directions, that’s on them. They might brake the database you say? Okay, that’s more of a concern.
As terrible as it is to say out loud, treat each user of your application like an absolute idiot. Don’t say that to their face, but think it behind the scenes. I wouldn’t blame you even if you chuckled as you allow yourself to scream “idiot” inside your head. You wouldn’t be too far from the truth. People will find ways to break your application, intentionally or unintentionally. It’s up to you to guard it with all your might.
https://blog.devgenius.io/php-p72-errors-intro-83e7b58700d5
Getting Serious about Errors
As of PHP 7, errors are reported by throwing the Error
exception. What are type of errors that we can encounter?
ArithmeticError
is the error that’s thrown during a failed mathematical operation.DivisionByZeroError
is a type ofArithmeticError
that occurs when you try to divide by zero.AssertionError
is thrown when an assertion fails.CompileError
is thrown for compilation errors.ParseError
is a type ofCompileError
that occurs while parsing PHP code. You’ll see this error occur, for example, with the use of theeval()
function.TypeError
occurs when the data type is not correct in a few scenarios, such as with class property mismatch, when argument types do not match declared parameter types, or when the return type is different from the declaration.ArgumentCountError
is a type ofTypeError
. It occurs when a method has a predefined set of parameters but too few arguments are passed to it.ValueError
is a tricky one. It occurs when the datatype is correct, but the expected value is incorrect. For example, expecting positive integers but receiving a negative one.UnhandledMatchError
occurs when there’s no arm for the match expression.FiberError
is thrown on invalid operations on aFiber
function.
Not catching an error
We can create an error quickly in PHP to test and see what happens. Create a new php file and write: echo 10/0;
You’ll get an error.
Fatal error: Uncaught DivisionByZeroError: Division by zero in /home/user/scripts/code.php:3
Stack trace:
#0 {main}
thrown in /home/user/scripts/code.php on line 3
There’s our DivisionByZero
error that we looked at earlier. The interesting part is that it says Uncaught
. We didn’t catch
it. But how do we catch an error? With the try/catch
block.
Try/Catch
A try/catch block represents a way for you to test something out, and if it doesn’t work, catch the error before it’s displayed on the screen and provides the user with negative UX.
<?php
try
{
// Try some code here
}
catch( SomeErrorCode $error )
{
// If your code failed above, catch it if it matches the catch Error code
}
What could we try inside of our try
block? We could try to execute our echo 10/0;
statement. Place that into your try { }
block body.
<?php
try
{
echo 10 / 0;
}
catch( SomeErrorCode $error )
{
// If your code failed above, catch it if it matches the catch Error code
}
We know that in this instance the error that will be thrown is the DivisionByZero
error. We saw it we tried out the code earlier and PHP returned that there was an Uncaught
error. A DivisionByZero
is just a datatype. It’s a datatype that was created by PHP developers. Think of it as a string
or an int
datatype. If you read my previous articles, you’ll remember that you can declare the datatype of the parameter.
https://blog.devgenius.io/php-p48-type-declarations-47114a7a0e0b
Let’s catch our DivisionByZero
error. We’ll catch it inside of our catch()
statement.
<?php
try
{
echo 10 / 0;
}
catch( DivisionByZeroError $error )
{
// If your code failed above, catch it if it matches the catch Error code
}
Run the code. A blank screen should appear. We caught the error and prevented it from appearing on the screen! It’s now stored in our $error
variable. We can handle it gracefully like sending out an email stating that there was an error that occurred or logging it to our Log file. We could also just echo out something more user friendly.
<?php
try
{
echo 10 / 0;
}
catch( DivisionByZeroError $error )
{
echo "Dear idiot user. You cannot divide by zero";
}
Run the code again and you’ll see that the message Dear idiot user. You cannot divide by zero
is displayed on the screen.
To see what the $error object holds, just use var_dump
and dump it to the screen.
/app/72 Errors/72 Errors Intro.php:10:
object(DivisionByZeroError)[1]
protected 'message' => string 'Division by zero' (length=16)
private 'string' (Error) => string '' (length=0)
protected 'code' => int 0
protected 'file' => string '/app/72 Errors/72 Errors Intro.php' (length=34)
protected 'line' => int 5
private array 'trace' (Error) =>
array (size=0)
empty
private ?Throwable 'previous' (Error) => null
public 'xdebug_message' => string '<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> DivisionByZeroError: Division by zero in /app/72 Errors/72 Errors Intro.php on line <i>5</i></th></tr>
<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>
<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align=''... (length=842)
Why Test Such a Thing?
Why would we want to test such a thing? We know that 10 / 0
will always throw the DivisionByZeroError
, so why even bother? User input. We may get input from the user and have something like this instead:
echo 10 / $_POST['user_number'];
The user can then type in whatever they want in the form, and when they click submit, the information is sent over to be processed. If they enter 0, the error will be caught.
Chaining Catch Statements
What if they enter something other than a number? What if the user enters a letter? We get a new type of error.
Fatal error: Uncaught TypeError: Unsupported operand types: int / string in /home/user/scripts/code.php:3Stack trace: #0 {main} thrown in /home/user/scripts/code.php on line 3
The error that’s received is a TypeError
. How do we catch it? We’ve already caught our DivisionByZeroError
. Where do we place the catch for the TypeError
. You’ll just chain onto it.
A More Generic Catch
Could there be other errors that arise? Of course. If we want to make sure that all of our errors are handled, at the end of the try/catch/catch/…/catch statement, we’ll need to add a more generic catch statement that catches anything that might have been missed. Just like you can catch a division by zero error with both the DivisionByZeroError
and the ArithmeticError
handlers (since DivisionByZeroError is of type ArithmeticError), you can catch any error with the Error handler. Everything that we looked at above, like TypeError
, is of type Error
.
<?php
try
{
echo 10 / 0;
}
catch( DivisionByZeroError $error )
{
echo "Dear idiot user. You cannot divide by zero";
}
catch( TypeError $error )
{
echo "Dear idiot user. You must use a number";
}
catch( Error $error )
{
echo "Dear idiot user. I have no idea how you got here but I caught it";
}
The Finally Statement
What if you wanted to execute a piece of code each time, regardless of whether an error is caught or not? You can do that with the finally
statement.
If you run the code above, you’ll get:
Dear idiot user. You cannot divide by zero
Dear idiot user. This code always executes.
Conclusion
I think we went as deep into errors as I would like to go in this article. They’re pretty straight forward. I’ve seen developers struggle with the try/catch concept. Just think of it like an if/else statement where if = try and else = catch. If you have multiple conditions, you start introducing the if/elseif/else statement. Similar concept with try/catch/catch.
PHP ERRORS SIGNAL CODE HICCUPS THAT NEED ATTENTION
Bugs are normally errors in code that the programmer coded, that produce a result, and that result is not correct.
Handling PHP errors paves the way for resilient code
We are programmers and we are great at what we do. Why would we think that we should ever worry about error handling?
FORMS CAPTURE DATA, CONNECTING USERS AND SYSTEMS.
We’ve gone through enough syntax that we’re ready to start looking at some actual concrete examples, like form processing. We’re going to introduce databases and data-persistence in a later article. All we want to know is how do we send data from a form on the client end to a server, wherever that server may live.