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.
Method | Description |
---|---|
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:
- If the username is a duplicate, we throw a
DuplicateUsernameException
. - If the email is not valid (here, we're using PHP's
filter_var()
function to check), we throw anInvalidEmailException
.
Following the try
block, we have multiple catch
blocks:
- The first
catch
block handles the case of a duplicate username and outputs an appropriate message. - The second one handles an invalid email format.
- 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 thecatch
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 havetry-catch
withoutfinally
.