MediaWiki
REL1_19
|
00001 <?php 00017 class MWException extends Exception { 00022 function useOutputPage() { 00023 return $this->useMessageCache() && 00024 !empty( $GLOBALS['wgFullyInitialised'] ) && 00025 !empty( $GLOBALS['wgOut'] ) && 00026 !empty( $GLOBALS['wgTitle'] ); 00027 } 00028 00033 function useMessageCache() { 00034 global $wgLang; 00035 00036 foreach ( $this->getTrace() as $frame ) { 00037 if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) { 00038 return false; 00039 } 00040 } 00041 00042 return $wgLang instanceof Language; 00043 } 00044 00052 function runHooks( $name, $args = array() ) { 00053 global $wgExceptionHooks; 00054 00055 if ( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) { 00056 return; // Just silently ignore 00057 } 00058 00059 if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) ) { 00060 return; 00061 } 00062 00063 $hooks = $wgExceptionHooks[ $name ]; 00064 $callargs = array_merge( array( $this ), $args ); 00065 00066 foreach ( $hooks as $hook ) { 00067 if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { // 'function' or array( 'class', hook' ) 00068 $result = call_user_func_array( $hook, $callargs ); 00069 } else { 00070 $result = null; 00071 } 00072 00073 if ( is_string( $result ) ) { 00074 return $result; 00075 } 00076 } 00077 } 00078 00088 function msg( $key, $fallback /*[, params...] */ ) { 00089 $args = array_slice( func_get_args(), 2 ); 00090 00091 if ( $this->useMessageCache() ) { 00092 return wfMsgNoTrans( $key, $args ); 00093 } else { 00094 return wfMsgReplaceArgs( $fallback, $args ); 00095 } 00096 } 00097 00105 function getHTML() { 00106 global $wgShowExceptionDetails; 00107 00108 if ( $wgShowExceptionDetails ) { 00109 return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) . 00110 '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . 00111 "</p>\n"; 00112 } else { 00113 return "<p>Set <b><tt>\$wgShowExceptionDetails = true;</tt></b> " . 00114 "at the bottom of LocalSettings.php to show detailed " . 00115 "debugging information.</p>"; 00116 } 00117 } 00118 00124 function getText() { 00125 global $wgShowExceptionDetails; 00126 00127 if ( $wgShowExceptionDetails ) { 00128 return $this->getMessage() . 00129 "\nBacktrace:\n" . $this->getTraceAsString() . "\n"; 00130 } else { 00131 return "Set \$wgShowExceptionDetails = true; " . 00132 "in LocalSettings.php to show detailed debugging information.\n"; 00133 } 00134 } 00135 00140 function getPageTitle() { 00141 return $this->msg( 'internalerror', "Internal error" ); 00142 } 00143 00150 function getLogMessage() { 00151 global $wgRequest; 00152 00153 $file = $this->getFile(); 00154 $line = $this->getLine(); 00155 $message = $this->getMessage(); 00156 00157 if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) { 00158 $url = $wgRequest->getRequestURL(); 00159 if ( !$url ) { 00160 $url = '[no URL]'; 00161 } 00162 } else { 00163 $url = '[no req]'; 00164 } 00165 00166 return "$url Exception from line $line of $file: $message"; 00167 } 00168 00170 function reportHTML() { 00171 global $wgOut; 00172 if ( $this->useOutputPage() ) { 00173 $wgOut->prepareErrorPage( $this->getPageTitle() ); 00174 00175 $hookResult = $this->runHooks( get_class( $this ) ); 00176 if ( $hookResult ) { 00177 $wgOut->addHTML( $hookResult ); 00178 } else { 00179 $wgOut->addHTML( $this->getHTML() ); 00180 } 00181 00182 $wgOut->output(); 00183 } else { 00184 header( "Content-Type: text/html; charset=utf-8" ); 00185 $hookResult = $this->runHooks( get_class( $this ) . "Raw" ); 00186 if ( $hookResult ) { 00187 die( $hookResult ); 00188 } 00189 00190 echo $this->getHTML(); 00191 die(1); 00192 } 00193 } 00194 00199 function report() { 00200 $log = $this->getLogMessage(); 00201 00202 if ( $log ) { 00203 wfDebugLog( 'exception', $log ); 00204 } 00205 00206 if ( self::isCommandLine() ) { 00207 MWExceptionHandler::printError( $this->getText() ); 00208 } else { 00209 $this->reportHTML(); 00210 } 00211 } 00212 00217 static function isCommandLine() { 00218 return !empty( $GLOBALS['wgCommandLineMode'] ); 00219 } 00220 } 00221 00227 class FatalError extends MWException { 00228 00232 function getHTML() { 00233 return $this->getMessage(); 00234 } 00235 00239 function getText() { 00240 return $this->getMessage(); 00241 } 00242 } 00243 00248 class ErrorPageError extends MWException { 00249 public $title, $msg, $params; 00250 00254 function __construct( $title, $msg, $params = null ) { 00255 $this->title = $title; 00256 $this->msg = $msg; 00257 $this->params = $params; 00258 00259 if( $msg instanceof Message ){ 00260 parent::__construct( $msg ); 00261 } else { 00262 parent::__construct( wfMsg( $msg ) ); 00263 } 00264 } 00265 00266 function report() { 00267 global $wgOut; 00268 00269 00270 $wgOut->showErrorPage( $this->title, $this->msg, $this->params ); 00271 $wgOut->output(); 00272 } 00273 } 00274 00280 class BadTitleError extends ErrorPageError { 00281 00286 function __construct( $msg = 'badtitletext', $params = null ) { 00287 parent::__construct( 'badtitle', $msg, $params ); 00288 } 00289 00294 function report() { 00295 global $wgOut; 00296 00297 // bug 33646: a badtitle error page need to return an error code 00298 // to let mobile browser now that it is not a normal page. 00299 $wgOut->setStatusCode( 400 ); 00300 parent::report(); 00301 } 00302 00303 } 00304 00310 class PermissionsError extends ErrorPageError { 00311 public $permission, $errors; 00312 00313 function __construct( $permission, $errors = array() ) { 00314 global $wgLang; 00315 00316 $this->permission = $permission; 00317 00318 if ( !count( $errors ) ) { 00319 $groups = array_map( 00320 array( 'User', 'makeGroupLinkWiki' ), 00321 User::getGroupsWithPermission( $this->permission ) 00322 ); 00323 00324 if ( $groups ) { 00325 $errors[] = array( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) ); 00326 } else { 00327 $errors[] = array( 'badaccess-group0' ); 00328 } 00329 } 00330 00331 $this->errors = $errors; 00332 } 00333 00334 function report() { 00335 global $wgOut; 00336 00337 $wgOut->showPermissionsErrorPage( $this->errors, $this->permission ); 00338 $wgOut->output(); 00339 } 00340 } 00341 00347 class ReadOnlyError extends ErrorPageError { 00348 public function __construct(){ 00349 parent::__construct( 00350 'readonly', 00351 'readonlytext', 00352 wfReadOnlyReason() 00353 ); 00354 } 00355 } 00356 00361 class ThrottledError extends ErrorPageError { 00362 public function __construct(){ 00363 parent::__construct( 00364 'actionthrottled', 00365 'actionthrottledtext' 00366 ); 00367 } 00368 00369 public function report(){ 00370 global $wgOut; 00371 $wgOut->setStatusCode( 503 ); 00372 return parent::report(); 00373 } 00374 } 00375 00380 class UserBlockedError extends ErrorPageError { 00381 public function __construct( Block $block ){ 00382 global $wgLang, $wgRequest; 00383 00384 $blocker = $block->getBlocker(); 00385 if ( $blocker instanceof User ) { // local user 00386 $blockerUserpage = $block->getBlocker()->getUserPage(); 00387 $link = "[[{$blockerUserpage->getPrefixedText()}|{$blockerUserpage->getText()}]]"; 00388 } else { // foreign user 00389 $link = $blocker; 00390 } 00391 00392 $reason = $block->mReason; 00393 if( $reason == '' ) { 00394 $reason = wfMsg( 'blockednoreason' ); 00395 } 00396 00397 /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked. 00398 * This could be a username, an IP range, or a single IP. */ 00399 $intended = $block->getTarget(); 00400 00401 parent::__construct( 00402 'blockedtitle', 00403 $block->mAuto ? 'autoblockedtext' : 'blockedtext', 00404 array( 00405 $link, 00406 $reason, 00407 $wgRequest->getIP(), 00408 $block->getByName(), 00409 $block->getId(), 00410 $wgLang->formatExpiry( $block->mExpiry ), 00411 $intended, 00412 $wgLang->timeanddate( wfTimestamp( TS_MW, $block->mTimestamp ), true ) 00413 ) 00414 ); 00415 } 00416 } 00417 00424 class HttpError extends MWException { 00425 private $httpCode, $header, $content; 00426 00434 public function __construct( $httpCode, $content, $header = null ){ 00435 parent::__construct( $content ); 00436 $this->httpCode = (int)$httpCode; 00437 $this->header = $header; 00438 $this->content = $content; 00439 } 00440 00441 public function reportHTML() { 00442 $httpMessage = HttpStatus::getMessage( $this->httpCode ); 00443 00444 header( "Status: {$this->httpCode} {$httpMessage}" ); 00445 header( 'Content-type: text/html; charset=utf-8' ); 00446 00447 if ( $this->header === null ) { 00448 $header = $httpMessage; 00449 } elseif ( $this->header instanceof Message ) { 00450 $header = $this->header->escaped(); 00451 } else { 00452 $header = htmlspecialchars( $this->header ); 00453 } 00454 00455 if ( $this->content instanceof Message ) { 00456 $content = $this->content->escaped(); 00457 } else { 00458 $content = htmlspecialchars( $this->content ); 00459 } 00460 00461 print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n". 00462 "<html><head><title>$header</title></head>\n" . 00463 "<body><h1>$header</h1><p>$content</p></body></html>\n"; 00464 } 00465 } 00466 00471 class MWExceptionHandler { 00475 public static function installHandler() { 00476 set_exception_handler( array( 'MWExceptionHandler', 'handle' ) ); 00477 } 00478 00482 protected static function report( Exception $e ) { 00483 global $wgShowExceptionDetails; 00484 00485 $cmdLine = MWException::isCommandLine(); 00486 00487 if ( $e instanceof MWException ) { 00488 try { 00489 // Try and show the exception prettily, with the normal skin infrastructure 00490 $e->report(); 00491 } catch ( Exception $e2 ) { 00492 // Exception occurred from within exception handler 00493 // Show a simpler error message for the original exception, 00494 // don't try to invoke report() 00495 $message = "MediaWiki internal error.\n\n"; 00496 00497 if ( $wgShowExceptionDetails ) { 00498 $message .= 'Original exception: ' . $e->__toString() . "\n\n" . 00499 'Exception caught inside exception handler: ' . $e2->__toString(); 00500 } else { 00501 $message .= "Exception caught inside exception handler.\n\n" . 00502 "Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " . 00503 "to show detailed debugging information."; 00504 } 00505 00506 $message .= "\n"; 00507 00508 if ( $cmdLine ) { 00509 self::printError( $message ); 00510 } else { 00511 self::escapeEchoAndDie( $message ); 00512 } 00513 } 00514 } else { 00515 $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" . 00516 $e->__toString() . "\n"; 00517 00518 if ( $wgShowExceptionDetails ) { 00519 $message .= "\n" . $e->getTraceAsString() . "\n"; 00520 } 00521 00522 if ( $cmdLine ) { 00523 self::printError( $message ); 00524 } else { 00525 self::escapeEchoAndDie( $message ); 00526 } 00527 } 00528 } 00529 00535 public static function printError( $message ) { 00536 # NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602). 00537 # Try to produce meaningful output anyway. Using echo may corrupt output to STDOUT though. 00538 if ( defined( 'STDERR' ) ) { 00539 fwrite( STDERR, $message ); 00540 } else { 00541 echo( $message ); 00542 } 00543 } 00544 00550 private static function escapeEchoAndDie( $message ) { 00551 echo nl2br( htmlspecialchars( $message ) ) . "\n"; 00552 die(1); 00553 } 00554 00566 public static function handle( $e ) { 00567 global $wgFullyInitialised; 00568 00569 self::report( $e ); 00570 00571 // Final cleanup 00572 if ( $wgFullyInitialised ) { 00573 try { 00574 wfLogProfilingData(); // uses $wgRequest, hence the $wgFullyInitialised condition 00575 } catch ( Exception $e ) {} 00576 } 00577 00578 // Exit value should be nonzero for the benefit of shell jobs 00579 exit( 1 ); 00580 } 00581 }