DEV Community

Cover image for Good Practices: handling error and exceptions in PHP
anastasionico
anastasionico

Posted on

Good Practices: handling error and exceptions in PHP

About this series:


In this series, we are exploring what are the best practices a web developer must take care of when creating or managing PHP code.
Sanitize, validate and escape
Security and managing passwords
Handling error and exceptions

Introduction


21st October 1879 19:15:13

Menlo Park, New Jersey


A strong breeze is cooling down the evening,
​ ​
the weather has not been kind lately and the rain has been fallen continuously for the past 2 weeks and it does not seem it wants to stop anytime soon.
​​
inside a factory, several men are working like hell on something that has the potential to change the world as they know it.
​​

​​
The final test will happen in a few minutes and it will be the result of weeks of research, hard work and calculation.
​​
3
​​
2

1
​​
POP!!
​​
A flashlight up the room for a second then dark again, another day has passed, another failed one, another error.
​​
That’s the 9’999th now.
​​

“I have not failed. I’ve just found 10,000 ways that won’t work.”
Thomas Edison

​​
If you have ever heard motivational speaker like Brian Tracy, Tony Robin, Zig Ziglar or Jim Rohn you know that all of them have a problem with the way schools run around the world. 
​​
In university and colleges, errors are punished rather than welcomed, 
​​
In the real world though, errors are the only way you can become a better one.
​​
This is especially true in web development.

Trial and errors are definitely the best way to become great and to craft your skill.

It is also true that we need to be careful and pay attention to the error we make. 

The reason for making errors is that we can manage and learn from them.

PHP make it easy to handle, manage and learn by error that can occur in your code.

How to manage errors in PHP 7

If you ask me what are the features that have improved the most between PHP 5.6 and PHP 7 or PHP 7.1 I will surely put error handling in the top 3 positions.
​​
The seventh version of our beloved programming language brought to us an interface with a defined goal:
​​
it needed to be easily implemented and be useful in lots of cases.
​​
The throwable interface,
​​
in fact, can be implemented by both types of class (Error class and Exception) and supply and a handful of methods that we can use to better understand and analyze our error.

Here you can see the class and how can you use it:

Throwable {
    abstract public getMessage ( void ) : string
    abstract public getCode ( void ) : int
    abstract public getFile ( void ) : string
    abstract public getLine ( void ) : int
    abstract public getTrace ( void ) : array
    abstract public getTraceAsString ( void ) : string
    abstract public getPrevious ( void ) : Throwable
    abstract public __toString ( void ) : string
}
Try {
    // some code
} catch (Throwable $e) {
    echo "an instance of class Throwable has been caught";
}

In the snippet above are showed all the methods you can use to debug a PHP error in your script (with lots of type hinting)

and the actual code you can use to try a block of code and throw an error or an exception.

Errors, errors everywhere!


Whoever has dealt with error prior to PHP 5.6 know what type of pain it was and the headache he needed to deal with during the debugging phase.

Previously errors were produced within the engine and you could have handled it as long as they weren’t fatal errors.

You also needed to consider that errors and exception were two completely different things in your code and it added another layer of difficulty to the already complicated situation.

From PHP 7 onwards, fatal errors result in error exception being thrown and you can easily manage not fatal errors with bespoke methods

I can, for instance, run some code and only throw an error in case of a fatal occurs.

try {
    echo $thisVariableIsNotSet;
    inexistentFunctionInvoked();
} catch (Error $e) {
    echo "Error: $e->getMessage()";
}
// Error: Call to undefined function inexistentFunctionInvoked();


If this is the first time you handle errors or you see a try-catch this may seem confusing you may feel you want to step back a bit,

Don't worry, in Php Basics you will find all you need to know to be ready to read this article.

Bear with me while I explain what is happening:

In our pseudocode we have to element a variable and a funtion, both of them have not been defined , thus are unser or inexistent.

