36

Right now my pages look something like this:

if($_GET['something'] == 'somevalue')
{
    $output .= 'somecode';

    // make a DB query, fetch a row
    //...
    $row = $stmt->Fetch(PDO::ASSOC);

    if($row != null)
    {
        $output .= 'morecode';

        if(somethingIsOK())
        {
            $output .= 'yet more page output';
        }
        else
        {
            $error = 'something is most definitely not OK.';
        }
    }
    else
    {
        $error = 'the row does not exist.';
    }
}
else
{
    $error = 'something is not a valid value';
}

if($error == '') // no error
{
    //display $output on page
}
else // an error
{
    // display whatever error occurred on the page
}

The way I'm doing things works, but it's very cumbersome and tedious for what is probably obvious: suppose that I call a function somewhere in the middle of my code, or want to check the value of a variable, or verify a DB query returned a valid result, and if it fails I want to output an error? I would have to make another if/else block and move all of the code inside the new if block. This doesn't seem like a smart way of doing things.

I have been reading about try/catch and have been thinking of putting all of my code inside a try statement, then let the code run sequentially without any if/else blocks and if something fails just throw an exception. From what I've read, that would halt the execution and make it jump straight to the catch block (just as a failed if statement will go to the else block), where I could then output the error message. But is that an acceptable or standard practice?

What's the best way of handling errors, fatal or not, in a php application that builds and outputs an HTML page? I don't want to just die with a blank screen, as that would be very user un-friendly, but instead want to output a message in the body of the page, still allowing the header and footer to show.

Thanks for your advice!

Nate
  • 22,356
  • 31
  • 115
  • 196
  • I use `exit($error)` myself, like: `if($some_error == TRUE){exit('Error.');}`Hopefully it's not a bad practice. – Abdulaziz Aug 10 '12 at 02:07
  • 1
    @AzizAG - not a bad idea, but I have a wrapper on my pages that includes a header and footer and if there is an error I want it to appear in a graphically pleasing manner. (i.e. I don't want to just die with a blank white page with an error message). – Nate Aug 10 '12 at 02:16
  • @Nate I lied, I never use `exit` directly. I have a function named `exitApp($error)` which : (1) displays the header (2) echo the `$error` (3) displays the footer (4) and finally `exit`ing the application. – Abdulaziz Aug 10 '12 at 04:23
  • @AzizAG that's what uncaught exceptions do, so you might just switch to exceptions. See my answer, it shows you how to enable PDO to throw exceptions and how to transform all PHP errors into exceptions (PHP has the ErrorException class especially for this purpose). – Tiberiu-Ionuț Stan Aug 14 '12 at 12:22

8 Answers8

35

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 local time 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:

Hope this helps :)

Community
  • 1
  • 1
Bart Platak
  • 3,997
  • 4
  • 23
  • 46
  • By the way, I assume you mean `badges.txt` not `bages.txt`? – uınbɐɥs Aug 18 '12 at 21:31
  • @ShaquinTrifonoff: Ah yes indeed, a typo :) Thanks for the edit! :) – Bart Platak Aug 18 '12 at 21:42
  • Agree with most of this, except for the warning against overuse of exceptions. Java, for instance, uses them everywhere. Python uses a StopIteration exception, which should seriously anger the anti-exception crowd. :) – pestilence669 Aug 18 '12 at 22:12
  • @Pestilence one of the things I love about Java. Sadly I don't think PHP was designed to use exceptions in non-exceptional situations, or as normal flow-control. Try debugging long PHP code that uses exceptions as normal flow-control - it's painful. Performance can be affected too. – Bart Platak Aug 18 '12 at 22:23
  • 2
    @norfavrell Are you really citing slower PHP *performance* as a reason to avoid exceptions? :) lol. I agree that debugging is a pain, but I rarely debug as I am a big fan of PHPUnit. I don't think the OP is speaking about normal control flow, just errors. Still, even exceptions get this wrong: JDBC: Ever written the proper triple-nested try-catch-finally construct for a quick read?? – pestilence669 Aug 18 '12 at 22:29
  • I think `if($badges){ echo $badges; } else { echo 'Could not display badges.'; }` would be better (remove the `!`, and swap the messages). – uınbɐɥs Sep 02 '12 at 18:59
  • @ShaquinTrifonoff, In normal situation I would always put 'standard' (i.e. non exceptional) condition first, yes. I have only done this to emphasize the use of return value as an indicator of abnormal (erroneous) situation. But yes from readability point of view I totally agree. – Bart Platak Sep 02 '12 at 19:06
  • Say what? *"For fatal errors I tend to use exceptions (with try-catch blocks)."* Uncaught exceptions become fatal errors. Caught exceptions are not fatal. Anything else that is fatal, you cannot handle with try-catch blocks. – Tiberiu-Ionuț Stan Dec 29 '15 at 11:06
