Error Handling in PHP

Errors are the most common event a developer faces when programming. Errors can be categorized as syntactical, run-time, or logical: missing the semicolon at the end of a statement is an example of a syntax error; trying to connect to a database when the server is down is an example of a run-time error; providing incorrect data to a variable is an example of a logic error. To help reduce the number of errors in your code, and to mitigate their effects, proper error handling is essential in your web application.

This article is a crash course in PHP error handling. You’ll learn about PHP’s built-in error reporting levels, and how to handle errors with custom error handlers and exception handling.

PHP Error Reporting Levels

All errors and warnings should be logged. Based on the severity of an error, notifications should be sent out to other systems/teams. So that you can better gauge its severity, PHP provides several built-in error levels to describe the nature of an error. Each level is represented by an integer value and named constant which can be used by the programmer. The table below is taken from the official PHP documentation and shows some of the different levels.

error constants

The levels can be masked together with bit-operators to include or subtract them from PHP’s configuration. For example, E_ALL|E_STRICT enables all errors and warnings with the addition of E_STRICT (necessary in versions of PHP prior to 5.4).

PHP provides a few configuration directives related to logging and displaying errors. Their values are generally different depending on whether the system is in a development or a production environment. This table shows some of the error-related directives.

error directives

The configuration directives can be set either in php.ini, in a web server configuration file (httpd.conf or .htaccess file), or at run-time in your script using the ini_set() function. Read the documentation for more information on the directives and how/where to set them.

Creating Custom Error Handlers

It’s also good practice not to display raw errors to the end user. Errors that are displayed should be abstracted with friendly, custom error messages. PHP not only provides built-in functions for logging and displaying errors, but also for raising them. You can pragmatically trigger an error of a specific level using trigger_error(). For example, this code triggers an E_USER_NOTICE warning if the value of $test is greater than 1:

<?php
$test = 5;
if ($test > 1) {
    trigger_error('Value of $test must be 1 or less', E_USER_NOTICE);
}

Triggering errors with trigger_error() is useful when you have an error-handling infrastructure in place, allowing you to unify the handling of both custom errors and the errors and warnings raised by PHP.

If you want to implement customized error handling strategies like sending an email or logging errors to a database based on their severity, then you’ll need to define custom error handlers using set_error_handler(). The function accepts two arguments: a callback function or static method that will be invoked when the error is raised, and optionally the error level the function/method handles. The signature of the callback is:

handler(int $errno, string $errstr, string $errfile, int $errline, array $errcontext)

callback arguments

Let’s take a look at a custom error handler function. The example below records errors to a database table database whenever one is encountered:

<?php
function errorHandler($errno, $errstr, $errfile, $errline) {
    static $db;
    if (empty($db)) {
        $db = new PDO(DSN, DBUSER, DBPASS);
    }

    $query = "INSERT INTO errorlog (severity, message, filename, lineno, time) VALUES (?, ?, ?, ?, NOW())";
    $stmt = $db->prepare($query);

    switch ($errno) {
        case E_NOTICE:
        case E_USER_NOTICE:
        case E_DEPRECATED:
        case E_USER_DEPRECATED:
        case E_STRICT:
            $stmt->execute(array("NOTICE", $errstr, $errfile, $errline));
            break;

        case E_WARNING:
        case E_USER_WARNING:
            $stmt->execute(array("WARNING", $errstr, $errfile, $errline));
            break;

        case E_ERROR:
        case E_USER_ERROR:
            $stmt->execute(array("FATAL", $errstr, $errfile, $errline));
            exit("FATAL error $errstr at $errfile:$errline");

        default:
            exit("Unknown error at $errfile:$errline");
    }
}

set_error_handler("errorHandler");

$test = 5;
if ($test > 1) {
   trigger_error("Value of $test must be 1 or less", E_USER_NOTICE);
}

The above snippet registers an error handler that does the following: when non-fatal errors occur, a record will be inserted into database instead of displaying the error and logging it to a file; When a fatal error occurs, it will be logged in database and terminate your script.

