Best way to handle errors on a php page?
There are a lot of ways that you can deal with this and frankly none of them is intrinsically 'right'.
You will have to decide for yourself, which method is more 'comfortable' for you - it's always a mater of preferences (although there are certain techniques you should avoid and for good reasons).
It will highly depend on how you split your logic, however I tend to enclose all code that can return non-fatal errors inside a function, and use a return value of said function to indicate there was an error.
For fatal errors I tend to use exceptions (with try-catch
blocks).
Now just to be clear:
- A non-fatal error is an error that you can recover from - meaning that even though something went wrong, there is still some code that can be executed and generate some valuable output. For example if you wanted to get current time using
NTP
protocol, but the server didn't respond, you can decide to use localtime
function and still display a some valuable data to the user. - A fatal error is an error that you would not be able to recover from - meaning that something really bad happened and the only thing you can do is tell your user that page cannot do what it was asked to. For example if you were fetching some data from your database and got
SQL Exception
- there is no valuable data to be shown and you can only inform the user of this.
Non-Fatal Errors (using function return)
A good example of using function-returns as a way of dealing with non-fatal problems would be a function that is trying to display content of some file on the page when this is not the main objective of the page (for example you would have a function that displays badges, fetched from a text file, on every single page - I know that this is far fetched but bear with me).
function getBadge($file){
$f = fopen($file,'r');
if(!$f){
return null;
}
.. do some processing ..
return $badges;
}
$badges = getBadges('badges.txt');
if(!$badges){
echo "Cannot display badges.";
} else {
echo $badges;
}
.. carry on doing whatever page should be doing ..
In fact, the function fopen
itself is an example of this - it will return.
Returns a file pointer resource on success, or FALSE on error.
Fatal-Errors (using exceptions - try-catch)
When you have some piece of code that needs to be executed because it's exactly what the user wanted (for example reading all news from database and displaying them to the user), you could use exceptions. Let's take a simple example - a user visited his profile and wanted to see all the messages he's got (let's assume, for now, that they are stored in plain text). You might have a function like:
function getMessages($user){
$messages = array();
$f = fopen("messages_$user.txt","r");
if(!$f){
throw new Exception("Could not read messages!");
}
... do some processing ...
return $messages;
}
And use it like this:
try{
..do some stuff..
$messages = getMessages($_SESSION['user'])); //assuming you store username in $_SESSION
foreach($messages as $msg){
echo $msg."<br/>";
}
} catch(Exception $e){
echo "Sorry, there was an error: ".$e->getMessage();
}
Now this could come in handy, if you had a 'top-level' script that would execute all the other code. That means that, for example, in your index.php
you would just have:
try{
.. execute some code, perform some functions ..
} catch(Exception $e){
echo "Sorry, there was an error: ".$e->getMessage();
}
Do not overuse exceptions!
Whatever you do, never use exceptions as a way to check something you can recover from. Have a read on another question(full credit goes to Anton Gogolev for a very good explanation on this, as well as other answer-ers) as to why this is the case.
Further reading
Now there is no better way to learn how to deal with errors than to try several things and see what is good for you. You might find the below useful:
- W3School on PHP Exception handling
- Short tutorial on error handling(similar to my function-returns method)
-
Extensive tutorial on PHP error handling - including using
trigger_error()
function, which I haven't mentioned because I don't use it and don't know much about it, but apparently it's really useful. This is a particularly good read.
Hope this helps :)
PHP has a built in class, ErrorException, for translating PHP errors into exceptions, which if unhandled, would naturally stop execution.
Exceptions have improved error handling mechanisms (try catch) and better debugging information (stack traces).
Include this at the top most of your execution path (the config, or something which is included first with all your code):
set_error_handler(function($nNumber, $strMessage, $strFilePath, $nLineNumber){
throw new \ErrorException($strMessage, 0, $nNumber, $strFilePath, $nLineNumber);
}, /*E_ALL*/ -1);
Although PDO supports throwing exceptions, it is off by default, you have to enable it:
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
If using MySQL, you also want an error for not setting mandatory fields and many other errors/warnings forgiven by default:
$pdo->exec("SET sql_mode = 'STRICT_ALL_TABLES'");
Exceptions can be handled like in many other programming languages using try catch finally:
try
{
echo $iAmAnUndefinedVariable;
}
catch(\Throwable $exception)
{
/*[...]*/
}
When validating stuff, just throw exceptions: throw new Exception("Missing URL variable userId!");
It would be nice if PHP made a clean break someday from the legacy error reporting thing and just throw exceptions by default (deprecate error_reporting() and change the default).
This is much more elegant and readable.
try
{
if($_GET['something'] != 'somevalue')
{
throw new Exception ('something is not a valid value');
}
$output .= 'somecode';
// make a DB query, fetch a row
//...
$row = $stmt->Fetch(PDO::ASSOC);
if($row == null)
{
throw new Exception ('the row does not exist.');
}
$output .= 'morecode';
if(somethingIsOK())
{
$output .= 'yet more page output';
}
else
{
throw new Exception ('something is most definitely not OK.');
}
echo $output;
}
catch (Exception $e)
{
echo $e->getMessage();
}
Using try-catch
is one of the cleanest solutions you can use.
I have made an example that still displays the header and footer when an error occurs, using your code converted to the try-catch
format:
PHP:
<?php
try {
$output = array();
if($_GET['something'] != 'somevalue') throw new Exception('something does not have a valid value.');
$output[] = 'Some Code';
$row = mt_rand(0, 10) < 5 ? null : mt_rand(0, 100);
if($row === null) throw new Exception('The row does not exist.');
$output[] = $row;
if(!somethingIsOK()) throw new Exception('Something is most definitely not OK.');
$output[] = 'Yet more page output';
} catch(Exception $e) {
$output[] = 'Error: ' . $e->getMessage(); // To show output and error
$output = array('Error: ' . $e->getMessage()); // To only show error
}
function somethingIsOK() {
return mt_rand(0, 10) < 5;
}
?>
HTML:
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>PHP Error test</title>
<style type="text/css">
body {
background: #eee;
text-align: center
}
#content {
padding: 60px
}
#header {
padding: 30px;
background: #fff
}
#footer {
padding: 10px;
background: #ddd
}
</style>
</head>
<body>
<div id="header">Header</div>
<div id="content">
<?php echo implode('<br />', $output); ?>
</div>
<div id="footer">Footer</div>
</body>
</html>
References:
- PHP: Exceptions - Manual
- PHP: CATCH - Manual