Paladox has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/322772

Change subject: Revert I5dfdc84e94ddac65417226cf7c84513ebb9f9faa and 
I671f1a88f0192a5199cfc9c6d7fbde1bff5c4ac8
......................................................................

Revert I5dfdc84e94ddac65417226cf7c84513ebb9f9faa and 
I671f1a88f0192a5199cfc9c6d7fbde1bff5c4ac8

Bug: T148957
Change-Id: Ie02d1b0aeea58c12d37899219d2e23a011eb1c36
---
M autoload.php
M includes/exception/MWException.php
M includes/exception/MWExceptionHandler.php
D includes/exception/MWExceptionRenderer.php
M includes/libs/rdbms/exception/DBError.php
5 files changed, 561 insertions(+), 446 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/72/322772/1

diff --git a/autoload.php b/autoload.php
index 9171494..059a480 100644
--- a/autoload.php
+++ b/autoload.php
@@ -303,22 +303,22 @@
        'CsvStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'CurlHttpRequest' => __DIR__ . '/includes/http/CurlHttpRequest.php',
        'DBAccessBase' => __DIR__ . '/includes/dao/DBAccessBase.php',
-       'DBAccessError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBAccessError.php',
+       'DBAccessError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
        'DBAccessObjectUtils' => __DIR__ . 
'/includes/dao/DBAccessObjectUtils.php',
        'DBConnRef' => __DIR__ . '/includes/libs/rdbms/database/DBConnRef.php',
-       'DBConnectionError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBConnectionError.php',
+       'DBConnectionError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
        'DBError' => __DIR__ . '/includes/libs/rdbms/exception/DBError.php',
-       'DBExpectedError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBExpectedError.php',
+       'DBExpectedError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
        'DBFileJournal' => __DIR__ . 
'/includes/filebackend/filejournal/DBFileJournal.php',
        'DBLockManager' => __DIR__ . 
'/includes/libs/lockmanager/DBLockManager.php',
        'DBMasterPos' => __DIR__ . 
'/includes/libs/rdbms/database/position/DBMasterPos.php',
-       'DBQueryError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBQueryError.php',
-       'DBReadOnlyError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBReadOnlyError.php',
-       'DBReplicationWaitError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBReplicationWaitError.php',
+       'DBQueryError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
+       'DBReadOnlyError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
+       'DBReplicationWaitError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
        'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
-       'DBTransactionError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBTransactionError.php',
-       'DBTransactionSizeError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBTransactionSizeError.php',
-       'DBUnexpectedError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBUnexpectedError.php',
+       'DBTransactionError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
+       'DBTransactionSizeError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
+       'DBUnexpectedError' => __DIR__ . 
'/includes/libs/rdbms/exception/DBError.php',
        'DataUpdate' => __DIR__ . '/includes/deferred/DataUpdate.php',
        'Database' => __DIR__ . '/includes/libs/rdbms/database/Database.php',
        'DatabaseBase' => __DIR__ . 
'/includes/libs/rdbms/database/Database.php',
@@ -780,7 +780,6 @@
        'MWDocGen' => __DIR__ . '/maintenance/mwdocgen.php',
        'MWException' => __DIR__ . '/includes/exception/MWException.php',
        'MWExceptionHandler' => __DIR__ . 
'/includes/exception/MWExceptionHandler.php',
-       'MWExceptionRenderer' => __DIR__ . 
'/includes/exception/MWExceptionRenderer.php',
        'MWFileProps' => __DIR__ . '/includes/utils/MWFileProps.php',
        'MWGrants' => __DIR__ . '/includes/MWGrants.php',
        'MWHttpRequest' => __DIR__ . '/includes/http/MWHttpRequest.php',
diff --git a/includes/exception/MWException.php 
b/includes/exception/MWException.php
index e958c94..0a174fe 100644
--- a/includes/exception/MWException.php
+++ b/includes/exception/MWException.php
@@ -71,7 +71,37 @@
         * @return string|null String to output or null if any hook has been 
