When writing your own error handler for PHP, it is absolutely necessary to follow several rules. Otherwise, it can disrupt the behavior of other libraries and applications that do not expect treachery in the error handler.
Parameters
The signature of the handler looks like this:
function errorHandler(
int $severity,
string $message,
string $file,
int $line,
array $context = null // only in PHP < 8
): ?bool {
...
}
The $severity
parameter contains the error level
(E_NOTICE
, E_WARNING
, …). Fatal errors such as
E_ERROR
cannot be caught by the handler, so this parameter will
never have these values. Fortunately, fatal errors have essentially disappeared
from PHP and have been replaced by exceptions.
The $message
parameter is the error message. If the html_errors
directive is enabled, special characters like <
are written as
HTML entities, so you need to decode
them back to plain text. However, beware, some characters are not written
as entities, which is a bug. Displaying errors in pure PHP is thus prone to XSS.
The $file
and $line
parameters represent the name
of the file and the line where the error occurred. If the error occurred inside
eval()
, $file
will be supplemented with this information.
Finally, the $context
parameter contains an array of local
variables, which is useful for debugging, but this has been removed in PHP
8. If the handler is to work in PHP 8, omit this parameter or give it a
default value.
Return Value
The return value of the handler can be null
or
false
. If the handler returns null
, nothing happens.
If it returns false
, the standard PHP handler is also called.
Depending on the PHP configuration, this can print or log the error.
Importantly, it also fills in internal information about the last error, which
is accessible by the error_get_last()
function.
Suppressed Errors
In PHP, error display can be suppressed either using the shut-up operator
@
or by error_reporting()
:
// suppress E_USER_DEPRECATED level errors
error_reporting(~E_USER_DEPRECATED);
// suppress all errors when calling fopen()
$file = @fopen($name, 'r');
Even when errors are suppressed, the handler is still called. Therefore, it is first necessary to verify whether the error is suppressed, and if so, we must end our own handler:
if (!($severity & error_reporting())) {
return false;
}
However, in this case, we must end it with return false
,
so that the standard error handler is still executed. It will not print or log
anything (because the error is suppressed), but ensures that the error can be
detected using error_get_last()
.
Other Errors
If our handler processes the error (for example, displays its own message,
etc.), there is no need to call the standard handler. Although then it will not
be possible to detect the error using error_get_last()
, this does
not matter in practice, as this function is mainly used in combination with the
shut-up operator.
If, on the other hand, the handler does not process the error for any reason,
it should return false
so as not to conceal it.
Example
Here's what the code for a custom error handler that transforms errors into ErrorException exceptions might look like:
set_error_handler(function (int $severity, string $message, string $file, int $line) {
if (!(error_reporting() & $severity)) {
return false;
}
throw new \ErrorException($message, 0, $severity, $file, $line);
});
Leave a comment