PHP handle an unset variable differently that a undefined function,

the first one is just an invalid variable a notice error, web developes work long hour, it can happen often,

the second error instead is an actual fuction, with maybe some important logic in it.

That is why in PHP an undefined function is a serious problem, a fatal error.

Having these two lines of code within the try block permits the catch to instanciate an instance of the Error class (the variable $e).

$e implements the Throwable interface, which means it can use all the method you saw in the previous paragraph. Hence $e->getMessage();

Creating a bespoke error handler


There may be occasions in which some errors that are not instace of the Error class occur.

If these errors are not fatal PHP allows you, as a developer, to define bespoke function and handle them the way you prefer.

To do so you need to use the set_error_handle() function.

This function accept either a string with the name of the function you want to use or an array that contains an object and the name of the method you are invoking.

The fuction can stop the script,

if you do not want it to continue or return a value and continue when the code invoked the function in the first place.

Let’s have a look at a practical yet easy example below.

set_error_handle() cannot manage fatal error, which means that, in order to test it, we need to simulate a warning (a division by zero will do),

then we’ll define the content of the function and catch the error when it occurs.

function myErrorHandler($errno, $errstr, $errfile, $errline) {
    echo "An error occurred in line {$errline} of file {$errfile} with message {$errstr}";
}

set_error_handler("myErrorHandler");

try {
    5 / 0;
} catch (Throwable $e) {
    echo $e->getMessage();
}

// An error occurred in line 1 of file /index with message Division by zero


Do not forget that you want to hide errors from the production environment when an error occurs it has to be visible only on local or a staging server.

In fact, showing an error in production can give a massive hint about the vulnerability of your website to a hacker or malicious users.

To do this you can edit 3 settings within your php.ini

  • display_errors if set to false it will suppress errors;
  • log_errors store errors into a log file;
  • error_reporting configures which type of errors trigger a report;

Error Handling Functions

