Learn PHP in One Day and Learn It Well

PHP for Beginners with Hands-on Project. The only book you need to start coding in PHP immediately. By Jamie Chan

Chapter 12: Managing Errors and Exceptions

We’ve come to the last chapter before our project. In this chapter, we are going to learn to manage errors and exceptions in our PHP scripts.

Frankly speaking, this is one of the messiest areas in PHP as PHP differentiates between an exception and an error. When something goes wrong, we’ll get either an exception or an error.

Internal PHP functions mainly give us errors when something goes wrong. Modern object-oriented extensions (such as the PDO extension we learned in the previous chapter), on the other hand, are more likely to use exceptions.

In this chapter, we’ll first discuss exceptions and errors separately before consolidating everything into a single file in the last section.

Ready? Let’s look at exceptions first.

12.1 Handling Exceptions

12.1.1 What is an exception?

An exception is a predefined object in PHP that allows us to alter the flow of our scripts when something undesirable happens.

For instance, if we try to connect to a database and the connection fails, we can use an exception to help us handle the situation. To see what an exception is, create a file called connect.php in htdocs and add the following code to it:

<?php
$pdo = new PDO("mysql:host=localhost;dbname=pawszone", "wrongadmin", "ABCD");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Here, we try to connect to the “pawszone” database using an invalid username (“wrongadmin”).

Next, create a new file called exceptionHandling.php in htdocs and add the following code to it:

<?php
	include "connect.php";
	echo '<BR>Welcome to Pawszone';

Here, we use an include statement to include the connect.php file (which contains the invalid code) and use an echo statement to echo the text “Welcome to Pawszone” after the include statement.

After creating the two files, try loading exceptionHandling.php in your browser (with Apache and MySQL running), what do you get?

You get a message similar to what is shown below, right? I’ve formatted and underlined parts of the message to make it more readable.

Fatal error: Uncaught PDOException: SQLSTATE[HY000] [1045] Access denied for user 'wrongadmin'@'localhost' (using password: YES) in /opt/lampp/htdocs/connect.php:3

This message informs us that a PDO exception ( PDOException ) has occurred and is not caught (i.e., not handled). This exception occurs because of the invalid username we used when connecting to the “pawszone” database.

When an exception occurs and is not caught, PHP displays a message similar to the one shown above. This output includes a stack trace listing all the files linked to the exception. The trace above tells us that the current exception is linked to line 3 in connect.php and line 2 in exceptionHandling.php. In addition, the database password, “ABCD”, is revealed in the trace.

Notice that the second statement ( echo '<BR>Welcome to Pawszone'; ) in exceptionHandling.php is not executed? This is because any exception that is not caught results in a fatal error; the script is terminated immediately before the echo statement can be executed.

If we do not want script termination to happen (or the message above to be displayed) when an exception occurs, we need to catch the exception.

12.1.2 try-catch-finally

To catch exceptions, we use the trycatch and finally blocks. The syntax is as follows:

try {
	//do something
}
catch (type of exception){
	//do something else when an exception occurs
}
finally {
	//do this regardless of whether the try or catch block is executed
}

The try block first tries to perform a task.

If the task fails, an exception will be thrown. Throwing an exception simply means PHP will create an object using one of the predefined classes for dealing with exceptions.

These classes include the ExceptionPDOExceptionDOMException and OutOfBoundsException class.

When an exception occurs, PHP creates an object of the relevant exception class. This object contains information about the exception and can be used to call various methods defined in the class.

These methods include the getMessage()getFile()getLine() and getTraceAsString() methods, which give us the message, file name, line number and stack trace of the exception respectively.

After an exception is thrown (i.e., after PHP creates the exception object), we need to catch it using a catch block. We can define multiple catch blocks to deal with different types of exceptions.

After the catch block(s), we have the finally block. This block is optional and always executed regardless of whether the try block succeeds or fails.

To illustrate how the trycatch and finally blocks work, replace the original code in exceptionHandling.php with the following code:

<?php
try{
	include "connect.php";
}
catch(PDOException $e){
 	echo '<BR>Unable to connect '.$e->getMessage();
}
catch(Exception $e){
 	echo '<BR>Something else happened'.$e->getMessage();
}
finally{
 	echo '<BR><BR>The finally block is always executed';
}
echo '<BR>After connecting';