There are some limitations to custom error handlers you should be aware of, however. The error handler bypasses PHP’s standard error handling behavior, so it can’t handle errors that may arise within your handler itself. In the event the database server is down, for example, the above function would fail to record the log. Also, the error handler is not able to catch certain internal errors, like E_CORE_ERROR and E_COMPILE_ERROR, or E_STRICT errors in the same file the handler is defined in since those errors occur before the handler has a chance to be registered.

Handling Errors using Exceptions

However good of an error handling framework you have in place, there will always be problems at run-time. Of course you don’t want these errors to show up in the user’s browser. This is where exception handling enters the picture. Exceptions allows you to handle errors and exceptional situations gracefully.

Exceptions are represented in PHP by the class Excpetion (or any of its subclasses). They can be raised using throw and can be caught using a try/catch block. You can extend Exception to create custom exception types for trapping specific errors.

The code that may trigger an exception is placed within the try block, and the code to handle the exception is placed within the catch block. Consider the following snippet:

<?php
try {
   $data = $this->getDataFromService();
}
catch (Exception $e) {
   echo "Caught exception: " . $e->getMessage() . "n";
}

If an exception is thrown by the fictitious getDataFromService() method, it will be caught in the catch block and a message will be displayed. If getDataFromService() executes successfully then the flow will pass over the catch block and continue through the rest of the script. Any exceptions that are thrown and not caught will generate an E_FATAL error with the message “Uncaught Exception.”

The Exception class offers six different methods to access information about what caused the problem, as shown in the table below.

exception methods

Creating a Custom Exception Handler

PHP will let you throw any object as if it were an exception, but as a rule of thumb the exception should extend PHP’s built-in Exception class. Based on the object’s type, you can handle the exceptions differently. Custom exception handling can perform suitable actions like logging error messages in file, providing exact details about the line on which the error occurred by examining the calling stack, etc.  Have a look at this example:

<?php
class NameException extends Exception { }
class EmailException extends Exception { }

$name = "";
$email= "";

try {
   if (empty($name)) {
       throw new NameException();
   }
   elseif (empty($email)) {
       throw new EmailException();
   }
   else {
       echo "Name is " . $name . "<br>";
       echo "Email is " . $email;
   }
}
catch (NameException $n) {
   echo "A name was not provided.";
   error_log($n->getTraceAsString());
}
catch (EmailException $e) {
   echo "An email address was not provided.";
   error_log($e->getTraceAsString());
}

The code above defines two new custom exception types, NameException and EmailException, which can be used to indicate different errors. Then within the try block, the code checks if values have been supplied for the variables $name and $email. If either is empty, then the appropriate exception is thrown using throw. The corresponding catch block is executed which handles the error.

Re-throwing Exceptions

try/catch blocks can be nested. Sometimes you’ll want to catch an exception, look at some of its properties, and then throw it again to let a parent catch block handle it. This can often be useful to check an error condition and decide whether it should be fatal or not. This example code demonstrates re-throwing an exception:

<?php
class FileExistException extends Exception { }
class FileReadException extends Exception { }

$filename = 'D:Exception.txt';
try {
    try {
        $text = file_get_contents($filename);
        if ($text === false) {
            throw new Exception();
        }
    }
    catch (Exception $e) {
        if (!file_exists($filename)) {
            throw new FileExistException($filename . " does not exist.");
        }
        elseif (!is_readable($filename)) {
            throw new FileReadException($filename . " is not readable.");
        }
        else {
            throw new Exception("Unknown error accessing file.");
        }
    }
}
catch (FileExistException $fe) {
    echo $fe->getMessage();
    error_log($fe->getTraceAsString());
}
catch (FileReadException $fr) {
    echo $fr->getMessage();
    error_log($fr->getTraceAsString());
}

Uncaught Exception Handler

Similar to how set_error_handler() allows you specify a function to handle run-time errors, the set_exception_handler() function allows you to handle exceptions that make it all the way up the call stack without being caught by any catch blocks. For example, if an exception makes it all the way up your stack, it would be a good idea to log it in a log file. You can create a callback function and register it  with set_exception_handler() as shown in the example below.

<?php
set_exception_handler(function ($exception) {
   $file = "var/log/exceptionLog.log";
   file_put_contents($file, $exception->__toString(), FILE_APPEND);
});

