Question
I can use set_error_handler() to catch most PHP errors, but it does not work for fatal errors such as E_ERROR, for example when calling a function that does not exist.
Is there another way to handle these errors in PHP?
I want to send an email with mail() whenever an error occurs, and I am using PHP 5.2.3.
Short Answer
By the end of this page, you will understand why set_error_handler() cannot catch true PHP fatal errors, how fatal errors stop normal script execution, and how to detect them using register_shutdown_function() together with error_get_last(). You will also learn practical patterns for logging, alerting, and safely reacting to serious runtime failures in PHP.
Concept
In PHP, not all errors behave the same way.
set_error_handler() can handle many runtime errors, warnings, and notices, but true fatal errors like E_ERROR are different. A fatal error stops the script immediately. Because execution ends at once, PHP does not continue into your normal error handler.
This is why code like this does not catch a fatal error:
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
mail('admin@example.com', 'PHP Error', $errstr);
});
undefined_function(); // Fatal error
The custom error handler is skipped because PHP terminates execution.
The common workaround is to register a shutdown function using register_shutdown_function(). A shutdown function runs when the script is ending, including after many fatal errors. Inside it, you can inspect the last error using error_get_last().
This matters in real programming because production systems need a way to:
- log unexpected crashes
- alert developers or administrators
- show friendly messages to users
- record debugging details without exposing them publicly
In older PHP versions like PHP 5.2.3, this shutdown-based approach was the standard way to react to fatal errors that could not catch.
Mental Model
Think of PHP error handling like building security in a store:
set_error_handler()is like a staff member handling small problems during business hours.- A fatal error is like the power suddenly going out.
- When the power goes out, the staff member cannot continue normal work.
register_shutdown_function()is like the emergency backup system that runs when the building is closing down.
So you do not really catch a fatal error in the usual sense. Instead, you run code at shutdown and inspect whether the script ended because of a fatal error.
Syntax and Examples
The key tools are:
set_error_handler()for non-fatal errorsregister_shutdown_function()for checking fatal errors at the enderror_get_last()for reading the last occurred error
Basic fatal error detection
<?php
register_shutdown_function('handleFatalError');
function handleFatalError()
{
$error = error_get_last();
if ($error !== null && $error['type'] === E_ERROR) {
mail(
'admin@example.com',
'Fatal PHP Error',
"Message: {$error['message']}\nFile: {$error['file']}\nLine: {$error['line']}"
);
}
}
undefined_function();
What this does
register_shutdown_function('handleFatalError')tells PHP to runhandleFatalError()when the script ends.
Step by Step Execution
Consider this example:
<?php
register_shutdown_function('handleShutdown');
echo "Start\n";
undefined_function();
echo "End\n";
function handleShutdown()
{
$error = error_get_last();
if ($error !== null && $error['type'] === E_ERROR) {
echo "Shutdown detected fatal error: {$error['message']}\n";
}
}
Execution trace
- PHP registers the shutdown function
handleShutdown. echo "Start\n";runs successfully.- PHP reaches
undefined_function();. - Because the function does not exist, PHP raises a fatal
E_ERROR. - Normal execution stops immediately.
echo "End\n";never runs.- Before the script fully terminates, PHP runs the shutdown function.
- Inside
handleShutdown(), returns details about the fatal error.
Real World Use Cases
Fatal error detection is useful in many real applications:
- Production monitoring: email or log when a script crashes unexpectedly.
- Background jobs: detect when a scheduled PHP task fails overnight.
- Legacy PHP systems: add minimal crash reporting without rewriting the whole app.
- Form handlers: record unexpected failures before the response ends.
- API endpoints: log crash details for debugging when requests fail.
Example: a cron job imports CSV files every night. If a missing function or unrecoverable error stops the job, a shutdown function can log the exact file and line so the issue is found quickly.
Example: an old PHP 5.2 application cannot use modern exception-based patterns everywhere. A shutdown handler can still provide a safety net for severe crashes.
Real Codebase Usage
In real projects, developers usually do not rely on email for every error because it can create too much noise. Instead, they combine several patterns.
Common patterns
- Central logging: send fatal error details to
error_log()or a log file. - Alerting for severe failures only: email only when the error type is fatal.
- Environment checks: show detailed output in development, but log only in production.
- Guard clauses: validate important values early to reduce fatal conditions.
- Fallback shutdown reporting: use shutdown handlers as a last-resort safety mechanism.
Typical production pattern
<?php
register_shutdown_function('shutdownMonitor');
function shutdownMonitor()
{
$error = error_get_last();
if ($error === null) {
return;
}
$fatalTypes = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR);
if (!in_array($error['type'], $fatalTypes)) {
return;
}
$message = sprintf(
,
[],
[],
[],
[]
);
();
}
Common Mistakes
1. Expecting set_error_handler() to catch E_ERROR
This is the most common misunderstanding.
<?php
set_error_handler('myHandler');
function myHandler($errno, $errstr)
{
echo "Caught: $errstr";
}
undefined_function();
This will not catch the fatal error.
Avoid it by: using register_shutdown_function() for fatal shutdown detection.
2. Thinking shutdown handling can continue execution
A shutdown function does not recover the script.
<?php
register_shutdown_function('handleShutdown');
undefined_function();
echo "Still running";
echo "Still running"; never executes.
Avoid it by: treating shutdown handlers as reporting tools, not recovery tools.
Comparisons
| Concept | What it handles | Can it catch E_ERROR directly? | Typical use |
|---|---|---|---|
set_error_handler() | Warnings, notices, user-triggered errors, many non-fatal runtime issues | No | Custom handling for non-fatal errors |
register_shutdown_function() | Code that runs when the script ends | Indirectly, by inspecting the last error | Detect/report fatal crashes |
error_get_last() | Returns the last error that occurred | Not by itself | Used inside shutdown logic |
| Exceptions | Thrown program errors and catchable exception flow | No, not classic PHP fatal errors in old PHP versions | Structured application-level error handling |
vs shutdown function
Cheat Sheet
// Non-fatal errors
set_error_handler('handleError');
// Fatal shutdown detection
register_shutdown_function('handleShutdown');
function handleError($errno, $errstr, $errfile, $errline)
{
error_log("[$errno] $errstr in $errfile on line $errline");
}
function handleShutdown()
{
$error = error_get_last();
if ($error !== null && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR))) {
error_log("Fatal: {$error['message']} in {$error['file']} on line {$error['line']}");
}
}
Key rules
set_error_handler()does not catch true fatal .
FAQ
Can set_error_handler() catch fatal errors in PHP?
No. It can handle many non-fatal errors, but true fatal errors like E_ERROR terminate execution before the normal error handler can continue.
How do I handle fatal errors in PHP 5.2?
Use register_shutdown_function() and inspect error_get_last() when the script ends.
Can I recover from a fatal error and keep the script running?
No. A fatal error stops the script. A shutdown function only lets you inspect or report the failure before the process fully ends.
Should I use mail() for every PHP error?
Usually no. It is better to log most errors and reserve email alerts for serious or rare failures.
What does error_get_last() return?
It returns the last error as an array containing details such as type, message, file, and line, or null if no error occurred.
Can I detect parse errors this way?
In many cases, shutdown detection can help report parse-related fatal conditions, but the script itself cannot continue normally after them.
What is the safest production approach?
Use a shutdown function to log fatal errors, use set_error_handler() for non-fatal ones, and avoid showing raw error details to users.
Mini Project
Description
Build a small PHP crash reporter for a legacy application. The script should log normal errors separately from fatal shutdown errors, and send an email only when a fatal error occurs. This demonstrates the practical difference between set_error_handler() and register_shutdown_function() in an older PHP-style environment.
Goal
Create a PHP script that logs non-fatal errors, detects fatal errors during shutdown, and emails fatal error details to an administrator.
Requirements
- Register a custom non-fatal error handler using
set_error_handler(). - Register a shutdown function using
register_shutdown_function(). - In the shutdown function, use
error_get_last()to check whether a fatal error occurred. - Log all handled errors with
error_log(). - Send an email only for fatal error types such as
E_ERROR.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.