Here, we first try to connect to the “pawszone” database inside the try block.

If we are unable to connect, PHP throws an exception and one of the catch blocks will be executed.

In our example, we have two catch blocks. The first catch block catches a PDO exception ( PDOException ) while the second catches a general exception ( Exception ).

 PDOException is a predefined class that deals with exceptions when PDO is unable to perform its task. Exception , on the other hand, is the parent class of all exception classes and deals with general exceptions.

 $e represents an object of the respective exception class. Inside our catch blocks, we use $e to call the built-in getMessage() method and use an echo statement to echo the error message.

If we run the code above, we’ll get the following output:

Unable to connect SQLSTATE[HY000] [1045] Access denied for user 'wrongadmin'@'localhost' (using password: YES)
The finally block is always executed
After connecting

The first line of output is from the catch(PDOException $e) block. As PHP is unable to connect to the database in connect.php, it throws a PDO exception.

The second line is from the finally block. This block is executed regardless of whether the code in the try block succeeds or fails.

Finally, the last line is from the echo statement after the try-catch-finally blocks. This line illustrates that when an exception occurs and is caught successfully, the script continues executing. Straightforward? Good!

12.1.3 Throwing Exceptions

Next, let’s learn to throw our own exceptions.

In the section above, we learn to catch exceptions thrown by PHP. Besides relying on PHP to throw exceptions for us, we can throw our own exceptions. We do that using the throw keyword.

Let’s look at an example. Add the function below to the end of exceptionHandling.php (i.e., after the echo '<BR>After connecting'; statement):

function displayUserInput($userInput){
	if ($userInput > 100){
 	  throw new OutOfRangeException('<BR>User input is too big');
	}
	else{
	  echo '<BR>'.$userInput;
	}
}

Here, we define a function called displayUserInput() that has one parameter – $userInput .

If $userInput is greater than 100 , we throw an OutOfRangeException exception using the throw keyword. OutOfRangeException is a predefined class in PHP extended from the Exception class. Throwing an OutOfRangeException exception involves creating a new object of the class.

When creating a new OutOfRangeException object, we can pass a few optional arguments to the constructor. We commonly pass an error message. In our example, we pass the string '<BR>User input is too big' to the constructor.

If we call the displayUserInput() function using the try-catch blocks below:

try{
	displayUserInput(105);
}
catch (OutOfRangeException $e){
	echo $e->getMessage();
}

an OutOfRangeException exception will be thrown in the try block as $userInput is greater than 100 . This gets caught by the catch block. Inside the catch block, we use the getMessage() method to get the error message.

If we run the code above, we’ll get

User input is too big

as the output. If we change the function call to

displayUserInput(16);

no exception will be thrown and we’ll get 16 as the output. Got it? Great!

12.1.4 Exception Handler

In the previous sections, we learned to throw and catch exceptions. Some exceptions, such as a PDO exception, are easy to pre-empt. Others may be harder. In most cases, despite our best efforts, there are bound to be exceptions we fail to anticipate.

In cases like these, PHP allows us to specify a custom exception handler that gets called whenever there are exceptions we fail to catch. We can use this custom handler to display a custom message to users. However, after the exception handler is called, the PHP script will be terminated.

To see how an exception handler works, add the following code to the end of exceptionHandling.php:

function myExceptionHandler($e){
	echo '<BR>Oppsss... An uncaught exception occurred.<BR>'.$e->getMessage();
}
set_exception_handler('myExceptionHandler');

Here, we define a function called myExceptionHandler() to serve as the exception handler. For a function to qualify as an exception handler, it needs to have one parameter for storing the uncaught exception object. This parameter is $e in our example.

Within the function, we use $e to call the getMessage() method and use an echo statement to echo the message.

After coding the function, we need to use a built-in function called set_exception_handler() to set myExceptionHandler() as the exception handler. To do that, we write

set_exception_handler('myExceptionHandler');

To test our exception handler, add the following lines to the end of exceptionHandling.php:

$pdo = new PDO("some invalid database");
echo 'This will not be executed';

