= 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 $e * @return bool Should the exception use $wgOut to output the error? */ private static function useOutputPage( Exception $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 $e */ private static function reportHTML( Exception $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 "\n" . '' . // Mimick OutputPage::setPageTitle behaviour '' . htmlspecialchars( self::msg( 'pagetitle', "$1 - $wgSitename", $pageTitle ) ) . '' . '' . "\n"; $hookResult = self::runHooks( $e, get_class( $e ) . 'Raw' ); if ( $hookResult ) { echo $hookResult; } else { echo self::getHTML( $e ); } echo "\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 $e * @return string Html to output */ public static function getHTML( Exception $e ) { if ( self::showBackTrace( $e ) ) { $html = "

" . nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $e ) ) ) . '

Backtrace:

' . nl2br( htmlspecialchars( MWExceptionHandler::getRedactedTraceAsString( $e ) ) ) . "

\n"; } else { $logId = WebRequest::getRequestId(); $html = "
" . '[' . $logId . '] ' . gmdate( 'Y-m-d H:i:s' ) . ": " . self::msg( "internalerror-fatal-exception", "Fatal exception of type $1", get_class( $e ), $logId, MWExceptionHandler::getURL() ) . "
\n" . ""; } 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 $e * @return string */ private function getText( Exception $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 $e * @return bool */ private static function showBackTrace( Exception $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 $e */ private static function reportOutageHTML( Exception $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 = "

$sorry

$again

$info

"; if ( $wgShowDBErrorBacktrace ) { $html .= '

Backtrace:

' .
				htmlspecialchars( $e->getTraceAsString() ) . '
'; } $html .= '
'; $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 = <<$usegoogle
$outofdate

EOT; return $trygoogle; } }