From 3ae8eb2416b3306346e2efc876a5cc124955ff54 Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Thu, 4 Dec 2014 18:23:03 -0700 Subject: [PATCH] Register a shutdown function to log fatal errors MWExceptionHandler::handleFatalError() is installed as a shutdown function to inspect `error_get_last()` output and log the error via MWExceptionHandler::logError(). The error is examined to determine if it is a missing class error so that a descriptive log message can be provided instructing the server administrator on 3rd party library installation methods. Bug: T74777 Change-Id: I04748626487022c51050ffa7846236947a082d68 --- includes/exception/MWExceptionHandler.php | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/includes/exception/MWExceptionHandler.php b/includes/exception/MWExceptionHandler.php index 9db04cbf6d..83801b6edb 100644 --- a/includes/exception/MWExceptionHandler.php +++ b/includes/exception/MWExceptionHandler.php @@ -24,12 +24,24 @@ */ class MWExceptionHandler { + protected static $reservedMemory; + protected static $fatalErrorTypes = array( + E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, + /* HHVM's FATAL_ERROR level */ 16777217, + ); + /** * Install handlers with PHP. */ public static function installHandler() { set_exception_handler( array( 'MWExceptionHandler', 'handleException' ) ); set_error_handler( array( 'MWExceptionHandler', 'handleError' ) ); + + // Reserve 16k of memory so we can report OOM fatals + self::$reservedMemory = str_repeat( ' ', 16384 ); + register_shutdown_function( + array( 'MWExceptionHandler', 'handleFatalError' ) + ); } /** @@ -194,6 +206,9 @@ class MWExceptionHandler { case E_USER_DEPRECATED: $levelName = 'Deprecated'; break; + case /* HHVM's FATAL_ERROR */ 16777217: + $levelName = 'Fatal'; + break; default: $levelName = 'Unknown error'; break; @@ -207,6 +222,44 @@ class MWExceptionHandler { return false; } + + /** + * Look for a fatal error as the cause of the request termination and log + * as an exception. + * + * Special handling is included for missing class errors as they may + * indicate that the user needs to install 3rd-party libraries via + * Composer or other means. + * + * @since 1.25 + */ + public static function handleFatalError() { + self::$reservedMemory = null; + $lastError = error_get_last(); + + if ( $lastError && + isset( $lastError['type'] ) && + in_array( $lastError['type'], self::$fatalErrorTypes ) + ) { + $msg = "Fatal Error: {$lastError['message']}"; + // HHVM: Class undefined: foo + // PHP5: Class 'foo' not found + if ( preg_match( "/Class (undefined: \w+|'\w+' not found)/", + $lastError['message'] + ) ) { + $msg = <<mediawiki.org for help on installing the required components. +TXT; + } + $e = new ErrorException( $msg, 0, $lastError['type'] ); + self::logError( $e ); + } + } + /** * Generate a string representation of an exception's stack trace * -- 2.20.1