Here, we try to create a PDO object using an invalid argument. This results in a PDO exception, which is not caught by any catch block.

If you run the code above, you’ll get

Oppsss... An uncaught exception occurred.invalid data source name

added to the output. The exception handler ( myExceptionHandler ) has successfully handled the exception. However, notice that the echo statement after the PDO instantiation statement is not executed? This is because the PHP script got terminated after the exception handler is called. Got it?

12.2 Handling Errors

12.2.1 What are errors?

Great! Now that you are familiar with exceptions, let’s move on to discuss errors. As mentioned previously, when something goes wrong in PHP, you will get either an exception or an error.

One major difference between errors and exceptions is that exceptions can be caught while errors, by default, cannot. This is especially true prior to PHP 7. From PHP 7 onwards, PHP throws an Error exception when some errors occur; this exception can then be caught.

There are four main types of errors defined in PHP – notices, warnings, syntax errors and fatal errors. Each error type is further divided into subtypes and each subtype is represented by a constant and an error code.

Warnings and notices are minor errors that do not lead to script termination. Common causes include trying to divide by zero (which generates a warning) and trying to access an undefined variable (which generates a notice).

Syntax errors, on the other hand, lead to script termination. They are caused by programmers making a mistake in the syntax of their code. Examples include forgetting to close a parenthesis or missing a semicolon.

Last but not least, we have fatal errors, which also lead to script termination. Common causes include trying to call a non-existent function.

Do not worry about the distinction between the different types of errors. When an error occurs, PHP has an excellent reporting system. For instance, if you try to access a non-existent variable using the statement

echo $some_undefined_variable;

you’ll get an output similar to what is shown below:

Notice: Undefined variable: some_undefined_variable in ...mycode.php on line 6

This output informs us that the error is a notice and gives us the reason for the error. It also gives us the file name of the error and the line number where it occurred.

As you can see, an error message is very informative. Such messages make it very easy for us to debug our code. However, they can reveal too much information to end users.

Therefore, it is a common practice for us to display these messages only during the development stage of our site. When our site goes live, it is strongly recommended that we do not display such messages to end users.

To adjust whether error messages are displayed, we need to update the error settings in our PHP server.

12.2.2 Error Reporting Settings in PHP

There are two relevant settings here – error_reporting and display_errors .

The error_reporting setting affects the type of errors that PHP will report. There are several predefined constants that we can assign to this setting.

If we want PHP to only report fatal errors (and not report warnings and notices etc.), we assign the E_ERROR constant to this setting. On the other hand, if we want PHP to report all errors, we assign E_ALL to it.

Besides E_ERROR and E_ALL , we can assign other constants to the error_reporting setting. You can find the list of all error constants and codes athttps://www.php.net/manual/en/errorfunc.constants.php.

Next, we have the display_errors setting. This affects whether errors reported by PHP will be displayed on the browser. During the development stage, we want errors to be displayed. To do that, we set display_errors to On .

On a live site, we do not want any errors to be displayed. To do that, we set display_errors to Off .

When display_errors is off, we typically log the errors reported by PHP to a log file. We’ll learn to do that later.

To configure the error settings mentioned above, the most direct way is to modify the php.ini file. We’ve already done that in Chapter 2, where we set error_reporting to E_ALL and display_errors to On .

If you do not have permission to edit php.ini, you can use two built-in functions – error_reporting() and ini_set() – to modify the settings. To do that, add the following statements to all your PHP scripts.

error_reporting(E_ALL); 
ini_set('display_errors', '1');

In the first statement above, we use the error_reporting() function to set error_reporting to E_ALL . Next, we use the ini_set() function to set display_errors to '1' when the site is in the development stage.

After the site goes live, you should change the display_errors setting to '0' using the following statement:

ini_set('display_errors', '0');

Changing the display_errors setting using ini_set() has its limitations. If there is any syntax error in your script, the ini_set() function will not work.

For instance, if you have the following line in your script

echo 'Hello;

The display_errors setting will not be set as the statement above has a syntax error (the closing quotation mark is missing). The page will simply fail to load without any indication of what’s causing it. Hence, whenever possible, it is strongly recommended that you update the php.ini file instead.

