MediaWiki
REL1_20
|
00001 <?php 00032 class MWException extends Exception { 00033 var $logId; 00034 00040 function useOutputPage() { 00041 return $this->useMessageCache() && 00042 !empty( $GLOBALS['wgFullyInitialised'] ) && 00043 !empty( $GLOBALS['wgOut'] ) && 00044 !empty( $GLOBALS['wgTitle'] ); 00045 } 00046 00052 function useMessageCache() { 00053 global $wgLang; 00054 00055 foreach ( $this->getTrace() as $frame ) { 00056 if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) { 00057 return false; 00058 } 00059 } 00060 00061 return $wgLang instanceof Language; 00062 } 00063 00071 function runHooks( $name, $args = array() ) { 00072 global $wgExceptionHooks; 00073 00074 if ( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) { 00075 return null; // Just silently ignore 00076 } 00077 00078 if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) ) { 00079 return null; 00080 } 00081 00082 $hooks = $wgExceptionHooks[ $name ]; 00083 $callargs = array_merge( array( $this ), $args ); 00084 00085 foreach ( $hooks as $hook ) { 00086 if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { // 'function' or array( 'class', hook' ) 00087 $result = call_user_func_array( $hook, $callargs ); 00088 } else { 00089 $result = null; 00090 } 00091 00092 if ( is_string( $result ) ) { 00093 return $result; 00094 } 00095 } 00096 return null; 00097 } 00098 00108 function msg( $key, $fallback /*[, params...] */ ) { 00109 $args = array_slice( func_get_args(), 2 ); 00110 00111 if ( $this->useMessageCache() ) { 00112 return wfMessage( $key, $args )->plain(); 00113 } else { 00114 return wfMsgReplaceArgs( $fallback, $args ); 00115 } 00116 } 00117 00125 function getHTML() { 00126 global $wgShowExceptionDetails; 00127 00128 if ( $wgShowExceptionDetails ) { 00129 return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) . 00130 '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . 00131 "</p>\n"; 00132 } else { 00133 return 00134 "<div class=\"errorbox\">" . 00135 '[' . $this->getLogId() . '] ' . 00136 gmdate( 'Y-m-d H:i:s' ) . 00137 ": Fatal exception of type " . get_class( $this ) . "</div>\n" . 00138 "<!-- Set \$wgShowExceptionDetails = true; " . 00139 "at the bottom of LocalSettings.php to show detailed " . 00140 "debugging information. -->"; 00141 } 00142 } 00143 00151 function getText() { 00152 global $wgShowExceptionDetails; 00153 00154 if ( $wgShowExceptionDetails ) { 00155 return $this->getMessage() . 00156 "\nBacktrace:\n" . $this->getTraceAsString() . "\n"; 00157 } else { 00158 return "Set \$wgShowExceptionDetails = true; " . 00159 "in LocalSettings.php to show detailed debugging information.\n"; 00160 } 00161 } 00162 00168 function getPageTitle() { 00169 return $this->msg( 'internalerror', "Internal error" ); 00170 } 00171 00179 function getLogId() { 00180 if ( $this->logId === null ) { 00181 $this->logId = wfRandomString( 8 ); 00182 } 00183 return $this->logId; 00184 } 00185 00192 function getLogMessage() { 00193 global $wgRequest; 00194 00195 $id = $this->getLogId(); 00196 $file = $this->getFile(); 00197 $line = $this->getLine(); 00198 $message = $this->getMessage(); 00199 00200 if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) { 00201 $url = $wgRequest->getRequestURL(); 00202 if ( !$url ) { 00203 $url = '[no URL]'; 00204 } 00205 } else { 00206 $url = '[no req]'; 00207 } 00208 00209 return "[$id] $url Exception from line $line of $file: $message"; 00210 } 00211 00215 function reportHTML() { 00216 global $wgOut; 00217 if ( $this->useOutputPage() ) { 00218 $wgOut->prepareErrorPage( $this->getPageTitle() ); 00219 00220 $hookResult = $this->runHooks( get_class( $this ) ); 00221 if ( $hookResult ) { 00222 $wgOut->addHTML( $hookResult ); 00223 } else { 00224 $wgOut->addHTML( $this->getHTML() ); 00225 } 00226 00227 $wgOut->output(); 00228 } else { 00229 header( "Content-Type: text/html; charset=utf-8" ); 00230 echo "<!doctype html>\n" . 00231 '<html><head>' . 00232 '<title>' . htmlspecialchars( $this->getPageTitle() ) . '</title>' . 00233 "</head><body>\n"; 00234 00235 $hookResult = $this->runHooks( get_class( $this ) . "Raw" ); 00236 if ( $hookResult ) { 00237 echo $hookResult; 00238 } else { 00239 echo $this->getHTML(); 00240 } 00241 00242 echo "</body></html>\n"; 00243 } 00244 } 00245 00250 function report() { 00251 global $wgLogExceptionBacktrace; 00252 $log = $this->getLogMessage(); 00253 00254 if ( $log ) { 00255 if ( $wgLogExceptionBacktrace ) { 00256 wfDebugLog( 'exception', $log . "\n" . $this->getTraceAsString() . "\n" ); 00257 } else { 00258 wfDebugLog( 'exception', $log ); 00259 } 00260 } 00261 00262 if ( defined( 'MW_API' ) ) { 00263 // Unhandled API exception, we can't be sure that format printer is alive 00264 header( 'MediaWiki-API-Error: internal_api_error_' . get_class( $this ) ); 00265 wfHttpError(500, 'Internal Server Error', $this->getText() ); 00266 } elseif ( self::isCommandLine() ) { 00267 MWExceptionHandler::printError( $this->getText() ); 00268 } else { 00269 header( "HTTP/1.1 500 MediaWiki exception" ); 00270 header( "Status: 500 MediaWiki exception", true ); 00271 00272 $this->reportHTML(); 00273 } 00274 } 00275 00282 static function isCommandLine() { 00283 return !empty( $GLOBALS['wgCommandLineMode'] ); 00284 } 00285 } 00286 00294 class FatalError extends MWException { 00295 00299 function getHTML() { 00300 return $this->getMessage(); 00301 } 00302 00306 function getText() { 00307 return $this->getMessage(); 00308 } 00309 } 00310 00317 class ErrorPageError extends MWException { 00318 public $title, $msg, $params; 00319 00327 function __construct( $title, $msg, $params = null ) { 00328 $this->title = $title; 00329 $this->msg = $msg; 00330 $this->params = $params; 00331 00332 if( $msg instanceof Message ){ 00333 parent::__construct( $msg ); 00334 } else { 00335 parent::__construct( wfMessage( $msg )->text() ); 00336 } 00337 } 00338 00339 function report() { 00340 global $wgOut; 00341 00342 $wgOut->showErrorPage( $this->title, $this->msg, $this->params ); 00343 $wgOut->output(); 00344 } 00345 } 00346 00355 class BadTitleError extends ErrorPageError { 00360 function __construct( $msg = 'badtitletext', $params = null ) { 00361 parent::__construct( 'badtitle', $msg, $params ); 00362 } 00363 00368 function report() { 00369 global $wgOut; 00370 00371 // bug 33646: a badtitle error page need to return an error code 00372 // to let mobile browser now that it is not a normal page. 00373 $wgOut->setStatusCode( 400 ); 00374 parent::report(); 00375 } 00376 00377 } 00378 00386 class PermissionsError extends ErrorPageError { 00387 public $permission, $errors; 00388 00389 function __construct( $permission, $errors = array() ) { 00390 global $wgLang; 00391 00392 $this->permission = $permission; 00393 00394 if ( !count( $errors ) ) { 00395 $groups = array_map( 00396 array( 'User', 'makeGroupLinkWiki' ), 00397 User::getGroupsWithPermission( $this->permission ) 00398 ); 00399 00400 if ( $groups ) { 00401 $errors[] = array( 'badaccess-groups', $wgLang->commaList( $groups ), count( $groups ) ); 00402 } else { 00403 $errors[] = array( 'badaccess-group0' ); 00404 } 00405 } 00406 00407 $this->errors = $errors; 00408 } 00409 00410 function report() { 00411 global $wgOut; 00412 00413 $wgOut->showPermissionsErrorPage( $this->errors, $this->permission ); 00414 $wgOut->output(); 00415 } 00416 } 00417 00425 class ReadOnlyError extends ErrorPageError { 00426 public function __construct(){ 00427 parent::__construct( 00428 'readonly', 00429 'readonlytext', 00430 wfReadOnlyReason() 00431 ); 00432 } 00433 } 00434 00441 class ThrottledError extends ErrorPageError { 00442 public function __construct(){ 00443 parent::__construct( 00444 'actionthrottled', 00445 'actionthrottledtext' 00446 ); 00447 } 00448 00449 public function report(){ 00450 global $wgOut; 00451 $wgOut->setStatusCode( 503 ); 00452 parent::report(); 00453 } 00454 } 00455 00462 class UserBlockedError extends ErrorPageError { 00463 public function __construct( Block $block ){ 00464 global $wgLang, $wgRequest; 00465 00466 $blocker = $block->getBlocker(); 00467 if ( $blocker instanceof User ) { // local user 00468 $blockerUserpage = $block->getBlocker()->getUserPage(); 00469 $link = "[[{$blockerUserpage->getPrefixedText()}|{$blockerUserpage->getText()}]]"; 00470 } else { // foreign user 00471 $link = $blocker; 00472 } 00473 00474 $reason = $block->mReason; 00475 if( $reason == '' ) { 00476 $reason = wfMessage( 'blockednoreason' )->text(); 00477 } 00478 00479 /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked. 00480 * This could be a username, an IP range, or a single IP. */ 00481 $intended = $block->getTarget(); 00482 00483 parent::__construct( 00484 'blockedtitle', 00485 $block->mAuto ? 'autoblockedtext' : 'blockedtext', 00486 array( 00487 $link, 00488 $reason, 00489 $wgRequest->getIP(), 00490 $block->getByName(), 00491 $block->getId(), 00492 $wgLang->formatExpiry( $block->mExpiry ), 00493 $intended, 00494 $wgLang->timeanddate( wfTimestamp( TS_MW, $block->mTimestamp ), true ) 00495 ) 00496 ); 00497 } 00498 } 00499 00528 class UserNotLoggedIn extends ErrorPageError { 00529 00538 public function __construct( 00539 $reasonMsg = 'exception-nologin-text', 00540 $titleMsg = 'exception-nologin', 00541 $params = null 00542 ) { 00543 parent::__construct( $titleMsg, $reasonMsg, $params ); 00544 } 00545 } 00546 00554 class HttpError extends MWException { 00555 private $httpCode, $header, $content; 00556 00564 public function __construct( $httpCode, $content, $header = null ){ 00565 parent::__construct( $content ); 00566 $this->httpCode = (int)$httpCode; 00567 $this->header = $header; 00568 $this->content = $content; 00569 } 00570 00571 public function report() { 00572 $httpMessage = HttpStatus::getMessage( $this->httpCode ); 00573 00574 header( "Status: {$this->httpCode} {$httpMessage}" ); 00575 header( 'Content-type: text/html; charset=utf-8' ); 00576 00577 if ( $this->header === null ) { 00578 $header = $httpMessage; 00579 } elseif ( $this->header instanceof Message ) { 00580 $header = $this->header->escaped(); 00581 } else { 00582 $header = htmlspecialchars( $this->header ); 00583 } 00584 00585 if ( $this->content instanceof Message ) { 00586 $content = $this->content->escaped(); 00587 } else { 00588 $content = htmlspecialchars( $this->content ); 00589 } 00590 00591 print "<!DOCTYPE html>\n". 00592 "<html><head><title>$header</title></head>\n" . 00593 "<body><h1>$header</h1><p>$content</p></body></html>\n"; 00594 } 00595 } 00596 00601 class MWExceptionHandler { 00605 public static function installHandler() { 00606 set_exception_handler( array( 'MWExceptionHandler', 'handle' ) ); 00607 } 00608 00612 protected static function report( Exception $e ) { 00613 global $wgShowExceptionDetails; 00614 00615 $cmdLine = MWException::isCommandLine(); 00616 00617 if ( $e instanceof MWException ) { 00618 try { 00619 // Try and show the exception prettily, with the normal skin infrastructure 00620 $e->report(); 00621 } catch ( Exception $e2 ) { 00622 // Exception occurred from within exception handler 00623 // Show a simpler error message for the original exception, 00624 // don't try to invoke report() 00625 $message = "MediaWiki internal error.\n\n"; 00626 00627 if ( $wgShowExceptionDetails ) { 00628 $message .= 'Original exception: ' . $e->__toString() . "\n\n" . 00629 'Exception caught inside exception handler: ' . $e2->__toString(); 00630 } else { 00631 $message .= "Exception caught inside exception handler.\n\n" . 00632 "Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " . 00633 "to show detailed debugging information."; 00634 } 00635 00636 $message .= "\n"; 00637 00638 if ( $cmdLine ) { 00639 self::printError( $message ); 00640 } else { 00641 echo nl2br( htmlspecialchars( $message ) ) . "\n"; 00642 } 00643 } 00644 } else { 00645 $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" . 00646 $e->__toString() . "\n"; 00647 00648 if ( $wgShowExceptionDetails ) { 00649 $message .= "\n" . $e->getTraceAsString() . "\n"; 00650 } 00651 00652 if ( $cmdLine ) { 00653 self::printError( $message ); 00654 } else { 00655 echo nl2br( htmlspecialchars( $message ) ) . "\n"; 00656 } 00657 } 00658 } 00659 00666 public static function printError( $message ) { 00667 # NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602). 00668 # Try to produce meaningful output anyway. Using echo may corrupt output to STDOUT though. 00669 if ( defined( 'STDERR' ) ) { 00670 fwrite( STDERR, $message ); 00671 } else { 00672 echo( $message ); 00673 } 00674 } 00675 00687 public static function handle( $e ) { 00688 global $wgFullyInitialised; 00689 00690 self::report( $e ); 00691 00692 // Final cleanup 00693 if ( $wgFullyInitialised ) { 00694 try { 00695 wfLogProfilingData(); // uses $wgRequest, hence the $wgFullyInitialised condition 00696 } catch ( Exception $e ) {} 00697 } 00698 00699 // Exit value should be nonzero for the benefit of shell jobs 00700 exit( 1 ); 00701 } 00702 }