How To Quickly & Reliably Exterminate Errors In PHP Code

When you’re building something in PHP, whether it’s for a simple website or a much larger web application, you always need to be on the lookout for errors cropping up.

Nasty little bugs can creep in when you least expect them.

An error occurs whenever PHP tries to execute an instruction which either results in an impossible outcome, or otherwise prevents the script from executing in the manner intended by the programmer.

Errors range from simple fatal errors, where program execution is halted at the point of the fault - usually a syntax error or a call to a function that doesn’t exist - to more complex errors where the cause of the problem isn’t immediately clear, and an indepth review of the code must be carried out to pinpoint the flaw.

Undefined Variables & Other Little Glitches

Dealing with errors in a meaningful and graceful manner is a major consideration when developing PHP code.

It’s important to ensure that when something goes wrong (and it likely will, eventually), your users aren’t treated to an unsightly page of technical jargon.

The default behaviour of PHP when encountering an error is to output a very simple line of text, with very basic formatting. It’s really quite ugly.

echo ($hello_world);

Here, we are trying to access a variable $hello_world which has not been defined. The resulting output will be a screen with the following text

Notice: Undefined variable: hello_world in C:\local\myphp.php on line 5

You might not see this appear on screen right away. PHP sometimes is not fully set up to display all errors as they happen.

To change PHP’s default behaviour, add the following lines before the echo statement above

error_reporting(E_ALL|E_STRICT);
ini_set("display_errors", TRUE);

Line 1 tells PHP to report all errors (including “strict” errors, which aren’t included as part of the E_ALL argument).

Line 2 sets a PHP configuration variable which instructs PHP to always display errors. This value is also hidden away in the php.ini configuration file, but that’s not always accessible, so this way of setting it is perfectly valid.

One way to get around this error message cropping up in the first place is to make sure you’ve properly defined the variable before you use it. And if you’re uncertain whether or not the variable is defined, you can always place checks before calling it, like so