If you do not have access to php.ini, you can try modifying the error settings using httpd.conf or .htaccess. These files allow you to modify the Apache web server settings, which in turn can be used to modify the PHP settings. Depending on your hosting company, you may also be allowed to add a custom php.ini file to your directory and use it to configure the PHP settings.

Check with your hosting company if you have permission to do any of the above and if yes, how to go about doing it. Instruction on using these techniques is beyond the scope of this book.

12.2.3 Error Handler and Shutdown Function

In the previous section, we learned to adjust the display_errors setting. On a live site, you are encouraged to turn display_errors off.

However, sometimes, not displaying any error message may not be the best approach. For instance, if a fatal error occurs on our site, the site will just fail to load without any information. This can be frustrating for the user.

Instead of leaving users entirely in the dark when something goes wrong, we can provide them with a “user-friendly” error message.

To do that, we need to create a custom error handler. A custom error handler allows us to handle the PHP errors ourselves, instead of relying on the default error handler in PHP. We can use this custom handler to display a custom message to users when something goes wrong.

To see how this works, create a new file in Brackets and save it as errorHandling.php to the htdocs folder. Add the following code to it:

<?php
function myErrorHandler($errno, $errstr, $errfile, $errline){
	echo '<BR>Oppsss... An error occurred.<BR>'.$errstr;
}
set_error_handler('myErrorHandler');

In the code above, we first create a function called myErrorHandler() that has four parameters – $errno$errstr$errfile and $errline . The first two parameters are mandatory for a function to “qualify” as a custom error handler, the last two are optional.

These four parameters are used to store the error code, error message, file name in which the error occurred and the line number respectively. All four pieces of information are provided by PHP when an error occurs.

Within the function, we use an echo statement to echo a custom error message to users.

After coding the function, we use a built-in function called set_error_handler() to set myErrorHandler() as the custom error handler. Got it?

At this point, you’ll probably notice that this custom error handler is very similar to the custom exception handler we coded in Chapter 12.1.4. Indeed, the two are similar.

However, while a custom exception handler handles uncaught exceptions, a custom error handler handles errors. In addition, a custom error handler does not result in script termination.

To test the error handler above, add the following code to the end of errorHandling.php:

echo $a;
echo '<BR>Script is not terminated';

If you run errorHandling.php now, you’ll get the following output:

Oppsss... An error occurred.
Undefined variable: a
Script is not terminated

The custom error handler is first executed to give us a custom error message when we try to access an undefined variable. After that, the script continues and the second echo statement is executed.

The custom error handler replaces the default PHP error handler and gives us a convenient way to modify error messages displayed to users.

However, it is unable to handle all errors. For instance, it is unable to handle fatal errors. These errors lead to script termination.

To handle these errors, we need another built-in function called register_shutdown_function() . This function tells PHP which function to run when a script is about to be terminated.

To see how this works, add the following code to the end of errorHandling.php:

function myShutDownHandler(){
	$lastError = error_get_last();
	if (isset($lastError)) {
 		echo '<BR>Oppsss... Script terminated.<BR>';
 	}
}
register_shutdown_function('myShutDownHandler');

Here, we declare a function called myShutDownHandler() . Within the function, we need to determine whether the script termination is due to an error. To do that, we use the error_get_last() function.

This function returns NULL if no error has occurred (for instance, if script termination is due to the user navigating to another page).

On the other hand, if an error has occurred, it returns the last error as an associative array. The array contains four keys: type (which gives the error type), message (which gives the error message), file (which gives the file where the error occurred) and line (which gives the line where the error occurred).

After calling the error_get_last() function, we use an if statement to check if an error has occurred. If it has, we echo a custom error message to users.

After coding the myShutDownHandler() function, we use another built-in function called register_shutdown_function() to register it as the shutdown function.

To test our shutdown function, add the following code to the end of errorHandling.php:

hello();

Here, we try to call a non-existent function. If you load errorHandling.php now, you’ll get the error message below:

Fatal error: Uncaught Error: Call to undefined function hello() in ...errorHandling.php:27 Stack trace: #0 {main} thrown in ...errorHandling.php on line 27Oppsss... Script terminated.

