MediaWiki  REL1_19
Exception.php
Go to the documentation of this file.
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 }