Logs a PHP error or exception and displays an error page in fatal cases.
$error: An array with the following keys: %type, !message, %function, %file, %line, severity_level, and backtrace. All the parameters are plain-text, with the exception of !message, which needs to be a safe HTML string, and backtrace, which is a standard PHP backtrace.
$fatal: TRUE if the error is fatal.
function _drupal_log_error($error, $fatal = FALSE) {
// Initialize a maintenance theme if the boostrap was not complete.
// Do it early because drupal_set_message() triggers a drupal_theme_initialize().
if ($fatal && drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) {
unset($GLOBALS['theme']);
if (!defined('MAINTENANCE_MODE')) {
define('MAINTENANCE_MODE', 'error');
}
drupal_maintenance_theme();
}
// Backtrace array is not a valid replacement value for t().
$backtrace = $error['backtrace'];
unset($error['backtrace']);
// When running inside the testing framework, we relay the errors
// to the tested site by the way of HTTP headers.
$test_info =& $GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
// $number does not use drupal_static as it should not be reset
// as it uniquely identifies each PHP error.
static $number = 0;
$assertion = array(
$error['!message'],
$error['%type'],
array(
'function' => $error['%function'],
'file' => $error['%file'],
'line' => $error['%line'],
),
);
header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
$number++;
}
watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
if (drupal_is_cli()) {
if ($fatal) {
// When called from CLI, simply output a plain text message.
// Should not translate the string to avoid errors producing more errors.
print html_entity_decode(strip_tags(format_string('%type: !message in %function (line %line of %file).', $error))) . "\n";
exit;
}
}
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
if ($fatal) {
if (error_displayable($error)) {
// When called from JavaScript, simply output the error message.
// Should not translate the string to avoid errors producing more errors.
print format_string('%type: !message in %function (line %line of %file).', $error);
}
exit;
}
}
else {
// Display the message if the current error reporting level allows this type
// of message to be displayed, and unconditionnaly in update.php.
if (error_displayable($error)) {
$class = 'error';
// If error type is 'User notice' then treat it as debug information
// instead of an error message.
// @see debug()
if ($error['%type'] == 'User notice') {
$error['%type'] = 'Debug';
$class = 'status';
}
// Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path
// in the message. This does not happen for (false) security.
$root_length = strlen(DRUPAL_ROOT);
if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) {
$error['%file'] = substr($error['%file'], $root_length + 1);
}
// Should not translate the string to avoid errors producing more errors.
$message = format_string('%type: !message in %function (line %line of %file).', $error);
// Check if verbose error reporting is on.
$error_level = config('system.logging')
->get('error_level');
if ($error_level == ERROR_REPORTING_DISPLAY_VERBOSE) {
// First trace is the error itself, already contained in the message.
// While the second trace is the error source and also contained in the
// message, the message doesn't contain argument values, so we output it
// once more in the backtrace.
array_shift($backtrace);
// Generate a backtrace containing only scalar argument values.
$message .= '<pre class="backtrace">' . format_backtrace($backtrace) . '</pre>';
}
drupal_set_message($message, $class, TRUE);
}
if ($fatal) {
// Should not translate the string to avoid errors producing more errors.
drupal_set_title('Error');
// We fallback to a maintenance page at this point, because the page generation
// itself can generate errors.
// Should not translate the string to avoid errors producing more errors.
$output = theme('maintenance_page', array(
'content' => 'The website has encountered an error. Please try again later.',
));
$response = new Response($output, 500);
if ($fatal) {
$response
->setStatusCode(500, '500 Service unavailable (with message)');
}
return $response;
}
}
}