throw new Exception();

Summary

PHP offers variety of built-in functions for handling error conditions, including logging and displaying them. It also provides you the flexibility to customize your error handling strategies by registering error handlers and using exceptions. Errors are a fact of life, but hopefully the information I presented in this article will help you handle them more gracefully.

Image via Ilya Andriyanov / Shutterstock

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.articleswave.com Manasi

    A very well written article! Good job..

  • Hardy

    Seconded – very good article :)

  • Sergey

    Great article. But there is nothing about catching fatal errors.
    I’m using PHP library Lagger to catch all type of errors/exceptions and log them in file/email/STDOUT: http://code.google.com/p/lagger

    • Sneha Heda

      Thanks for information Sergey. It was really very useful.
      What I can suggest is log all errors in php.ini error_log file and whenever fatal error occurs send mails reporting fatal errors.I hope this answers your question.

  • http://www.miniscript.it oly1982
  • Jozef

    Well written tutorial for PHP beginners. I woulkd like to suggest always implement your own 3 error handlers (see PHP manual for set_error_handler, set_exception_handler, register_shutdown_function). In your shutdown_function you have the last chance to log PHP fatal errors to file.

  • Bigorangemachine

    I like to use ‘debug_backtrace’ with my error handling functions to provide a better idea of whats going on with the error. Especially in production!

    Create a function to handle the backtrace then just ‘pop’ or ‘shift’ a few of the array items off the front of the array and you have a really good idea of process tree that called the function/method.

  • http://stripbandunk.com/ Eko Kurniawan Khannedy

    nice tutorial :D

  • http://fiveholiday55.blogspot.com Helen Neely

    Nice article Sneha. Coming from Java background, the error handlers look very similar to what we’re already used to. That would make learning PHP a lot easier.
    Nice job.

  • guest

    Just to note that errors of type E_ERROR can not be handled with user defined function.
    But, you can use a custom function and register_shutdown_function() to catch error of E_ERROR type.
    http://php.net/manual/en/function.set-error-handler.php

  • Juri

    “Re-throwing Exceptions” – pretty nice thing, simple and useful! Thanks for idea.

  • Sneha Heda

    Thanks Juri. Re-throwing exceptions are mostly used for better management of exceptions and perform different actions based on exceptions.For example, you can first log error in catch block and then throw it up to the UI to generate a friendly error message.There can be other reasons to use it though:)

  • Andrea Parmeggiani

    Nice tutorial and user comments!

  • Sathish

    good article

  • Qtronik Webmaster

    Finally a good exception handler crash course for beginning in this and well explain! The best in all my research ! I’ve get some of It in my project but not all trick! The re-throw it’s perfect! I alway making 5 or 6 try catch for each of my processing… Now I will only put throw and less inside of inside of inside of ins….

  • http://bnk.com Narendra

    Nice your code…it really work best for my site

  • Mark D

    Good article. Thanks it helped me sort out some issues with a school assignment.

  • praveen kollepara

    A very nice article Sneha!!

  • Pedro

    Hi,
    I am entangled in error reporting issues, this by far the best summary I’ve found! A pity, you haven’t done it in procedural, could you(someone) do it? When you do everything from Photoshop to SQL (+ javascript, css, html, apache, php …) you’re going mad :-))) Thanks!

  • Tivie

    Nice tutorial. I would like to point out that it’s not usually a good idea to use functions inside Exceptions or error_handlers that can fail. For instance, file_put_contents($file, $exception->__toString(), FILE_APPEND);
    If the file isn’t writable, this will fail silently. You think you’re logging your exceptions but you’re actually writing to dev_null

  • harish kumar mandiyal

    Hello There!
    Very good article but still I am Unable to understand Exceptions
    I got every bit of ErrorHandling but I am getting to Understand Exceptions.
    Please if anybody can help me in Understanding PHP exception.I searched through google alot for this but none of them are quite able to help me in completely understanding it.
    even if you have a useful resource please provide link plz share it for me.
    Any help in this matter will be apreciated alot.
    Accept my thanks in Advance…..

  • http://www.dusandevic.com Dusan

    Thanks, useful explanation!