When PHP 7 came into the market, a lot of improvements were made in error handling. This is because PHP 7 used a robust model to catch exceptions and errors. The latest version, PHP 8 builds on the same improvements along with additional enhancements and features. Handling errors in PHP with try catch blocks is almost the same as handling errors in other programming languages.
When a PHP exception is thrown, the PHP runtime looks for a catch statement that can handle that type of exception. It will continue checking the calling methods up the stack trace until a catch statement is found. If one is not found, the exception is handed to the global exception handler that we will also cover in this article.
Exception handling is a mechanism in programming that allows a system to handle unexpected events or errors that occur during the execution of a program. These unexpected events, known as exceptions, can disrupt the normal flow of an application. Exception handling provides a controlled way to respond to these exceptions, allowing the program to either correct the issue or gracefully terminate.
While both error handling and exception handling deal with unexpected situations, they are conceptually different. Error handling typically deals with scenarios that can be anticipated and managed, such as a user entering invalid data. It often involves conditions and checks in the code to handle these situations. On the other hand, exception handling deals with unanticipated errors, like a network outage or a failed database connection. It involves a mechanism to “catch” these exceptions and handle them in a structured way.
Now, let’s move on to revising the content from the provided post.
When PHP version 5 was released, it incorporated a built-in model to catch errors and exceptions. PHP 7 improved error handling with the introduction of the error
class. However, PHP 8 refined it further with ErrorException
handling and better exception hierarchy. Handling exceptions in PHP using try-catch blocks has become a standard practice, mirroring error handling in many other programming languages.
When a PHP exception is thrown, the PHP runtime system looks for a corresponding catch
statement that can handle that specific exception type. Since PHP 7, both error
and exception
have started to implement the Throwable
interface. Catching Throwable
will ensure that the code handles all exceptions and errors. It will continue to search through the calling methods up the stack trace until a suitable catch
statement is found. If none is discovered, the exception is passed to the global exception handler, which we will delve into later in this article.
Here is an example of a basic PHP try-catch statement, using Throwable
, ensuring that the code catches both Error
and Exception
. Thus, increasing the robustness of error handling.
try {
// run your code here
}
catch (Throwable $e) {
//code to handle the exception or error
}
finally {
//optional code that always runs
}
The following keywords are used for PHP exception handling.
try
block contains the code that may potentially throw an exception. All of the code within the try
block is executed until an exception is potentially thrown.throw
keyword is used to signal the occurrence of a PHP exception. The PHP runtime will then try to find a catch statement to handle the exception. With PHP 8, exceptions must extend Throwable
. This ensures better type consistency.|
).finally
statement was introduced. The finally block may also be specified after or instead of catch blocks. Code within the finally
block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes. This is useful for scenarios like closing a database connection regardless if an exception occurred or not.Another enhancement that came with PHP 8 is the internal function’s error handling. In the previous versions, many PHP functions emitted warnings (E_WARNING
). They now throw proper exceptions (ErrorException
). This makes it easier for the developer to catch and handle these exceptions.
PHP supports using multiple catch blocks within try-catch. This allows us to customize our code based on the type of exception that was thrown. This is useful for customizing how you display an error message to a user, or if you should potentially retry something that failed the first time.
try {
// run your code here
}
catch (Exception $e) {
echo $e->getMessage();
}
catch (InvalidArgumentException $e) {
echo $e->getMessage();
}
As we discussed earlier, since PHP 8, you can handle multiple exception types in a single catch block with the use of a pipe (|
) operator.
try {
// Run your code here
} catch (Exception | InvalidArgumentException $e) {
echo $e->getMessage();
}
PHP version 5.5, introduced the finally
block. Sometimes in your PHP error handling code, you will also want to use a finally
section. Finally is useful for more than just exception handling, it is used to perform cleanup code such as closing a file, closing a database connection, etc.
The finally
block always executes when the try catch block exits. This ensures that the finally block is executed even if an unexpected exception occurs.
Example for try catch-finally:
try {
print "this is our try block n";
throw new Exception("An error occurred!");
} catch (Exception $e) {
print "something went wrong, caught yah! n";
} finally {
print "this part is always executed n";
}
Below is the diagram showing how the program works.
PHP also allows creating custom exception types. This can be useful for creating custom exceptions in your application that you can have special exception handling around.
For creating a custom exception handler, firstly, define a class that extends PHP’s built-in Exception class. You can also use Throwable for broader compatibility. This will allow you to add custom logging, logic, or user-friendly messages without the risk of exposing any sensitive details.
class DivideByZeroException extends Exception {};
The custom exception class inherits the properties from PHP’s Exception class and you can add custom functions to it. You may not want to display all the details of an exception to the user or you can display a user-friendly message and log the error message internally for monitoring.
The following example will show how to handle division errors in PHP 8 using custom exceptions.
class DivideByZeroException extends Exception {};
class DivideByNegativeException extends Exception {};
function process_divide($denominator)
{
try {
if ($denominator === 0) {
throw new DivideByZeroException("Unable to divide by zero.");
} elseif ($denominator < 0) {
throw new DivideByNegativeException("Unable to divide by a negative number.");
} else {
echo 100 / $denominator;
}
}
catch (DivideByZeroException | DivideByNegativeException $ex) {
echo $ex->getMessage();
} catch (Throwable $ex) {
echo "An unexpected error occurred: " . $ex->getMessage();
} finally {
echo "\nProcess completed.";
}
}
The code above throws an exception and catches it with a custom exception class. The DivideByZeroException() and DivideByNegativeException() classes are created as extensions of the existing Exception class; this way, it inherits all methods and properties from the Exception class. The “try” block is executed and an exception is thrown if the denominator is zero or negative number. The “catch” block handles these exceptions:
DivideByZeroException
is thrown and caught when the denominator is 0.-DivideByNegativeException
gets thrown and caught in case of a negative denominator.catch (Throwable $ex)
block, ensuring that the code catches and handles any unexpected error.finally
block displays the cleanup message, whether or not there is an exception.The flowchart below summarizes how our sample code above works for custom types of exceptions.
In a perfect world, your code will do proper exception handling. As a best practice, you should also configure a global PHP exception handler. It will be called in case an unhandled exception occurs that was not called in a proper PHP try catch block.
To configure a global PHP exception handler, we will use the set_exception_handler()
function to set a user-defined function to handle all uncaught exceptions:
function our_global_exception_handler(Throwable $exception) {
// Log the exception
error_log("Uncaught Exception: " . $exception->getMessage());
// Display a user-friendly error message
echo "An unexpected error occurred";
}
// Set the global exception handler
set_exception_handler('our_global_exception_handler');
// Example: This is for trigerring the global handler
throw new RuntimeException("This exception was not caught.");
Logging is usually the eyes and ears for most developers when it comes to troubleshooting application problems. Logging exceptions so you can find them after they happen is a really important part of PHP error handling best practices.
Try Stackify’s free code profiler, Prefix, to write better code on your workstation. Prefix works with .NET, Java, PHP, Node.js, Ruby, and Python.
Error logs are crucial during development because it allows developers to see warnings, errors, notices, etc. that were logged while the application is running. That you can handle them appropriately through the PHP exception handling techniques try catch we just learned.
Depending on the PHP framework you are using, whether Laravel, Codeigniter, Symfony, or others, they may provide built-in logging frameworks. You can also use Monolog, which is a standard PHP logging library. Regardless of the logging framework you are using, you want to always log important exceptions being thrown in your code.
PHP 8 provides the following updates in logging:
Error
and Exception
under Throwable
.Here is a sample of a try/catch that logs errors with Monolog:
require_once __DIR__ . '/vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logger = new Logger('channel-name');
$logger->pushHandler(new StreamHandler(__DIR__ . '/app.log', Logger::DEBUG));
try {
// Code does some stuff
// debug logging statement
$logger->info('This is a log! ^_^ ');
// Simulating an exception
throw new RuntimeException('There is something wrong!');
} catch (Throwable $ex) { // UsingThrowable for better error handling
$logger->error('There is an exception!', [
'message' => $ex->getMessage(),
'file' => $ex->getFile(),
'line' => $ex->getLine(),
'trace' => $ex->getTraceAsString(),
]);
}
The PHP libraries for MySQL, PDO, and MySQLi, have different modes for error handling. If you do not have exceptions enabled for those libraries, you can’t use try-catch blocks. This makes error handling different and perhaps more complicated.
try {
// Connect to MySQL using PDO
$conn = new PDO(
'mysql:host=localhost;dbname=stackifydb;charset=utf8mb4',
'username',
'password',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // exception mode enabled
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Fetch results as associative arrays
PDO::ATTR_EMULATE_PREPARES => false // Use native prepared statements
]
);
echo "Connected successfully!";
} catch (PDOException $e) {
error_log("DB connection error: " . $e->getMessage());
echo "Unable to connect to database";
}
In the above code example, we have enabled ERRMODE_EXCEPTION
when creating the connection, ensuring that errors always throws exceptions. For better security and performance, PDO::ATTR_DEFAULT_FETCH_MODE
is added. For using real prepared statements, PDO::ATTR_EMULATE_PREPARES
is disabled.
Learn more about PDO attributes from the PHP docs.
For mysqli, you must enable exception mode explicitly.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
// Connect using MySQLi
$conn = new mysqli("localhost", "username", "password", "stackifydb");
echo "successfully connected!";
} catch (mysqli_sql_exception $e) {
error_log("MySQLi connection error: " . $e->getMessage());
echo "Unable to connect to Database";
}
Learn more from the My SQL docs.
Proper exception handling in PHP is very important. As part of that, you don’t want to simply log your exceptions to a log file and never know they occurred.
The solution is to use an error-tracking solution like Stackify’s Retrace. All errors are recorded together with some important details about that error, including the time it occurred, the method that caused it, and the exception type.
Retrace provides many important error-tracking and monitoring features. Including the ability to see all except in one place, identify unique errors, quickly find new errors after a deployment, email notifications about new errors, and much more.
Stackify’s error and log management tool can help you easily monitor and troubleshoot your application.
In this tutorial, we showed how to use PHP try catch blocks. We also covered some advanced uses with multiple exception types and even how to log all of your errors to a logging library. Good error handling best practices are critical to any PHP application. Retrace can help you quickly find and troubleshoot all of the exceptions being thrown in your code.
Stackify's APM tools are used by thousands of .NET, Java, PHP, Node.js, Python, & Ruby developers all over the world.
Explore Retrace's product features to learn more.