Chris@0: ['Error', RfcLogLevel::ERROR], Chris@0: E_WARNING => ['Warning', RfcLogLevel::WARNING], Chris@0: E_PARSE => ['Parse error', RfcLogLevel::ERROR], Chris@0: E_NOTICE => ['Notice', RfcLogLevel::NOTICE], Chris@0: E_CORE_ERROR => ['Core error', RfcLogLevel::ERROR], Chris@0: E_CORE_WARNING => ['Core warning', RfcLogLevel::WARNING], Chris@0: E_COMPILE_ERROR => ['Compile error', RfcLogLevel::ERROR], Chris@0: E_COMPILE_WARNING => ['Compile warning', RfcLogLevel::WARNING], Chris@0: E_USER_ERROR => ['User error', RfcLogLevel::ERROR], Chris@0: E_USER_WARNING => ['User warning', RfcLogLevel::WARNING], Chris@0: E_USER_NOTICE => ['User notice', RfcLogLevel::NOTICE], Chris@0: E_STRICT => ['Strict warning', RfcLogLevel::DEBUG], Chris@0: E_RECOVERABLE_ERROR => ['Recoverable fatal error', RfcLogLevel::ERROR], Chris@0: E_DEPRECATED => ['Deprecated function', RfcLogLevel::DEBUG], Chris@0: E_USER_DEPRECATED => ['User deprecated function', RfcLogLevel::DEBUG], Chris@0: ]; Chris@0: Chris@0: return $types; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Provides custom PHP error handling. Chris@0: * Chris@0: * @param $error_level Chris@0: * The level of the error raised. Chris@0: * @param $message Chris@0: * The error message. Chris@0: * @param $filename Chris@0: * The filename that the error was raised in. Chris@0: * @param $line Chris@0: * The line number the error was raised at. Chris@0: * @param $context Chris@0: * An array that points to the active symbol table at the point the error Chris@0: * occurred. Chris@0: */ Chris@0: function _drupal_error_handler_real($error_level, $message, $filename, $line, $context) { Chris@0: if ($error_level & error_reporting()) { Chris@0: $types = drupal_error_levels(); Chris@0: list($severity_msg, $severity_level) = $types[$error_level]; Chris@0: $backtrace = debug_backtrace(); Chris@0: $caller = Error::getLastCaller($backtrace); Chris@0: Chris@0: // We treat recoverable errors as fatal. Chris@0: $recoverable = $error_level == E_RECOVERABLE_ERROR; Chris@0: // As __toString() methods must not throw exceptions (recoverable errors) Chris@0: // in PHP, we allow them to trigger a fatal error by emitting a user error Chris@0: // using trigger_error(). Chris@0: $to_string = $error_level == E_USER_ERROR && substr($caller['function'], -strlen('__toString()')) == '__toString()'; Chris@0: _drupal_log_error([ Chris@0: '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error', Chris@0: // The standard PHP error handler considers that the error messages Chris@0: // are HTML. We mimick this behavior here. Chris@0: '@message' => Markup::create(Xss::filterAdmin($message)), Chris@0: '%function' => $caller['function'], Chris@0: '%file' => $caller['file'], Chris@0: '%line' => $caller['line'], Chris@0: 'severity_level' => $severity_level, Chris@0: 'backtrace' => $backtrace, Chris@0: '@backtrace_string' => (new \Exception())->getTraceAsString(), Chris@0: ], $recoverable || $to_string); Chris@0: } Chris@14: // If the site is a test site then fail for user deprecations so they can be Chris@14: // caught by the deprecation error handler. Chris@14: elseif (DRUPAL_TEST_IN_CHILD_SITE && $error_level === E_USER_DEPRECATED) { Chris@14: $backtrace = debug_backtrace(); Chris@14: $caller = Error::getLastCaller($backtrace); Chris@14: _drupal_error_header( Chris@14: Markup::create(Xss::filterAdmin($message)), Chris@14: 'User deprecated function', Chris@14: $caller['function'], Chris@14: $caller['file'], Chris@14: $caller['line'] Chris@14: ); Chris@14: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determines whether an error should be displayed. Chris@0: * Chris@0: * When in maintenance mode or when error_level is ERROR_REPORTING_DISPLAY_ALL, Chris@0: * all errors should be displayed. For ERROR_REPORTING_DISPLAY_SOME, $error Chris@0: * will be examined to determine if it should be displayed. Chris@0: * Chris@0: * @param $error Chris@0: * Optional error to examine for ERROR_REPORTING_DISPLAY_SOME. Chris@0: * Chris@0: * @return Chris@0: * TRUE if an error should be displayed. Chris@0: */ Chris@0: function error_displayable($error = NULL) { Chris@0: if (defined('MAINTENANCE_MODE')) { Chris@0: return TRUE; Chris@0: } Chris@0: $error_level = _drupal_get_error_level(); Chris@0: if ($error_level == ERROR_REPORTING_DISPLAY_ALL || $error_level == ERROR_REPORTING_DISPLAY_VERBOSE) { Chris@0: return TRUE; Chris@0: } Chris@0: if ($error_level == ERROR_REPORTING_DISPLAY_SOME && isset($error)) { Chris@0: return $error['%type'] != 'Notice' && $error['%type'] != 'Strict warning'; Chris@0: } Chris@0: return FALSE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Logs a PHP error or exception and displays an error page in fatal cases. Chris@0: * Chris@0: * @param $error Chris@0: * An array with the following keys: %type, @message, %function, %file, Chris@0: * %line, @backtrace_string, severity_level, and backtrace. All the parameters Chris@0: * are plain-text, with the exception of @message, which needs to be an HTML Chris@0: * string, and backtrace, which is a standard PHP backtrace. Chris@0: * @param bool $fatal Chris@0: * TRUE for: Chris@0: * - An exception is thrown and not caught by something else. Chris@0: * - A recoverable fatal error, which is a fatal error. Chris@0: * Non-recoverable fatal errors cannot be logged by Drupal. Chris@0: */ Chris@0: function _drupal_log_error($error, $fatal = FALSE) { Chris@0: $is_installer = drupal_installation_attempted(); Chris@0: Chris@0: // Backtrace array is not a valid replacement value for t(). Chris@0: $backtrace = $error['backtrace']; Chris@0: unset($error['backtrace']); Chris@0: Chris@0: // When running inside the testing framework, we relay the errors Chris@0: // to the tested site by the way of HTTP headers. Chris@0: if (DRUPAL_TEST_IN_CHILD_SITE && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) { Chris@14: _drupal_error_header($error['@message'], $error['%type'], $error['%function'], $error['%file'], $error['%line']); Chris@0: } Chris@0: Chris@0: $response = new Response(); Chris@0: Chris@0: // Only call the logger if there is a logger factory available. This can occur Chris@0: // if there is an error while rebuilding the container or during the Chris@0: // installer. Chris@0: if (\Drupal::hasService('logger.factory')) { Chris@0: try { Chris@14: // Provide the PHP backtrace to logger implementations. Chris@14: \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error + ['backtrace' => $backtrace]); Chris@0: } Chris@0: catch (\Exception $e) { Chris@0: // We can't log, for example because the database connection is not Chris@0: // available. At least try to log to PHP error log. Chris@0: error_log(strtr('Failed to log error: %type: @message in %function (line %line of %file). @backtrace_string', $error)); Chris@0: } Chris@0: } Chris@0: Chris@0: // Log fatal errors, so developers can find and debug them. Chris@0: if ($fatal) { Chris@0: error_log(sprintf('%s: %s in %s on line %d %s', $error['%type'], $error['@message'], $error['%file'], $error['%line'], $error['@backtrace_string'])); Chris@0: } Chris@0: Chris@0: if (PHP_SAPI === 'cli') { Chris@0: if ($fatal) { Chris@0: // When called from CLI, simply output a plain text message. Chris@0: // Should not translate the string to avoid errors producing more errors. Chris@17: $response->setContent(html_entity_decode(strip_tags(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error))) . "\n"); Chris@0: $response->send(); Chris@0: exit; Chris@0: } Chris@0: } Chris@0: Chris@0: if (\Drupal::hasRequest() && \Drupal::request()->isXmlHttpRequest()) { Chris@0: if ($fatal) { Chris@0: if (error_displayable($error)) { Chris@0: // When called from JavaScript, simply output the error message. Chris@0: // Should not translate the string to avoid errors producing more errors. Chris@17: $response->setContent(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error)); Chris@0: $response->send(); Chris@0: } Chris@0: exit; Chris@0: } Chris@0: } Chris@0: else { Chris@0: // Display the message if the current error reporting level allows this type Chris@0: // of message to be displayed, and unconditionally in update.php. Chris@0: $message = ''; Chris@0: $class = NULL; Chris@0: if (error_displayable($error)) { Chris@0: $class = 'error'; Chris@0: Chris@0: // If error type is 'User notice' then treat it as debug information Chris@0: // instead of an error message. Chris@0: // @see debug() Chris@0: if ($error['%type'] == 'User notice') { Chris@0: $error['%type'] = 'Debug'; Chris@0: $class = 'status'; Chris@0: } Chris@0: Chris@0: // Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path Chris@0: // in the message. This does not happen for (false) security. Chris@0: if (\Drupal::hasService('app.root')) { Chris@0: $root_length = strlen(\Drupal::root()); Chris@0: if (substr($error['%file'], 0, $root_length) == \Drupal::root()) { Chris@0: $error['%file'] = substr($error['%file'], $root_length + 1); Chris@0: } Chris@0: } Chris@0: Chris@0: // Check if verbose error reporting is on. Chris@0: $error_level = _drupal_get_error_level(); Chris@0: Chris@0: if ($error_level != ERROR_REPORTING_DISPLAY_VERBOSE) { Chris@0: // Without verbose logging, use a simple message. Chris@0: Chris@17: // We use \Drupal\Component\Render\FormattableMarkup directly here, Chris@17: // rather than use t() since we are in the middle of error handling, and Chris@17: // we don't want t() to cause further errors. Chris@17: $message = new FormattableMarkup('%type: @message in %function (line %line of %file).', $error); Chris@0: } Chris@0: else { Chris@0: // With verbose logging, we will also include a backtrace. Chris@0: Chris@0: // First trace is the error itself, already contained in the message. Chris@0: // While the second trace is the error source and also contained in the Chris@0: // message, the message doesn't contain argument values, so we output it Chris@0: // once more in the backtrace. Chris@0: array_shift($backtrace); Chris@0: // Generate a backtrace containing only scalar argument values. Chris@0: $error['@backtrace'] = Error::formatBacktrace($backtrace); Chris@17: $message = new FormattableMarkup('%type: @message in %function (line %line of %file).
@backtrace', $error); Chris@0: } Chris@0: } Chris@0: Chris@0: if ($fatal) { Chris@0: // We fallback to a maintenance page at this point, because the page generation Chris@0: // itself can generate errors. Chris@0: // Should not translate the string to avoid errors producing more errors. Chris@0: $message = 'The website encountered an unexpected error. Please try again later.' . '