6

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).

Tiberiu-Ionuț Stan
  • 5,033
  • 5
  • 34
  • 61
5

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();
}
Edson Medina
  • 8,352
  • 3
  • 37
  • 48
  • 1
    This is what I was envisioning when I asked my question, but I wasn't sure whether it was a proper use of try/catch or not. – Nate Aug 14 '12 at 13:22
2

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:

uınbɐɥs
  • 6,800
  • 5
  • 24
  • 42
1

PDO error exception handling for queries, and really all code should be run through:

try{

}

catch{


}

finally{

}

The reason for this, is it makes debugging much easier when you can pinpoint roughly where in lengthy scripts an error is occuring

more info here: http://php.net/manual/en/language.exceptions.php

CodeTalk
  • 3,193
  • 12
  • 49
  • 81
0

Create error handler (set_error_handler) and throw exceptions inside it.
It will help for functions, that don't support exceptions.

Dmitry
  • 6,829
  • 12
  • 53
  • 81
0

Handle PHP error and warning in correctly by using error handling functions. ( See example here )

Best way to Error handling in PHP is, You can stop all error reporting by adding this line in top of your php file -

error_reporting(0);

//OR

error_reporting('E_ALL');

// Predefined Constant

Error handling in PHP using functions:

  • debug_backtrace — Generates a backtrace
  • debug_print_backtrace — Prints a backtrace
  • error_clear_last — Clear the most recent error
  • error_get_last — Get the last occurred error
  • error_log — Send an error message to the defined error handling routines
  • error_reporting — Sets which PHP errors are reported
  • restore_error_handler — Restores the previous error handler function
  • restore_exception_handler — Restores the previously defined exception handler function
  • set_error_handler — Sets a user-defined error handler function
  • set_exception_handler — Sets a user-defined exception handler function
  • trigger_error — Generates a user-level error/warning/notice message
  • user_error — Alias of trigger_error

All functions which is listed above are used for Error handling in PHP.

Steve
  • 1
-3

If you're searching for a code structure which will look pretty and will work - you could use the whitelist method I always use. For example - validating a $_GET variable:

$error = false;

if(!isset($_GET['var'])) 
{
    $error = 'Please enter var\'s value';
}
elseif(empty($_GET['var'])) 
{
    $error = 'Var shouldn\'t be empty';
}
elseif(!ctype_alnum($_GET['var'])) 
{
    $error = 'Var should be alphanumeric';
}

//if we have no errors -> proceed to db part
if(!$error) 
{
    //inserting var into database table
}

So, this is it , just 2 if/elseif blocks, without nesting

Noobie
  • 113
  • 10
  • I didn't downvote, but Nate is looking for something without `if` statements, so that the code requires minimal amounts of code to be added if features are to be added. – uınbɐɥs Aug 18 '12 at 22:12
  • This is what _if/else_ blocks were made for - for checking variables and values – Noobie Aug 30 '12 at 11:26
  • True, they are, but it is not an efficient way to handle errors. Also, this is basically what @Nate already has. – uınbɐɥs Aug 30 '12 at 19:58
  • @ShaquinTrifonoff , @Nate uses nested `if/else` blocks, that aren't good for error checking , this is why I've proposed this method ;) Cheers – Noobie Sep 02 '12 at 11:19
  • @Nate doesn't want `if/else` blocks. If you are using `if/else`, as the project gets more complex, nested blocks inevitably will be required. Therefore, a cleaner solution is needed. See the accepted answer and my answer. *Much cleaner*. – uınbɐɥs Sep 02 '12 at 18:54