PHP has several functions that can make handling errors and easy tasks in this section you will find a brief description for each of them.

  • debug_•backtrace() it accepts few parameters like options’ flag and a number that limits the results and generate a summary of how your script got where it is, it does it returning an array variable;
  • debug_•print_•backtrace() it works in a similar way as the previous one but instead of creating an array of trace it prints them in an inverted chronological order;
  • error_•get_•last() it returns an associative array that contains the info of the last error occurred in the script;
  • error_•clear_•last() reset the internal error log of PHP, if used before error_get_last() the latter will return null;
  • error_•log() it requires a mandatory message as a parameter and sends it to the defined error handling routines;
  • error_•reporting() This function requires either none or error constants as a parameter (you can find the complete list here https://www.php.net/manual/en/errorfunc.constants.php ) and set the level of accuracy the PHP application must have when handling errors;
  • set_•error_•handler() and set_•exception_•handler() set bespoke functions that handle errors and exception;
  • restore_•error_•handler() and restore_•exception_•handler() it is used after set_•error_•handler() and set_•exception_•handler() it aims to revert the handler to the previous error handler it could be the built-in or a user-defined function;
  • trigger_•error() this function triggers an error by accepting a message as a mandatory parameter and an error_type’s flag as a discretionary one;
  • user_•error() alias of trigger_•error();


The PHP manual provides the entire list of predefined constant you can use as flag in some of the function above

What are exceptions


Exceptions are relatively new features of PHP, they have been implemented only in PHP 5 but they quickly became a core part of any object-oriented programming script.

Exceptions are states of the scripts the require special treatment because the script is not running as it is supposed to.

Both Errors and Exception are just classes that implement the Throwable interface.

Like any other class in the OOP world, the can be extended,

which allows to create error hierarchies and create tailor the way you handle exceptions.

Something to pay attention to and that can lead to errors and misunderstandings is that you cannot declare your own class and then decide to throw exceptions.

The only classes that can throw error are the ones that implement the Throwable class.

Let's play a game, look at the code and answer the following question.

class MainException extends Exception {}
class SubException extends MainException {}

try {
    throw new SubException("SubException thrown");
} catch (MainException $e) {
    echo "MainException thrown" . $e->getMessage();
} catch (SubException $e) {
    echo "SubException thrown" . $e->getMessage();
} catch (Exception $e) {
    echo "Exception thrown" . $e->getMessage();
}


Which exception is thrown?

In the example below the exception thrown is the SubException, it inherits from MainException which extends Exception.

The block is evaluated from the first (the one on top) to the last and when the exception matches the name of the class given it is triggered.

This above can be considered the best practice because we narrow down the message we want to show and eventually.

In case none of our classes matches, we use the PHP class Exception for an overall check.

Catch multiple exceptions at once


Until now we have seen several tries with several catches.

Each block of code echo a message with different errors,

Think at the case we create a dozen of different exceptions,

should we add a dozen of catch blocks? And what if we want to handle several types of error in the same way?

To manage this situation PHP provided the pipe keyword “ | ”;

Here is an example of how to use it:

class MainException extends Exception {}
class SubException extends Exception {}

try {
    throw new SubException;
} catch (MainException |  SubException $e) {
    echo "Exception thrown: " . get_class($e);
}
// Exception thrown: SubException

The finally keyword


I am sure you have already seen a switch-case conditional structure in the past,
​​
if not I got an entire article about conditional statements in PHP.
​​
One of the elements of the switch statement is that if none of the cases is evaluated eventually a “default“ block, if present, is going to run.
​​
The try a catch allows to do something similar to your code by providing the keyword finally

try {
    $handle = fopen("c:\\folder\\resource.txt", "r");   
    // do some stuff with the file

    throw new Exception("Exception thrown");
} catch (Exception $e) {
    echo "Exception thrown" . $e->getMessage();
} finally {
    fclose ( resource $handle );
}


A common use for this keyword is, as shown in the example above, when we need to close a file that we have previously opened inside the try block.
​​
You must remember that the code inside the finally block is always executed, even if exceptions are thrown earlier in the script.
​​
But you are free to use it whenever you believe this will help your case.
​​


If you think this article was useful and you want to learn more about good practices in PHP click the image below

http___eepurl.com_dIZqjf.jpg



Conclusion

Thomas Edison came from the humble origin and it is confirmed by historical data that he wasn’t the most brilliant of the individual.
​​
Anyway, his capacity to surround himself by creative and passionate people, plus the ability to deeply analyze and learn from his error made him one of the best inventors ever lived, surely one of the most wealthy and famous of his era.
​​
With no hesitation, I can say that we as web developers can do the same.
​​
Maybe we will never invent a world’s famous application or change the life of billions of people with our website but there is no doubt that by learning from our mistake we will increase our skill way faster.
​​
Managing errors is a fundamental part of this.
​​
Learn how to properly do that and master all the possibility available in PHP and its components such as PHPUnit and Sentry will make this task easy for each of us.
​​
Useful components or website you can use to manage your error right now:
filp/whoops
nette/tracy
vimeo/psalm
sebastianbergmann/phpunit
codeception/codeception
getsentry/sentry
https://phpcodechecker.com
https://www.piliapp.com/php-syntax-check

Top comments (5)

Collapse
 
abhinav1217 profile image
Abhinav Kulshreshtha

Brother... check your formatting. Content is good but having two empty lines between lines really confuses the mind.

Collapse
 
anastasionico profile image
anastasionico

Hi Abhinav,
I wrote this way so it seems there is more content hahaha.
Joking aside, I didn't realized there was such a huge gap.
Thanks for letting me know on your feedback

Collapse
 
elcotu profile image
Daniel Coturel

Good post!

Collapse
 
anastasionico profile image
anastasionico

Thanks a lot, Daniel

Collapse
 
jonathands profile image
Jonathan DS

edison was an a*hole, the rest is great