//the isset function checks if a given variable is defined or not
if(isset($hello_world){
  echo $hello_world;
} else {
  echo "The variable 'hello world' couldn't be located";
}

But that’s not terribly practical when you have to do the same checks for every variable, and the error message displayed still makes it all too clear to the visiting public that something’s gone wrong.

Ugly Errors? Prettify Them With Custom Error Handling

The following example is a very common instance of an error message when something goes wrong

Fatal error: Call to undefined function hello_world() in C:localmyphp.php on line 1

The error displayed above isn’t terribly pretty to look at. In fact, if a visitor saw that on your site they’d wonder why it was there in the first place, and would probably be discouraged from exploring the site any more, even if the error wasn’t a fatal error, and they could still browse.

A good way to work around this is to use a custom error handler, which allows you to intercept PHP’s error commands, and replace them with your own, nicely formatted ones, even hiding them away completely from visitors, and displaying them only to yourself.

function custom_error_function($level, $message, $file, $line, $context){

Creating a custom error handler is as simple as creating a new function with up to 5 seperate arguments, defined as follows

  • Error level (required) : This specifies the specific level of error reported, useful for determining how serious a fault is
  • Error message (required) : This variable holds the error message generated by PHP
  • Error file (optional) : This tells the function which file the error occured in
  • Error line (optional) : This tells the function which line of the file the error occured on
  • Error context (optional) : This holds an array containing all variables in use at the time the fault occurred

The specific error levels generated by an error are outlined below. These are the values passed into the first variable of the custom error handler, along with their associated numerical value

  • E_WARNING (2) : Non-fatal, the script can continue
  • E_NOTICE (8) : This might be an error, but could also be a normal part of the script, and the code can continue execution
  • E_USER_ERROR (256) : A fatal error, triggered by the user. Later, we will see how errors like this can occur. Wxecution of the script will halt
  • E_USER_WARNING (512) : A non-fatal error (the code will continue to execute) triggered by the user
  • E_USER_NOTICE (1024) : Again, triggered by the user. Non-fatal
  • E_RECOVERABLE_ERROR : A fatal error, but one which can be caught, and recovered from, by use of the try…catch statement, which we will cover later in this tutorial

Lets try creating our own customised error function, to try and make the default error message a little bit friendlier to look at

function customError($level, $message){
  //note that the final three arguments are optional
  echo "An error has occured! The error level is $level and the message was '$message'";
  echo "Please get in touch with the site administrator at admin@email.com to notify them of this fault";
}

The function above is still only just that, a function, but when set to be the default error handler for PHP, as the following code will do, it prints out a much friendlier error message, and lets the user know of a course of action they can take to get the problem remedied.

//this function will set the custom error handler to our function above
set_error_handler("customError");
//now, let's try triggering that error again...
echo ($hello_world); /* our undefined variable */

Which produces

An error has occured! The error level is 8 and the message was 'Undefined variable: hello_world'
Please get in touch with the site administrator at admin@email.com to notify them of this fault

A somewhat friendlier thing to see, but still not the best solution. Ideally, we’d prefer for the visitor never to see these error messages, as they indicate that something has gone wrong with the code, reducing their confidence in the site.

When errors do occur, as they inevitably will, it helps to be able to take as much control of the error as you can.

This normally includes shielding the website visitor from the full effects of the error, while giving yourself as much information as possible about the problem so you can set about fixing it in a timely manner.

Error logging in PHP is a very helpful tool you can use to record messages about errors that have occured on your site.

This way, you don’t have to rely on your site visitors notifying you when things go wrong. Instead, you have a means of automatically noting down anything that goes awry.

error_log()

Let’s modify our custom error handler function from the previous section to use a useful function of PHP, error_log(). This function usually takes three variables, and a fourth when we are sending error logs via email.

  • Error message : The message to send to the log
  • Message type : 0 - Use PHP’s system logger to record the message, 1 - Send the message via email to the address specified in the next argument, 3 -Write the error to the file specified in the next argument
  • Message destination : When message type is set to 1 or 3, this is either the email address, or the local file to be written to, respectively
  • Custom headers : When message type is set to 1 (send via email) this variable holds the custom mail headers used by mail() - see the tutorial on PHP and email

    function customError($level, $message, $file, $line) { //here we’ll append the error to the local file error.log error_log(“Error level $level occured : ‘$message’”,3, “error.log”); //now that we’ve safely recorded the error, we don’t need to rely on the visitor //to inform us of any faults } set_error_handler(“customError”); //let’s trigger the error again… echo($hello_world);

Now, we won’t see anything appear on screen, but the error will be recorded within the error.log file. Here, if we look on the first line of that file, we will see

Error level 8 occured : 'Undefined variable: hello_world'

Good stuff! We now have a handler in place to notify us of any warnings or errors that crop up on the site. You can use a file like this to keep track of any serious errors that occur

Important note: This method of error logging, especially when used to just track undefined variables, can be very system intensive if you have a lot of visitors. Use this method wisely, and only for the more serious forms of error.

Selective error handling

We can modify the behaviour of our custom error handler to deal with the errors more wisely

function customError($level, $message, $file, $line)
{ 
  if($level >= E_USER_ERROR)
    error_log("Error level $level occured : '$message'",3, "error.log");
}

Now, this function will ignore all errors less serious than E_USER_ERROR and log only those errors which pose a risk to the application. We can test this by use of the trigger_error() function, which will generate a user-triggered error

trigger_error("User generated error");

This will cause the following line to appear in error.log

Error level 1024 occured : 'User generated error'

Fatal errors : Unfortunately, PHP doesn’t allow fatal errors to be handled with custom error handlers - the assumption here being that a fatal error will pretty much have killed your script anyway, with no hope of recovery.

Fatal errors can be created by calling functions that don’t exist, for example

hello_world();

Gives, again, the messy error string

Fatal error: Call to undefined function hello_world() in C:localmyphp.php on line 1

The solution to this little scenario : Check that you’ve defined your functions Fatal errors are usually a sign that something’s gone really wrong and you should probably get it sorted quickish!

Introducing: Debug Mode

Sometimes it’s useful to be able to see basic errors, especially while you’re developing your site.

Nothing provides quite as many headaches as a problem that seems to have no obvious cause and having error messages popping up all over the place, while not looking pretty, is a pretty effective way of pinpointing the offending code.

However, at the same time, you certainly don’t want all these error messages cropping up on your site for the average visitor, on the off-chance that something goes wrong.

To prevent this from happening, it’s possible to create a custom error handler which obeys one set of rules for yourself, the developer, and one set of rules for the visiting public.

There’s a couple of simple ways of specifying “who you are” to your website while you’re developing it, so that the site can distinguish between yourself, the developer, and a member of the public.

Debug using your IP address

The first way involves your unique IP address, which is stored in the $_SERVER variable on the webserver, and can be accessed simply inside your script.

First, grab your IP address from a site like What’s My IP. Once you’ve got that you can make some simple adjustments to your error handling function like so

function customError($level, $message, $file, $line)
{ 
  if($_SERVER['REMOTE_ADDR'] == "164.25.66.42"){
    echo "An error occurred ($level) : '$message'";
  } else {
    if($level >= E_USER_ERROR) {
      //write critical errors to the log for normal users
      error_log("nError level $level occured : '$message'",3, "error.log");
    }
  }
}
set_error_handler("customError");
//let's trigger the error again...
echo $hello_world;

Here, the variable $_SERVER['REMOTE_ADDR'] holds the IP address of the visiting computer (yourself, in this case) and if it matches the value you’ve entered, then an error is displayed.

If not, then the script knows you’re just a member of the public browsing the site and won’t display an error, although the error will still be logged if it’s serious enough.

Debug using $_GET variables

A second way, which doesn’t rely on storing your IP address uses the $_GET variable of PHP, which passes data from the URL into the script.

Very handy for passing in variables as required, and this has a couple of benefits over the previous code

You don’t need to keep track of your IP address in the code, which can be troublesome if you develop from multiple locations, or your IP address changes frequently. You can turn debugging on and off easily, which allows you to see the site as a visitor would

Take the following URL, for example

http://www.mysite.com/myphp.php?debug=on

By typing the website URL like so, we have access to a variable $_GET[‘debug’] from within the script. Modifying our custom error handler again

function customError($level, $message, $file, $line)
{ 
  if($_GET['debug'] == "on"){
    echo "An error occurred ($level) : '$message'";
  } else {
    if($level >= E_USER_ERROR) {
      //write critical errors to the log for normal users
      error_log("nError level $level occured : '$message'",3, "error.log");
    }
  }
}
set_error_handler("customError");
//let's trigger the error again...
echo $hello_world;

A small change, but one which allows us to control our debugging status more easily.

There’s a lot that can be done in the realms of error handling and, especially for large scale applications, it’s wise to invest a decent amount of time and effort into ensuring your application functions well even when things come crashing down behind the scenes.

This article only covers the basics of PHP error handling, but hopefully it’ll give you a good idea of what to consider when you’re writing your code, and a few ways you can gracefully alert your users to a fault when the servers burst into flames!

Taking An Exception

A neater way of handling errors in PHP is to use a feature called exceptions.

Exceptions can be used when we wish to impose conditions on the execution of our scripts, or when we want to be able to recover gracefully from conditions where, otherwise, execution of the program would grind to a halt

Exceptions also offer a more Object Oriented way of dealing with errors, and they allow us to build error handling into our class definitions so that any errors that occur will cause the code to degrade gracefully and provide detailed feedback about the exact location of the fault.

Since they are based around a root Exception object we can also extend the Exception class to deal with errors in a more versatile way than the simpler methods outlined on the previous page.

Exceptions to catch user-defined Runtime Errors

Let’s look first at a simple of use the Exception class to prevent a function from completing execution whenever it is passed an argument outwith certain boundaries.

function tryValue($v){
  if($v < 1 || $v > 6){
    throw new Exception("the value is not between 1 and 6 inclusive");
  }
  else return $v*2;
}

try {
  echo tryValue(4);
  echo tryValue(10);
} catch(Exception $e) {
  echo "Exception : ".$e->getMessage();
}
echo "All done";

This code produces the output

8
Exception : the value is not between 1 and 6 inclusive
All done

We need a little explanation of what’s going on here…

  • Line 1-6 : Here we define a function tryValue which simply takes an input $v and doubles it. However, we wish to ensure that the value $v is between 1 and 6 inclusive
  • Line 3 : Here, we enter the if statement whenever $v is outwith our boundaries. We then trigger a new exception with the expression throw new Exception(), which halts execution of the function, and PHP then has to find a way to handle the exception Since the exception has been thrown, we need to have a way to catch it. Think of it as if the function is throwing the execution out of itself if it notices an error. The function call on line 9 hasn’t thrown anything, because the value is within the acceptable range. The function call on line 10 however, throws an exception, and this is caught by the catch(Exception $e) block
  • Line 12 : The code within the catch block is executed whenever an exception is thrown by code within the try block. It’s almost as if you’re saying

    try {this code} and if that throws an (Exception, $e) {do this instead (catch the throw)}

Think of it as if the catch block is a safety net, to catch any failing functions, and prevent the code from hitting the ground hard!

Inside the catch block you also have access to the Exception object $e, which contains some useful information about the particular error that was thrown, including the message defined at the time the exception was thrown.

Using Exceptions to identify Errors in Classes

One useful application of exceptions is to isolate the location of an error within a complex class that depends on many internal functions during run-time.

We can encase the whole function call within a try…catch block to ensure that if an exception is thrown at any point during the operation of the class, then the class will immediately exit and return control to the try…catch block where the error will be reported.

This is in comparison to our earlier error handling functions using set_error_handler(), which would simply output the information there and then, without leaving the scope of the class

class BaseClass{
  function tryValue($v){
    if($v < 1 || $v > 6){
      throw new Exception("the value is not between 1 and 6 inclusive");
    }
    else return $v*2;
  }
}
class MyClass extends BaseClass{
  function doubler($x){
    parent::tryValue($x);
  }
}
function printException(&$e){
    $trace = $e->getTrace();
    echo "Exception on line ".$e->getLine()." : ".$e->getMessage()."<br/>";
    $count = 1;
    foreach($trace as $k => $v){
        echo "Line {$v['line']} : {$v['class']}::{$v['function']}()<br/>";
        $count++;
    }
    echo "-----------------------<br/>";
}
$baseclass = new BaseClass;
$myclass = new MyClass;
try {
  echo $baseclass->tryValue(64);
} catch(Exception $e) {
  printException($e);
}
try {
  echo $myclass->doubler(10);
} catch(Exception $e) {
  printException($e);
}
echo "All done";

This produces useful feedback as shown below

Exception on line 5 : the value is not between 1 and 6 inclusive
Line 27 : BaseClass::tryValue()
-----------------------
Exception on line 5 : the value is not between 1 and 6 inclusive
Line 11 : BaseClass::tryValue()
Line 32 : MyClass::doubler()
-----------------------
All done

Let’s see what this seemingly complicated tangle of code is telling us…

Both function calls have failed - first on line 27 and then the second on line 32. Since the function tryValue throws exceptions whenever the value is out of range, we’ve been provided with some useful debugging information

The first exception tells us that it occurred on line 5, exactly at the point we’ve specified the exception should be thrown, We also have access to a trace, which we can use to step back through the code to see the path of execution. We can see here that the function call came from line 27, just as our code says.

The second exception also tells us that it occurred on line 5. This is correct, since the function call came from elsewhere in the code, but it also came from an extended version of the first class. We can see that the initial call, of MyClass::doubler() came from line 32, but then doubler() calls the function tryValue (belonging to it’s parent BaseClass) from line 11

The Exception instance, $e

When an exception is thrown, PHP is nice enough to give us an instance of the Exception class $e (you can name the variable whatever you like here) complete with a collection of useful methods you can utilise to retrieve more information about the error that just occurred

  • $e->getMessage() : Fetch the custom message that was defined at the point the exception was thrown
  • $e->getLine() : Get the line number where the exception occurred
  • $e->getCode() : Get the code that caused the exception (although in our case we’re manually throwing an exception, so no code is returned)
  • $e->getFile() : Get the name of the PHP file the exception happened in
  • $e->getTrace() : Returns an array containing trace information. This is the array I used to build the detailed debug information in the last example. You can also use getTraceAsString() for a pre-formatted trace string

Hopefully from that example you can see how an Exception based error handler can provide you with ever-useful detail about your code which makes the difference between hours of hair-tearing debugging hell, and ten minutes of peaceful script inspection!

A Summary

These are only a few of the ways in which you can gracefully let your users be aware of faults that occur within your application. Error handling and fault prevention is a detailed subject though, so I’d recommend digging down further into the subject if you’re keen to make sure that your code is solid and reliable.