called
         */
        public function runHooks( $name, $args = [] ) {
-               return MWExceptionRenderer::runHooks( $this, $name, $args );
+               global $wgExceptionHooks;
+
+               if ( !isset( $wgExceptionHooks ) || !is_array( 
$wgExceptionHooks ) ) {
+                       return null; // Just silently ignore
+               }
+
+               if ( !array_key_exists( $name, $wgExceptionHooks ) ||
+                       !is_array( $wgExceptionHooks[$name] )
+               ) {
+                       return null;
+               }
+
+               $hooks = $wgExceptionHooks[$name];
+               $callargs = array_merge( [ $this ], $args );
+
+               foreach ( $hooks as $hook ) {
+                       if (
+                               is_string( $hook ) ||
+                               ( is_array( $hook ) && count( $hook ) >= 2 && 
is_string( $hook[0] ) )
+                       ) {
+                               // 'function' or [ 'class', 'hook' ]
+                               $result = call_user_func_array( $hook, 
$callargs );
+                       } else {
+                               $result = null;
+                       }
+
+                       if ( is_string( $result ) ) {
+                               return $result;
+                       }
+               }
+               return null;
        }
 
        /**
@@ -206,13 +236,7 @@
                        self::header( 'MediaWiki-API-Error: 
internal_api_error_' . get_class( $this ) );
                        wfHttpError( 500, 'Internal Server Error', 
$this->getText() );
                } elseif ( self::isCommandLine() ) {
-                       $message = $this->getText();
-                       // T17602: STDERR may not be available
-                       if ( defined( 'STDERR' ) ) {
-                               fwrite( STDERR, $message );
-                       } else {
-                               echo $message;
-                       }
+                       MWExceptionHandler::printError( $this->getText() );
                } else {
                        self::statusHeader( 500 );
                        self::header( "Content-Type: $wgMimeType; 
charset=utf-8" );
diff --git a/includes/exception/MWExceptionHandler.php 
b/includes/exception/MWExceptionHandler.php
index 736cb06..9c83d3c 100644
--- a/includes/exception/MWExceptionHandler.php
+++ b/includes/exception/MWExceptionHandler.php
@@ -60,21 +60,71 @@
         * @param Exception|Throwable $e
         */
        protected static function report( $e ) {
-               try {
-                       // Try and show the exception prettily, with the normal 
skin infrastructure
-                       if ( $e instanceof MWException ) {
-                               // Delegate to MWException until all subclasses 
are handled by
-                               // MWExceptionRenderer and 
MWException::report() has been
-                               // removed.
+               global $wgShowExceptionDetails;
+
+               $cmdLine = MWException::isCommandLine();
+
+               if ( $e instanceof MWException ) {
+                       try {
+                               // Try and show the exception prettily, with 
the normal skin infrastructure
                                $e->report();
-                       } else {
-                               MWExceptionRenderer::output( $e, 
MWExceptionRenderer::AS_PRETTY );
+                       } catch ( Exception $e2 ) {
+                               // Exception occurred from within exception 
handler
+                               // Show a simpler message for the original 
exception,
+                               // don't try to invoke report()
+                               $message = "MediaWiki internal error.\n\n";
+
+                               if ( $wgShowExceptionDetails ) {
+                                       $message .= 'Original exception: ' . 
self::getLogMessage( $e ) .
+                                               "\nBacktrace:\n" . 
self::getRedactedTraceAsString( $e ) .
+                                               "\n\nException caught inside 
exception handler: " . self::getLogMessage( $e2 ) .
+                                               "\nBacktrace:\n" . 
self::getRedactedTraceAsString( $e2 );
+                               } else {
+                                       $message .= "Exception caught inside 
exception handler.\n\n" .
+                                               "Set \$wgShowExceptionDetails = 
true; at the bottom of LocalSettings.php " .
+                                               "to show detailed debugging 
information.";
+                               }
+
+                               $message .= "\n";
+
+                               if ( $cmdLine ) {
+                                       self::printError( $message );
+                               } else {
+                                       echo nl2br( htmlspecialchars( $message 
) ) . "\n";
+                               }
                        }
-               } catch ( Exception $e2 ) {
-                       // Exception occurred from within exception handler
-                       // Show a simpler message for the original exception,
-                       // don't try to invoke report()
-                       MWExceptionRenderer::output( $e, 
MWExceptionRenderer::AS_RAW, $e2 );
+               } else {
+                       if ( !$wgShowExceptionDetails ) {
+                               $message = self::getPublicLogMessage( $e );
+                       } else {
+                               $message = self::getLogMessage( $e ) .
+                                       "\nBacktrace:\n" .
+                                       self::getRedactedTraceAsString( $e ) . 
"\n";
+                       }
+
+                       if ( $cmdLine ) {
+                               self::printError( $message );
+                       } else {
+                               echo nl2br( htmlspecialchars( $message ) ) . 
"\n";
+                       }
+
+               }
+       }
+
+       /**
+        * Print a message, if possible to STDERR.
+        * Use this in command line mode only (see isCommandLine)
+        *
+        * @param string $message Failure text
+        */
+       public static function printError( $message ) {
+               # NOTE: STDERR may not be available, especially if php-cgi is 
used from the
+               # command line (bug #15602). Try to produce meaningful output 
anyway. Using
+               # echo may corrupt output to STDOUT though.
+               if ( defined( 'STDERR' ) ) {
+                       fwrite( STDERR, $message );
+               } else {
+                       echo $message;
                }
        }
 
@@ -87,12 +137,7 @@
         * @param Exception|Throwable $e
         */
        public static function rollbackMasterChangesAndLog( $e ) {
-               $services = MediaWikiServices::getInstance();
-               if ( $services->isServiceDisabled( 'DBLoadBalancerFactory' ) ) {
-                       return; // T147599
-               }
-
-               $lbFactory = $services->getDBLoadBalancerFactory();
+               $lbFactory = 
MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                if ( $lbFactory->hasMasterChanges() ) {
                        $logger = LoggerFactory::getInstance( 'Bug56269' );
                        $logger->warning(
diff --git a/includes/exception/MWExceptionRenderer.php 
b/includes/exception/MWExceptionRenderer.php
deleted file mode 100644
index 8fdc417..0000000
--- a/includes/exception/MWExceptionRenderer.php
+++ /dev/null
@@ -1,405 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @author Aaron Schulz
- */
-
-/**
- * Class to expose exceptions to the client (API bots, users, admins using CLI 
scripts)
- * @since 1.28
- */
-class MWExceptionRenderer {
-       const AS_RAW = 1; // show as text
-       const AS_PRETTY = 2; // show as HTML
-
-       /**
-        * @param Exception|Throwable $e Original exception
-        * @param integer $mode MWExceptionExposer::AS_* constant
-        * @param Exception|Throwable|null $eNew New exception from attempting 
to show the first
-        */
-       public static function output( $e, $mode, $eNew = null ) {
-               global $wgMimeType;
-
-               if ( defined( 'MW_API' ) ) {
-                       // Unhandled API exception, we can't be sure that 
format printer is alive
-                       self::header( 'MediaWiki-API-Error: 
internal_api_error_' . get_class( $e ) );
-                       wfHttpError( 500, 'Internal Server Error', 
self::getText( $e ) );
-               } elseif ( self::isCommandLine() ) {
-                       self::printError( self::getText( $e ) );
-               } elseif ( $mode === self::AS_PRETTY ) {
-                       if ( $e instanceof DBConnectionError ) {
-                               self::reportOutageHTML( $e );
-                       } else {
-                               self::statusHeader( 500 );
-                               self::header( "Content-Type: $wgMimeType; 
charset=utf-8" );
-                               self::reportHTML( $e );
-                       }
-               } else {
-                       if ( $eNew ) {
-                               $message = "MediaWiki internal error.\n\n";
-                               if ( self::showBackTrace( $e ) ) {
-                                       $message .= 'Original exception: ' .
-                                               
MWExceptionHandler::getLogMessage( $e ) .
-                                               "\nBacktrace:\n" . 
MWExceptionHandler::getRedactedTraceAsString( $e ) .
-                                               "\n\nException caught inside 
exception handler: " .
-                                                       
MWExceptionHandler::getLogMessage( $eNew ) .
-                                               "\nBacktrace:\n" . 
MWExceptionHandler::getRedactedTraceAsString( $eNew );
-                               } else {
-                                       $message .= "Exception caught inside 
exception handler.\n\n" .
-                                               "Set \$wgShowExceptionDetails = 
true; at the bottom of LocalSettings.php " .
-                                               "to show detailed debugging 
information.";
-                               }
-                               $message .= "\n";
-                       } else {
-                               if ( self::showBackTrace( $e ) ) {
-                                       $message = 
MWExceptionHandler::getLogMessage( $e ) .
-                                               "\nBacktrace:\n" .
-                                               
MWExceptionHandler::getRedactedTraceAsString( $e ) . "\n";
-                               } else {
-                                       $message = 
MWExceptionHandler::getPublicLogMessage( $e );
-                               }
-                       }
-                       if ( self::isCommandLine() ) {
-                               self::printError( $message );
-                       } else {
-                               echo nl2br( htmlspecialchars( $message ) ) . 
"\n";
-                       }
-               }
-       }
-
-       /**
-        * Run hook to allow extensions to modify the text of the exception
-        *
-        * Called by MWException for b/c
-        *
-        * @param Exception|Throwable $e
-        * @param string $name Class name of the exception
-        * @param array $args Arguments to pass to the callback functions
-        * @return string|null String to output or null if any hook has been 
called
-        */
-       public static function runHooks( $e, $name, $args = [] ) {
-               global $wgExceptionHooks;
-
-               if ( !isset( $wgExceptionHooks ) || !is_array( 
$wgExceptionHooks ) ) {
-                       return null; // Just silently ignore
-               }
-
-               if ( !array_key_exists( $name, $wgExceptionHooks ) ||
-                       !is_array( $wgExceptionHooks[$name] )
-               ) {
-                       return null;
-               }
-
-               $hooks = $wgExceptionHooks[$name];
-               $callargs = array_merge( [ $e ], $args );
-
-               foreach ( $hooks as $hook ) {
-                       if (
-                               is_string( $hook ) ||
-                               ( is_array( $hook ) && count( $hook ) >= 2 && 
is_string( $hook[0] ) )
-                       ) {
-                               // 'function' or [ 'class', 'hook' ]
-                               $result = call_user_func_array( $hook, 
$callargs );
-                       } else {
-                               $result = null;
-                       }
-
-                       if ( is_string( $result ) ) {
-                               return $result;
-                       }
-               }
-
-               return null;
-       }
-
-       /**
-        * @param Exception|Throwable $e
-        * @return bool Should the exception use $wgOut to output the error?
-        */
-       private static function useOutputPage( $e ) {
-               // Can the extension use the Message class/wfMessage to get 
i18n-ed messages?
-               foreach ( $e->getTrace() as $frame ) {
-                       if ( isset( $frame['class'] ) && $frame['class'] === 
'LocalisationCache' ) {
-                               return false;
-                       }
-               }
-
-               return (
-                       !empty( $GLOBALS['wgFullyInitialised'] ) &&
-                       !empty( $GLOBALS['wgOut'] ) &&
-                       !defined( 'MEDIAWIKI_INSTALL' )
-               );
-       }
-
-       /**
-        * Output the exception report using HTML
-        *
-        * @param Exception|Throwable $e
-        */
-       private static function reportHTML( $e ) {
-               global $wgOut, $wgSitename;
-
-               if ( self::useOutputPage( $e ) ) {
-                       if ( $e instanceof MWException ) {
-                               $wgOut->prepareErrorPage( $e->getPageTitle() );
-                       } elseif ( $e instanceof DBReadOnlyError ) {
-                               $wgOut->prepareErrorPage( self::msg( 
'readonly', 'Database is locked' ) );
-                       } elseif ( $e instanceof DBExpectedError ) {
-                               $wgOut->prepareErrorPage( self::msg( 
'databaseerror', 'Database error' ) );
-                       } else {
-                               $wgOut->prepareErrorPage( self::msg( 
'internalerror', 'Internal error' ) );
-                       }
-
-                       $hookResult = self::runHooks( $e, get_class( $e ) );
-                       if ( $hookResult ) {
-                               $wgOut->addHTML( $hookResult );
-                       } else {
-                               // Show any custom GUI message before the 
details
-                               if ( $e instanceof MessageSpecifier ) {
-                                       $wgOut->addHTML( 
Message::newFromSpecifier( $e )->escaped() );
-                               }
-                               $wgOut->addHTML( self::getHTML( $e ) );
-                       }
-
-                       $wgOut->output();
-               } else {
-                       self::header( 'Content-Type: text/html; charset=utf-8' 
);
-                       $pageTitle = self::msg( 'internalerror', 'Internal 
error' );
-                       echo "<!DOCTYPE html>\n" .
-                               '<html><head>' .
-                               // Mimick OutputPage::setPageTitle behaviour
-                               '<title>' .
-                               htmlspecialchars( self::msg( 'pagetitle', "$1 - 
$wgSitename", $pageTitle ) ) .
-                               '</title>' .
-                               '<style>body { font-family: sans-serif; margin: 
0; padding: 0.5em 2em; }</style>' .
-                               "</head><body>\n";
-
-                       $hookResult = self::runHooks( $e, get_class( $e ) . 
'Raw' );
-                       if ( $hookResult ) {
-                               echo $hookResult;
-                       } else {
-                               echo self::getHTML( $e );
-                       }
-
-                       echo "</body></html>\n";
-               }
-       }
-
-       /**
-        * If $wgShowExceptionDetails is true, return a HTML message with a
-        * backtrace to the error, otherwise show a message to ask to set it to 
true
-        * to show that information.
-        *
-        * @param Exception|Throwable $e
-        * @return string Html to output
-        */
-       public static function getHTML( $e ) {
-               if ( self::showBackTrace( $e ) ) {
-                       $html = "<div class=\"errorbox\"><p>" .
-                               nl2br( htmlspecialchars( 
MWExceptionHandler::getLogMessage( $e ) ) ) .
-                               '</p><p>Backtrace:</p><p>' .
-                               nl2br( htmlspecialchars( 
MWExceptionHandler::getRedactedTraceAsString( $e ) ) ) .
-                               "</p></div>\n";
-               } else {
-                       $logId = WebRequest::getRequestId();
-                       $html = "<div class=\"errorbox\">" .
-                               '[' . $logId . '] ' .
-                               gmdate( 'Y-m-d H:i:s' ) . ": " .
-                               self::msg( "internalerror-fatal-exception",
-                                       "Fatal exception of type $1",
-                                       get_class( $e ),
-                                       $logId,
-                                       MWExceptionHandler::getURL()
-                               ) . "</div>\n" .
-                       "<!-- Set \$wgShowExceptionDetails = true; " .
-                       "at the bottom of LocalSettings.php to show detailed " .
-                       "debugging information. -->";
-               }
-
-               return $html;
-       }
-
-       /**
-        * Get a message from i18n
-        *
-        * @param string $key Message name
-        * @param string $fallback Default message if the message cache can't be
-        *                  called by the exception
-        * The function also has other parameters that are arguments for the 
message
-        * @return string Message with arguments replaced
-        */
-       private static function msg( $key, $fallback /*[, params...] */ ) {
-               $args = array_slice( func_get_args(), 2 );
-               try {
-                       return wfMessage( $key, $args )->text();
-               } catch ( Exception $e ) {
-                       return wfMsgReplaceArgs( $fallback, $args );
-               }
-       }
-
-       /**
-        * @param Exception|Throwable $e
-        * @return string
-        */
-       private static function getText( $e ) {
-               if ( self::showBackTrace( $e ) ) {
-                       return MWExceptionHandler::getLogMessage( $e ) .
-                               "\nBacktrace:\n" .
-                               MWExceptionHandler::getRedactedTraceAsString( 
$e ) . "\n";
-               } else {
-                       return "Set \$wgShowExceptionDetails = true; " .
-                               "in LocalSettings.php to show detailed 
debugging information.\n";
-               }
-       }
-
-       /**
-        * @param Exception|Throwable $e
-        * @return bool
-        */
-       private static function showBackTrace( $e ) {
-               global $wgShowExceptionDetails, $wgShowDBErrorBacktrace;
-
-               return (
-                       $wgShowExceptionDetails &&
-                       ( !( $e instanceof DBError ) || $wgShowDBErrorBacktrace 
)
-               );
-       }
-
-       /**
-        * @return bool
-        */
-       private static function isCommandLine() {
-               return !empty( $GLOBALS['wgCommandLineMode'] );
-       }
-
-       /**
-        * @param string $header
-        */
-       private static function header( $header ) {
-               if ( !headers_sent() ) {
-                       header( $header );
-               }
-       }
-
-       /**
-        * @param integer $code
-        */
-       private static function statusHeader( $code ) {
-               if ( !headers_sent() ) {
-                       HttpStatus::header( $code );
-               }
-       }
-
-       /**
-        * Print a message, if possible to STDERR.
-        * Use this in command line mode only (see isCommandLine)
-        *
-        * @param string $message Failure text
-        */
-       private static function printError( $message ) {
-               // NOTE: STDERR may not be available, especially if php-cgi is 
used from the
-               // command line (bug #15602). Try to produce meaningful output 
anyway. Using
-               // echo may corrupt output to STDOUT though.
-               if ( defined( 'STDERR' ) ) {
-                       fwrite( STDERR, $message );
-               } else {
-                       echo $message;
-               }
-       }
-
-       /**
-        * @param Exception|Throwable $e
-        */
-       private static function reportOutageHTML( $e ) {
-               global $wgShowDBErrorBacktrace, $wgShowHostnames, 
$wgShowSQLErrors;
-
-               $sorry = htmlspecialchars( self::msg(
-                       'dberr-problems',
-                       'Sorry! This site is experiencing technical 
difficulties.'
-               ) );
-               $again = htmlspecialchars( self::msg(
-                       'dberr-again',
-                       'Try waiting a few minutes and reloading.'
-               ) );
-
-               if ( $wgShowHostnames || $wgShowSQLErrors ) {
-                       $info = str_replace(
-                               '$1',
-                               Html::element( 'span', [ 'dir' => 'ltr' ], 
htmlspecialchars( $e->getMessage() ) ),
-                               htmlspecialchars( self::msg( 'dberr-info', 
'($1)' ) )
-                       );
-               } else {
-                       $info = htmlspecialchars( self::msg(
-                               'dberr-info-hidden',
-                               '(Cannot access the database)'
-                       ) );
-               }
-
-               MessageCache::singleton()->disable(); // no DB access
-
-               $html = 
"<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
-
-               if ( $wgShowDBErrorBacktrace ) {
-                       $html .= '<p>Backtrace:</p><pre>' .
-                               htmlspecialchars( $e->getTraceAsString() ) . 
'</pre>';
-               }
-
-               $html .= '<hr />';
-               $html .= self::googleSearchForm();
-
-               echo $html;
-       }
-
-       /**
-        * @return string
-        */
-       private static function googleSearchForm() {
-               global $wgSitename, $wgCanonicalServer, $wgRequest;
-
-               $usegoogle = htmlspecialchars( self::msg(
-                       'dberr-usegoogle',
-                       'You can try searching via Google in the meantime.'
-               ) );
-               $outofdate = htmlspecialchars( self::msg(
-                       'dberr-outofdate',
-                       'Note that their indexes of our content may be out of 
date.'
-               ) );
-               $googlesearch = htmlspecialchars( self::msg( 'searchbutton', 
'Search' ) );
-               $search = htmlspecialchars( $wgRequest->getVal( 'search' ) );
-               $server = htmlspecialchars( $wgCanonicalServer );
-               $sitename = htmlspecialchars( $wgSitename );
-               $trygoogle = <<<EOT
-<div style="margin: 1.5em">$usegoogle<br />
-<small>$outofdate</small>
-</div>
-<form method="get" action="//www.google.com/search" id="googlesearch">
-       <input type="hidden" name="domains" value="$server" />
-       <input type="hidden" name="num" value="50" />
-       <input type="hidden" name="ie" value="UTF-8" />
-       <input type="hidden" name="oe" value="UTF-8" />
-       <input type="text" name="q" size="31" maxlength="255" value="$search" />
-       <input type="submit" name="btnG" value="$googlesearch" />
-       <p>
-               <label><input type="radio" name="sitesearch" value="$server" 
checked="checked" />$sitename</label>
-               <label><input type="radio" name="sitesearch" value="" 
/>WWW</label>
-       </p>
-</form>
-EOT;
-               return $trygoogle;
-       }
-}
diff --git a/includes/libs/rdbms/exception/DBError.php 
b/includes/libs/rdbms/exception/DBError.php
index 526596d..a8e8546 100644
--- a/includes/libs/rdbms/exception/DBError.php
+++ b/includes/libs/rdbms/exception/DBError.php
@@ -1,5 +1,7 @@
 <?php
 /**
+ * This file contains database error classes.
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -23,17 +25,467 @@
  * Database error base class
  * @ingroup Database
  */
-class DBError extends Exception {
-       /** @var IDatabase|null */
+class DBError extends MWException {
+       /** @var DatabaseBase */
        public $db;
 
        /**
         * Construct a database error
-        * @param IDatabase $db Object which threw the error
+        * @param DatabaseBase $db Object which threw the error
         * @param string $error A simple error message to be used for debugging
         */
-       function __construct( IDatabase $db = null, $error ) {
+       function __construct( DatabaseBase $db = null, $error ) {
                $this->db = $db;
                parent::__construct( $error );
        }
 }
+
+/**
+ * Base class for the more common types of database errors. These are known to 
occur
+ * frequently, so we try to give friendly error messages for them.
+ *
+ * @ingroup Database
+ * @since 1.23
+ */
+class DBExpectedError extends DBError {
+       /**
+        * @return string
+        */
+       function getText() {
+               global $wgShowDBErrorBacktrace;
+
+               $s = $this->getTextContent() . "\n";
+
+               if ( $wgShowDBErrorBacktrace ) {
+                       $s .= "Backtrace:\n" . $this->getTraceAsString() . "\n";
+               }
+
+               return $s;
+       }
+
+       /**
+        * @return string
+        */
+       function getHTML() {
+               global $wgShowDBErrorBacktrace;
+
+               $s = $this->getHTMLContent();
+
+               if ( $wgShowDBErrorBacktrace ) {
+                       $s .= '<p>Backtrace:</p><pre>' . htmlspecialchars( 
$this->getTraceAsString() ) . '</pre>';
+               }
+
+               return $s;
+       }
+
+       function getPageTitle() {
+               return $this->msg( 'databaseerror', 'Database error' );
+       }
+
+       /**
+        * @return string
+        */
+       protected function getTextContent() {
+               return $this->getMessage();
+       }
+
+       /**
+        * @return string
+        */
+       protected function getHTMLContent() {
+               return '<p>' . nl2br( htmlspecialchars( $this->getTextContent() 
) ) . '</p>';
+       }
+}
+
+/**
+ * @ingroup Database
+ */
+class DBConnectionError extends DBExpectedError {
+       /** @var string Error text */
+       public $error;
+
+       /**
+        * @param DatabaseBase $db Object throwing the error
+        * @param string $error Error text
+        */
+       function __construct( DatabaseBase $db = null, $error = 'unknown error' 
) {
+               $msg = 'DB connection error';
+
+               if ( trim( $error ) != '' ) {
+                       $msg .= ": $error";
+               } elseif ( $db ) {
+                       $error = $this->db->getServer();
+               }
+
+               parent::__construct( $db, $msg );
+               $this->error = $error;
+       }
+
+       /**
+        * @return bool
+        */
+       function useOutputPage() {
+               // Not likely to work
+               return false;
+       }
+
+       /**
+        * @param string $key
+        * @param string $fallback Unescaped alternative error text in case the
+        *   message cache cannot be used. Can contain parameters as in regular
+        *   messages, that should be passed as additional parameters.
+        * @return string Unprocessed plain error text with parameters replaced
+        */
+       function msg( $key, $fallback /*[, params...] */ ) {
+               $args = array_slice( func_get_args(), 2 );
+
+               if ( $this->useMessageCache() ) {
+                       return wfMessage( $key, $args )->useDatabase( false 
)->text();
+               } else {
+                       return wfMsgReplaceArgs( $fallback, $args );
+               }
+       }
+
+       /**
+        * @return bool
+        */
+       function isLoggable() {
+               // Don't send to the exception log, already in dberror log
+               return false;
+       }
+
+       /**
+        * @return string Safe HTML
+        */
+       function getHTML() {
+               global $wgShowDBErrorBacktrace, $wgShowHostnames, 
$wgShowSQLErrors;
+
+               $sorry = htmlspecialchars( $this->msg(
+                       'dberr-problems',
+                       'Sorry! This site is experiencing technical 
difficulties.'
+               ) );
+               $again = htmlspecialchars( $this->msg(
+                       'dberr-again',
+                       'Try waiting a few minutes and reloading.'
+               ) );
+
+               if ( $wgShowHostnames || $wgShowSQLErrors ) {
+                       $info = str_replace(
+                               '$1', Html::element( 'span', [ 'dir' => 'ltr' 
], $this->error ),
+                               htmlspecialchars( $this->msg( 'dberr-info', 
'(Cannot access the database: $1)' ) )
+                       );
+               } else {
+                       $info = htmlspecialchars( $this->msg(
+                               'dberr-info-hidden',
+                               '(Cannot access the database)'
+                       ) );
+               }
+
+               # No database access
+               MessageCache::singleton()->disable();
+
+               $html = 
"<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
+
+               if ( $wgShowDBErrorBacktrace ) {
+                       $html .= '<p>Backtrace:</p><pre>' . htmlspecialchars( 
$this->getTraceAsString() ) . '</pre>';
+               }
+
+               $html .= '<hr />';
+               $html .= $this->searchForm();
+
+               return $html;
+       }
+
+       protected function getTextContent() {
+               global $wgShowHostnames, $wgShowSQLErrors;
+
+               if ( $wgShowHostnames || $wgShowSQLErrors ) {
+                       return $this->getMessage();
+               } else {
+                       return 'DB connection error';
+               }
+       }
+
+       /**
+        * Output the exception report using HTML.
+        *
+        * @return void
+        */
+       public function reportHTML() {
+               global $wgUseFileCache;
+
+               // Check whether we can serve a file-cached copy of the page 
with the error underneath
+               if ( $wgUseFileCache ) {
+                       try {
+                               $cache = $this->fileCachedPage();
+                               // Cached version on file system?
+                               if ( $cache !== null ) {
+                                       // Hack: extend the body for error 
messages
+                                       $cache = str_replace( [ '</html>', 
'</body>' ], '', $cache );
+                                       // Add cache notice...
+                                       $cache .= '<div style="border:1px solid 
#ffd0d0;padding:1em;">' .
+                                               htmlspecialchars( $this->msg( 
'dberr-cachederror',
+                                                       'This is a cached copy 
of the requested page, and may not be up to date.' ) ) .
+                                               '</div>';
+
+                                       // Output cached page with notices on 
bottom and re-close body
+                                       echo "{$cache}<hr 
/>{$this->getHTML()}</body></html>";
+
+                                       return;
+                               }
+                       } catch ( Exception $e ) {
+                               // Do nothing, just use the default page
+                       }
+               }
+
+               // We can't, cough and die in the usual fashion
+               parent::reportHTML();
+       }
+
+       /**
+        * @return string
+        */
+       function searchForm() {
+               global $wgSitename, $wgCanonicalServer, $wgRequest;
+
+               $usegoogle = htmlspecialchars( $this->msg(
+                       'dberr-usegoogle',
+                       'You can try searching via Google in the meantime.'
+               ) );
+               $outofdate = htmlspecialchars( $this->msg(
+                       'dberr-outofdate',
+                       'Note that their indexes of our content may be out of 
date.'
+               ) );
+               $googlesearch = htmlspecialchars( $this->msg( 'searchbutton', 
'Search' ) );
+
+               $search = htmlspecialchars( $wgRequest->getVal( 'search' ) );
+
+               $server = htmlspecialchars( $wgCanonicalServer );
+               $sitename = htmlspecialchars( $wgSitename );
+
+               $trygoogle = <<<EOT
+<div style="margin: 1.5em">$usegoogle<br />
+<small>$outofdate</small>
+</div>
+<form method="get" action="//www.google.com/search" id="googlesearch">
+       <input type="hidden" name="domains" value="$server" />
+       <input type="hidden" name="num" value="50" />
+       <input type="hidden" name="ie" value="UTF-8" />
+       <input type="hidden" name="oe" value="UTF-8" />
+
+       <input type="text" name="q" size="31" maxlength="255" value="$search" />
+       <input type="submit" name="btnG" value="$googlesearch" />
+       <p>
+               <label><input type="radio" name="sitesearch" value="$server" 
checked="checked" />$sitename</label>
+               <label><input type="radio" name="sitesearch" value="" 
/>WWW</label>
+       </p>
+</form>
+EOT;
+
+               return $trygoogle;
+       }
+
+       /**
+        * @return string
+        */
+       private function fileCachedPage() {
+               $context = RequestContext::getMain();
+
+               if ( $context->getOutput()->isDisabled() ) {
+                       // Done already?
+                       return '';
+               }
+
+               if ( $context->getTitle() ) {
+                       // Use the main context's title if we managed to set it
+                       $t = $context->getTitle()->getPrefixedDBkey();
+               } else {
+                       // Fallback to the raw title URL param. We can't use 
the Title
+                       // class is it may hit the interwiki table and give a 
DB error.
+                       // We may get a cache miss due to not sanitizing the 
title though.
+                       $t = str_replace( ' ', '_', 
$context->getRequest()->getVal( 'title' ) );
+                       if ( $t == '' ) { // fallback to main page
+                               $t = Title::newFromText(
+                                       $this->msg( 'mainpage', 'Main Page' ) 
)->getPrefixedDBkey();
+                       }
+               }
+
+               $cache = new HTMLFileCache( $t, 'view' );
+               if ( $cache->isCached() ) {
+                       return $cache->fetchText();
+               } else {
+                       return '';
+               }
+       }
+}
+
+/**
+ * @ingroup Database
+ */
+class DBQueryError extends DBExpectedError {
+       public $error, $errno, $sql, $fname;
+
+       /**
+        * @param DatabaseBase $db
+        * @param string $error
+        * @param int|string $errno
+        * @param string $sql
+        * @param string $fname
+        */
+       function __construct( DatabaseBase $db, $error, $errno, $sql, $fname ) {
+               if ( $db->wasConnectionError( $errno ) ) {
+                       $message = "A connection error occured. \n" .
+                               "Query: $sql\n" .
+                               "Function: $fname\n" .
+                               "Error: $errno $error\n";
+               } else {
+                       $message = "A database error has occurred. Did you 
forget to run " .
+                               "maintenance/update.php after upgrading?  See: 
" .
+                               
"https://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n"; .
+                               "Query: $sql\n" .
+                               "Function: $fname\n" .
+                               "Error: $errno $error\n";
+               }
+               parent::__construct( $db, $message );
+
+               $this->error = $error;
+               $this->errno = $errno;
+               $this->sql = $sql;
+               $this->fname = $fname;
+       }
+
+       /**
+        * @return string
+        */
+       function getPageTitle() {
+               return $this->msg( 'databaseerror', 'Database error' );
+       }
+
+       /**
+        * @return string
+        */
+       protected function getHTMLContent() {
+               $key = 'databaseerror-text';
+               $s = Html::element( 'p', [], $this->msg( $key, 
$this->getFallbackMessage( $key ) ) );
+
+               $details = $this->getTechnicalDetails();
+               if ( $details ) {
+                       $s .= '<ul>';
+                       foreach ( $details as $key => $detail ) {
+                               $s .= str_replace(
+                                       '$1', call_user_func_array( 
'Html::element', $detail ),
+                                       Html::element( 'li', [],
+                                               $this->msg( $key, 
$this->getFallbackMessage( $key ) )
+                                       )
+                               );
+                       }
+                       $s .= '</ul>';
+               }
+
+               return $s;
+       }
+
+       /**
+        * @return string
+        */
+       protected function getTextContent() {
+               $key = 'databaseerror-textcl';
+               $s = $this->msg( $key, $this->getFallbackMessage( $key ) ) . 
"\n";
+
+               foreach ( $this->getTechnicalDetails() as $key => $detail ) {
+                       $s .= $this->msg( $key, $this->getFallbackMessage( $key 
), $detail[2] ) . "\n";
+               }
+
+               return $s;
+       }
+
+       /**
+        * Make a list of technical details that can be shown to the user. This 
information can
+        * aid in debugging yet may be useful to an attacker trying to exploit 
a security weakness
+        * in the software or server configuration.
+        *
+        * Thus no such details are shown by default, though if 
$wgShowHostnames is true, only the
+        * full SQL query is hidden; in fact, the error message often does 
contain a hostname, and
+        * sites using this option probably don't care much about "security by 
obscurity". Of course,
+        * if $wgShowSQLErrors is true, the SQL query *is* shown.
+        *
+        * @return array Keys are message keys; values are arrays of arguments 
for Html::element().
+        *   Array will be empty if users are not allowed to see any of these 
details at all.
+        */
+       protected function getTechnicalDetails() {
+               global $wgShowHostnames, $wgShowSQLErrors;
+
+               $attribs = [ 'dir' => 'ltr' ];
+               $details = [];
+
+               if ( $wgShowSQLErrors ) {
+                       $details['databaseerror-query'] = [
+                               'div', [ 'class' => 'mw-code' ] + $attribs, 
$this->sql ];
+               }
+
+               if ( $wgShowHostnames || $wgShowSQLErrors ) {
+                       $errorMessage = $this->errno . ' ' . $this->error;
+                       $details['databaseerror-function'] = [ 'code', 
$attribs, $this->fname ];
+                       $details['databaseerror-error'] = [ 'samp', $attribs, 
$errorMessage ];
+               }
+
+               return $details;
+       }
+
+       /**
+        * @param string $key Message key
+        * @return string English message text
+        */
+       private function getFallbackMessage( $key ) {
+               $messages = [
+                       'databaseerror-text' => 'A database query error has 
occurred.
+This may indicate a bug in the software.',
+                       'databaseerror-textcl' => 'A database query error has 
occurred.',
+                       'databaseerror-query' => 'Query: $1',
+                       'databaseerror-function' => 'Function: $1',
+                       'databaseerror-error' => 'Error: $1',
+               ];
+
+               return $messages[$key];
+       }
+}
+
+/**
+ * @ingroup Database
+ */
+class DBUnexpectedError extends DBError {
+}
+
+/**
+ * @ingroup Database
+ */
+class DBReadOnlyError extends DBExpectedError {
+       function getPageTitle() {
+               return $this->msg( 'readonly', 'Database is locked' );
+       }
+}
+
+/**
+ * @ingroup Database
+ */
+class DBTransactionError extends DBExpectedError {
+}
+
+/**
+ * Exception class for attempted DB access
+ * @ingroup Database
+ */
+class DBAccessError extends DBUnexpectedError {
+       public function __construct() {
+               parent::__construct( "Mediawiki tried to access the database 
via wfGetDB(). " .
+                       "This is not allowed, because database access has been 
disabled." );
+       }
+}
+
+/**
+ * Exception class for replica DB wait timeouts
+ * @ingroup Database
+ */
+class DBReplicationWaitError extends DBUnexpectedError {
+}
+

-- 
To view, visit https://gerrit.wikimedia.org/r/322772
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie02d1b0aeea58c12d37899219d2e23a011eb1c36
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: REL1_28
Gerrit-Owner: Paladox <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to