Please wait

Try Catch Finally Blocks

Catching an exception in PHP means handling unexpected conditions (errors) that may occur during the execution of a program. We can use try catch blocks to help us handle exceptions. Instead of letting your application crash when it encounters an error, you can catch the exception and decide how to respond, which can include gracefully failing, retrying the operation, or providing useful error messages to the user.

Basic Example

Let's look at a simple example of the try/catch syntax :

try {
  $age = -5;
 
  if ($age < 0) {
    throw new Exception("Age cannot be negative");
  }
 
  echo "Your age is: $age";
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();
}

The try block is where you place code that you think might cause an error. If any line inside this block throws an error or exception, the execution of the try block stops immediately, and control jumps to the catch block.

The catch block is designed to "catch" or handle exceptions thrown in the associated try block. Inside the parentheses (Exception $e), Exception is the type of exception you're preparing to handle. In this case, it's the general Exception type, which can catch many types of common errors. $e is a variable that will hold the caught exception, letting you access details about the error.

Inside the block, you can decide what to do with the error. Here, we're just displaying a message using $e->getMessage(), which fetches the error's description.

In simple terms, think of the try block as saying, "Let's try to do this, but something might go wrong." The catch block says, "If something does go wrong in the try block, let's handle it this way."

Using this mechanism, you can prevent unexpected errors from crashing your entire script and instead handle them in a controlled and informative manner.

Exception Class

We learned about the Exception class from the previous lesson. As we know, we can throw an exception with the throw keyword. This keyword is followed by a new instance of an exception. This instance is passed on to our catch blocks.

On this class, we have access to various methods for grabbing information about the exception.

MethodDescription
getMessage()Gets the exception message.
getPrevious()Returns the previous Throwable (exception or error) if one exists or null if not.
getCode()Gets the exception code.
getFile()Gets the file in which the exception occurred.
getLine()Gets the line in the file where the exception occurred.

Multiple Catch Blocks

We're not limited to a single catch block. Multiple catch blocks can be chained to catch various exceptions if you have a block of code that can throw different exceptions.

Let's imagine a scenario where we are attempting to register a user for an online service. There can be various reasons this might fail, such as a duplicate username or an invalid email format. We can use multiple exceptions to differentiate between these error types.

class DuplicateUsernameException extends Exception {}
class InvalidEmailException extends Exception {}
 
try {
  $username = "existingUser123";
  $email = "userexample.com";  // Missing '@'
 
  // Mock logic
  if ($username == "existingUser123") {
    throw new DuplicateUsernameException("Username already exists.");
  }
 
  if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    throw new InvalidEmailException("Email format is invalid.");
  }
 
} catch (DuplicateUsernameException $due) {
  echo $due->getMessage();
} catch (InvalidEmailException $iee) {
  echo $iee->getMessage();
} catch (Exception $e) {
  // Catch any other general exceptions
  echo "An unexpected error occurred: " . $e->getMessage();
}

In the example, we've defined two custom exception classes: DuplicateUsernameException and InvalidEmailException.

We then have a try block where we check the conditions for user registration:

  1. If the username is a duplicate, we throw a DuplicateUsernameException.
  2. If the email is not valid (here, we're using PHP's filter_var() function to check), we throw an InvalidEmailException.

Following the try block, we have multiple catch blocks:

  1. The first catch block handles the case of a duplicate username and outputs an appropriate message.
  2. The second one handles an invalid email format.
  3. The third, more general catch block will handle any other exceptions that might be thrown. It's like a safety net for unexpected errors.

In our mock logic, since the username matches "existingUser123" and the email format is invalid, the output will be: "Username already exists." If you were to correct the username and re-run the script, the output would shift to: "Email format is invalid."

The idea of chaining multiple catch blocks is to handle specific types of exceptions differently. It provides better clarity on what went wrong and allows for more tailored responses based on the error type.

Multiple Exceptions in a Single Catch Block

Starting from version 7.1, you can catch multiple exception types within a single catch block. This is particularly useful when you want to handle different exceptions in the same manner.

class DatabaseException extends Exception {}
class NetworkException extends Exception {}
 
try {
  // Mock logic
  $errorType = "network"; // Change to "database" to trigger DatabaseException
 
  if ($errorType == "database") {
      throw new DatabaseException("Failed to connect to the database.");
  }
 
  if ($errorType == "network") {
      throw new NetworkException("Failed to connect to the network.");
  }
 
} catch (DatabaseException | NetworkException $e) {
  echo "There was a connectivity issue: " . $e->getMessage();
}

In this example, we've defined two custom exceptions: DatabaseException for issues related to database connectivity and NetworkException for network-related problems. Inside the try block, depending on the value of $errorType, we artificially throw one of the two exceptions.

The magic happens in the catch block. By using the | symbol (pipe), we're indicating that the catch block should handle both DatabaseException and NetworkException. If any of these two exceptions are thrown, the code inside the catch block will execute.

So, why might you want to do this? Sometimes, even though exceptions can have different causes, the action you want to take in response is the same. In our mock scenario, whether it's a network issue or a database problem, we might want to output a similar message to the user, indicating a connectivity problem. This approach helps in reducing code duplication and makes the error-handling part more concise.

In the provided example, with $errorType set to "network", the output will be: "There was a connectivity issue: Failed to connect to the network." If you change the $errorType to "database", the output would adapt to the database-related error message.

Finally Block

When working with exceptions using the try-catch structure, there's an optional block you can add called finally. The code inside this finally block will run no matter what - whether an exception was thrown or not.

It's a way to ensure that certain cleanup or finishing tasks get executed, regardless of whether the operations in the try block were successful or if an exception occurred in the catch block.

$ovenOn = true;
 
try {
  // Attempting to bake a cake
  if (!$ovenOn) {
    throw new Exception("The oven is not turned on!");
  }
  echo "Cake is baking...\n";
} catch (Exception $e) {
  echo "Baking failed: " . $e->getMessage() . "\n";
} finally {
  echo "Always make sure the kitchen is clean after baking!";
}

In this scenario, we're trying to bake a cake. We have a condition that checks if the oven is on. If the oven isn't on, we throw an exception, indicating we can't bake the cake.

The catch block will handle the exception if one arises, notifying us that there is a problem.

Regardless of whether we successfully bake the cake or run into a problem (like the oven not being on), the finally block will always execute. In this case, it reminds us to clean the kitchen.

When running this script, since the oven is on ($ovenOn = true), the output will be:

Cake is baking...
Always make sure the kitchen is clean after baking!

But if you set $ovenOn to false, the output will show the error message, followed by the finally block's message.

Key Takeaways

  • The try-catch-finally structure in PHP provides a way to handle and recover from exceptional conditions (errors) in a controlled manner.
  • The code that might cause an exception is placed inside the try block.
  • If an exception is thrown in the try block, the code inside the catch block is executed to handle the exception.
  • Code within the finally block is executed regardless of whether an exception was thrown.
  • You can chain multiple catch blocks to handle different types of exceptions separately.
  • As of PHP 7.1, you can catch multiple exception types in a single catch block using the | (pipe) symbol.
  • While the finally block is useful, it's optional. You can have try-catch without finally.

Comments

Please read this before commenting