The “Fatal error” message is displayed only when display_errors is set to On (or '1' ). If you turn display_errors to Off (or '0' ), only the last line is displayed. This line is generated by our shutdown function. Got it?

12.3 Putting it All Together

In the previous sections, we talked about exceptions and errors and how to handle them separately. In this section, let’s consolidate everything into a single file.

To do that, create a file called debugging.php in your htdocs folder.

Inside this file, we need to first create a function called myDebugger() ; this function serves as the main function for handling any uncaught exception or error later.

To create the function, add the following code to debugging.php:

function myDebugger($msg, $file, $line, $trace = ''){
 	$message = $trace.'<BR><BR><strong>'.$msg.'</strong> found on <u>line '.$line.'</u> in file <u>'.$file.'</u>';
 	if (ini_get('display_errors')) {
 	  echo $message;
 	}
 	else {
 	  error_log($message);
      header('Location: error.html');
 	}
 }

Here, we define a function called myDebugger() with four parameters – $msg$file$line and $trace . The first three parameters are for storing the message, file name and line number of an error or exception respectively. The last parameter is for storing the stack trace of an exception. All four pieces of information will be provided by PHP when an error or exception occurs.

Within the function, we concatenate the four parameters and format the resulting string using HTML, before assigning it to a variable called $message .

Next, we use an if statement to determine what gets displayed on our browser. To do that, we use a built-in function called ini_get() to get the setting for 'display_errors' .

When 'display_errors' is set to On (or '1' ), the if condition evaluates to true and we use an echo statement to display the error message on our browser. Else, we use another built-in function called error_log() to log $message to our error log file.

The error log file is a text file that contains all the messages we write to it using the error_log() function. You can find where the error log file is located on your system by searching for “error_log” (without quotes) in the PHP information page, which can be found athttp://localhost/dashboard/phpinfo.php.

If you are using Windows, the log file should be stored in the C:\xampp\php\logs folder. However, weirdly, this folder may not exist and you may have to create it yourself. To do that, simply navigate to C:\xampp\php and create a logs folder. When an error occurs, PHP will automatically create the log file when the error_log() function is called.

After logging our error message, we use the header() function to redirect users to another page called error.html (which we need to create separately). With that, the myDebugger() function is complete.

Next, we need to create an exception handler, an error handler and a shutdown function. Inside all three functions, we need to call the myDebugger() function.

To do that, add the following code to debugging.php:

function myExceptionHandler ($e){
  	myDebugger($e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString());
}
function myErrorHandler($errno, $errstr, $errfile, $errline){
 	myDebugger($errstr, $errfile, $errline);
}
function myShutDownHandler(){
 	$lastError = error_get_last();

 	if (isset($lastError)) {
  		myDebugger($lastError['message'], $lastError['file'], $lastError['line']);
	}
}

The first function – myExceptionHandler() – has a parameter called $e that is used to store an uncaught exception object. Inside the function, we use $e to call the built-in getMessage()getFile()getLine() and getTraceAsString() methods and pass the results as arguments to the myDebugger() function.

Next, we have the myErrorHandler() function. This function has four parameters – $errno$errstr$errfile and $errline . Inside the function, we pass $errstr$errfile and $errline as arguments to the myDebugger() function.

Last but not least, we have the myShutDownHandler() function. Inside this function, we check if the shutdown is due to an error. If it is, we pass the elements in the associative array returned by error_get_last() as arguments to the myDebugger() function.

After coding these three functions, we need to set them as our custom handlers and shutdown function. To do that, add the following lines to debugging.php:

set_exception_handler('myExceptionHandler');
set_error_handler('myErrorHandler');
register_shutdown_function('myShutDownHandler');

With that, the debugging.php file is complete; we’ll be using it in our project later.

In PHP, there are many different approaches to dealing with exceptions and errors, debugging.php demonstrates one of the many techniques available.

Regardless of whether an error or an uncaught exception occurs on our site, this file uses one of the custom handlers or the shutdown function to call the myDebugger() function. This function displays an error message on the browser when display_errors is set to On or '1' . Else, it logs the error message and redirects users to a custom error page called error.html. Got it?