useMessageCache() && !empty( $GLOBALS['wgFullyInitialised'] ) && !empty( $GLOBALS['wgOut'] ) && !defined( 'MEDIAWIKI_INSTALL' ); } /** * Whether to log this exception in the exception debug log. * * @since 1.23 * @return bool */ public function isLoggable() { return true; } /** * Can the extension use the Message class/wfMessage to get i18n-ed messages? * * @return bool */ public function useMessageCache() { global $wgLang; foreach ( $this->getTrace() as $frame ) { if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) { return false; } } return $wgLang instanceof Language; } /** * Run hook to allow extensions to modify the text of the exception * * @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 function runHooks( $name, $args = array() ) { 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( array( $this ), $args ); foreach ( $hooks as $hook ) { if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { // 'function' or array( 'class', hook' ) $result = call_user_func_array( $hook, $callargs ); } else { $result = null; } if ( is_string( $result ) ) { return $result; } } return null; } /** * 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 */ public function msg( $key, $fallback /*[, params...] */ ) { $args = array_slice( func_get_args(), 2 ); if ( $this->useMessageCache() ) { try { return wfMessage( $key, $args )->text(); } catch ( Exception $e ) { } } return wfMsgReplaceArgs( $fallback, $args ); } /** * 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. * * @return string Html to output */ public function getHTML() { global $wgShowExceptionDetails; if ( $wgShowExceptionDetails ) { return '

' . nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $this ) ) ) . '

Backtrace:

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

\n"; } else { $logId = MWExceptionHandler::getLogId( $this ); $type = get_class( $this ); return "
" . '[' . $logId . '] ' . gmdate( 'Y-m-d H:i:s' ) . ": " . $this->msg( "internalerror-fatal-exception", "Fatal exception of type $1", $type, $logId, MWExceptionHandler::getURL( $this ) ) . "
\n" . ""; } } /** * Get the text to display when reporting the error on the command line. * If $wgShowExceptionDetails is true, return a text message with a * backtrace to the error. * * @return string */ public function getText() { global $wgShowExceptionDetails; if ( $wgShowExceptionDetails ) { return MWExceptionHandler::getLogMessage( $this ) . "\nBacktrace:\n" . MWExceptionHandler::getRedactedTraceAsString( $this ) . "\n"; } else { return "Set \$wgShowExceptionDetails = true; " . "in LocalSettings.php to show detailed debugging information.\n"; } } /** * Return the title of the page when reporting this error in a HTTP response. * * @return string */ public function getPageTitle() { return $this->msg( 'internalerror', 'Internal error' ); } /** * Output the exception report using HTML. */ public function reportHTML() { global $wgOut, $wgSitename; if ( $this->useOutputPage() ) { $wgOut->prepareErrorPage( $this->getPageTitle() ); $hookResult = $this->runHooks( get_class( $this ) ); if ( $hookResult ) { $wgOut->addHTML( $hookResult ); } else { $wgOut->addHTML( $this->getHTML() ); } $wgOut->output(); } else { self::header( 'Content-Type: text/html; charset=utf-8' ); echo "\n" . '' . // Mimick OutputPage::setPageTitle behaviour '' . htmlspecialchars( $this->msg( 'pagetitle', "$1 - $wgSitename", $this->getPageTitle() ) ) . '' . '' . "\n"; $hookResult = $this->runHooks( get_class( $this ) . 'Raw' ); if ( $hookResult ) { echo $hookResult; } else { echo $this->getHTML(); } echo "\n"; } } /** * Output a report about the exception and takes care of formatting. * It will be either HTML or plain text based on isCommandLine(). */ public function report() { 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( $this ) ); wfHttpError( 500, 'Internal Server Error', $this->getText() ); } elseif ( self::isCommandLine() ) { MWExceptionHandler::printError( $this->getText() ); } else { self::statusHeader( 500 ); self::header( "Content-Type: $wgMimeType; charset=utf-8" ); $this->reportHTML(); } } /** * Check whether we are in command line mode or not to report the exception * in the correct format. * * @return bool */ public static function isCommandLine() { return !empty( $GLOBALS['wgCommandLineMode'] ); } /** * Send a header, if we haven't already sent them. We shouldn't, * but sometimes we might in a weird case like Export * @param string $header */ private static function header( $header ) { if ( !headers_sent() ) { header( $header ); } } private static function statusHeader( $code ) { if ( !headers_sent() ) { HttpStatus::header( $code ); } } }