filebackend: use self:: instead of FileBackend:: for some constant uses
[lhc/web/wiklou.git] / includes / exception / MWExceptionHandler.php
index bd823b5..b4e483b 100644 (file)
@@ -35,12 +35,35 @@ class MWExceptionHandler {
         * @var string $reservedMemory
         */
        protected static $reservedMemory;
+
        /**
+        * Error types that, if unhandled, are fatal to the request.
+        *
+        * On PHP 7, these error types may be thrown as Error objects, which
+        * implement Throwable (but not Exception).
+        *
+        * On HHVM, these invoke the set_error_handler callback, similar to how
+        * (non-fatal) warnings and notices are reported, except that after this
+        * handler runs for fatal error tpyes, script execution stops!
+        *
+        * The user will be shown an HTTP 500 Internal Server Error.
+        * As such, these should be sent to MediaWiki's "fatal" or "exception"
+        * channel. Normally, the error handler logs them to the "error" channel.
+        *
         * @var array $fatalErrorTypes
         */
        protected static $fatalErrorTypes = [
-               E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR,
-               /* HHVM's FATAL_ERROR level */ 16777217,
+               E_ERROR,
+               E_PARSE,
+               E_CORE_ERROR,
+               E_COMPILE_ERROR,
+               E_USER_ERROR,
+
+               // E.g. "Catchable fatal error: Argument X must be Y, null given"
+               E_RECOVERABLE_ERROR,
+
+               // HHVM's FATAL_ERROR constant
+               16777217,
        ];
        /**
         * @var bool $handledFatalCallback
@@ -51,9 +74,32 @@ class MWExceptionHandler {
         * Install handlers with PHP.
         */
        public static function installHandler() {
+               // This catches:
+               // * Exception objects that were explicitly thrown but not
+               //   caught anywhere in the application. This is rare given those
+               //   would normally be caught at a high-level like MediaWiki::run (index.php),
+               //   api.php, or ResourceLoader::respond (load.php). These high-level
+               //   catch clauses would then call MWExceptionHandler::logException
+               //   or MWExceptionHandler::handleException.
+               //   If they are not caught, then they are handled here.
+               // * Error objects (on PHP 7+), for issues that would historically
+               //   cause fatal errors but may now be caught as Throwable (not Exception).
+               //   Same as previous case, but more common to bubble to here instead of
+               //   caught locally because they tend to not be safe to recover from.
+               //   (e.g. argument TypeErorr, devision by zero, etc.)
                set_exception_handler( 'MWExceptionHandler::handleUncaughtException' );
+
+               // This catches:
+               // * Non-fatal errors (e.g. PHP Notice, PHP Warning, PHP Error) that do not
+               //   interrupt execution in any way. We log these in the background and then
+               //   continue execution.
+               // * Fatal errors (on HHVM in PHP5 mode) where PHP 7 would throw Throwable.
                set_error_handler( 'MWExceptionHandler::handleError' );
 
+               // This catches:
+               // * Fatal error for which no Throwable is thrown (PHP 7), and no Error emitted (HHVM).
+               //   This includes Out-Of-Memory and Timeout fatals.
+               //
                // Reserve 16k of memory so we can report OOM fatals
                self::$reservedMemory = str_repeat( ' ', 16384 );
                register_shutdown_function( 'MWExceptionHandler::handleFatalError' );
@@ -192,10 +238,6 @@ class MWExceptionHandler {
                // behaviour given the null was not part of the code and is likely not
                // accounted for.
                switch ( $level ) {
-                       case E_RECOVERABLE_ERROR:
-                               $levelName = 'Error';
-                               $severity = LogLevel::ERROR;
-                               break;
                        case E_WARNING:
                        case E_CORE_WARNING:
                        case E_COMPILE_WARNING:
@@ -206,8 +248,12 @@ class MWExceptionHandler {
                                $levelName = 'Notice';
                                $severity = LogLevel::ERROR;
                                break;
-                       case E_USER_WARNING:
                        case E_USER_NOTICE:
+                               // Used by wfWarn(), MWDebug::warning()
+                               $levelName = 'Notice';
+                               $severity = LogLevel::WARNING;
+                               break;
+                       case E_USER_WARNING:
                                // Used by wfWarn(), MWDebug::warning()
                                $levelName = 'Warning';
                                $severity = LogLevel::WARNING;
@@ -231,9 +277,9 @@ class MWExceptionHandler {
                self::logError( $e, 'error', $severity );
 
                // If $wgPropagateErrors is true return false so PHP shows/logs the error normally.
-               // Ignore $wgPropagateErrors if the error should break execution, or track_errors is set
+               // Ignore $wgPropagateErrors if track_errors is set
                // (which means someone is counting on regular PHP error handling behavior).
-               return !( $wgPropagateErrors || $level == E_RECOVERABLE_ERROR || ini_get( 'track_errors' ) );
+               return !( $wgPropagateErrors || ini_get( 'track_errors' ) );
        }
 
        /**
@@ -439,22 +485,6 @@ TXT;
                }, $trace );
        }
 
-       /**
-        * Get the ID for this exception.
-        *
-        * The ID is saved so that one can match the one output to the user (when
-        * $wgShowExceptionDetails is set to false), to the entry in the debug log.
-        *
-        * @since 1.22
-        * @deprecated since 1.27: Exception IDs are synonymous with request IDs.
-        * @param Exception|Throwable $e
-        * @return string
-        */
-       public static function getLogId( $e ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               return WebRequest::getRequestId();
-       }
-
        /**
         * If the exception occurred in the course of responding to a request,
         * returns the requested URL. Otherwise, returns false.