]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/Exception.php
MediaWiki 1.17.1
[autoinstallsdev/mediawiki.git] / includes / Exception.php
1 <?php
2 /**
3  * Exception class and handler
4  *
5  * @file
6  */
7
8 /**
9  * @defgroup Exception Exception
10  */
11
12 /**
13  * MediaWiki exception
14  *
15  * @ingroup Exception
16  */
17 class MWException extends Exception {
18         /**
19          * Should the exception use $wgOut to output the error ?
20          * @return bool
21          */
22         function useOutputPage() {
23                 return $this->useMessageCache() &&
24                         !empty( $GLOBALS['wgFullyInitialised'] ) &&
25                         ( !empty( $GLOBALS['wgArticle'] ) || ( !empty( $GLOBALS['wgOut'] ) && !$GLOBALS['wgOut']->isArticle() ) ) &&
26                         !empty( $GLOBALS['wgTitle'] );
27         }
28
29         /**
30          * Can the extension use wfMsg() to get i18n messages ?
31          * @return bool
32          */
33         function useMessageCache() {
34                 global $wgLang;
35
36                 foreach ( $this->getTrace() as $frame ) {
37                         if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) {
38                                 return false;
39                         }
40                 }
41
42                 return is_object( $wgLang );
43         }
44
45         /**
46          * Run hook to allow extensions to modify the text of the exception
47          *
48          * @param $name String: class name of the exception
49          * @param $args Array: arguments to pass to the callback functions
50          * @return Mixed: string to output or null if any hook has been called
51          */
52         function runHooks( $name, $args = array() ) {
53                 global $wgExceptionHooks;
54
55                 if ( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) {
56                         return; // Just silently ignore
57                 }
58
59                 if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) ) {
60                         return;
61                 }
62
63                 $hooks = $wgExceptionHooks[ $name ];
64                 $callargs = array_merge( array( $this ), $args );
65
66                 foreach ( $hooks as $hook ) {
67                         if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) {    // 'function' or array( 'class', hook' )
68                                 $result = call_user_func_array( $hook, $callargs );
69                         } else {
70                                 $result = null;
71                         }
72
73                         if ( is_string( $result ) )
74                                 return $result;
75                 }
76         }
77
78         /**
79          * Get a message from i18n
80          *
81          * @param $key String: message name
82          * @param $fallback String: default message if the message cache can't be
83          *                  called by the exception
84          * The function also has other parameters that are arguments for the message
85          * @return String message with arguments replaced
86          */
87         function msg( $key, $fallback /*[, params...] */ ) {
88                 $args = array_slice( func_get_args(), 2 );
89
90                 if ( $this->useMessageCache() ) {
91                         return wfMsgReal( $key, $args );
92                 } else {
93                         return wfMsgReplaceArgs( $fallback, $args );
94                 }
95         }
96
97         /**
98          * If $wgShowExceptionDetails is true, return a HTML message with a
99          * backtrace to the error, otherwise show a message to ask to set it to true
100          * to show that information.
101          *
102          * @return String html to output
103          */
104         function getHTML() {
105                 global $wgShowExceptionDetails;
106
107                 if ( $wgShowExceptionDetails ) {
108                         return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) .
109                                 '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
110                                 "</p>\n";
111                 } else {
112                         return "<p>Set <b><tt>\$wgShowExceptionDetails = true;</tt></b> " .
113                                 "at the bottom of LocalSettings.php to show detailed " .
114                                 "debugging information.</p>";
115                 }
116         }
117
118         /**
119          * If $wgShowExceptionDetails is true, return a text message with a
120          * backtrace to the error.
121          */
122         function getText() {
123                 global $wgShowExceptionDetails;
124
125                 if ( $wgShowExceptionDetails ) {
126                         return $this->getMessage() .
127                                 "\nBacktrace:\n" . $this->getTraceAsString() . "\n";
128                 } else {
129                         return "Set \$wgShowExceptionDetails = true; " .
130                                 "in LocalSettings.php to show detailed debugging information.\n";
131                 }
132         }
133
134         /* Return titles of this error page */
135         function getPageTitle() {
136                 if ( $this->useMessageCache() ) {
137                         return wfMsg( 'internalerror' );
138                 } else {
139                         global $wgSitename;
140
141                         return "$wgSitename error";
142                 }
143         }
144
145         /**
146          * Return the requested URL and point to file and line number from which the
147          * exception occured
148          *
149          * @return String
150          */
151         function getLogMessage() {
152                 global $wgRequest;
153
154                 $file = $this->getFile();
155                 $line = $this->getLine();
156                 $message = $this->getMessage();
157
158                 if ( isset( $wgRequest ) ) {
159                         $url = $wgRequest->getRequestURL();
160                         if ( !$url ) {
161                                 $url = '[no URL]';
162                         }
163                 } else {
164                         $url = '[no req]';
165                 }
166
167                 return "$url   Exception from line $line of $file: $message";
168         }
169
170         /** Output the exception report using HTML */
171         function reportHTML() {
172                 global $wgOut;
173
174                 if ( $this->useOutputPage() ) {
175                         $wgOut->setPageTitle( $this->getPageTitle() );
176                         $wgOut->setRobotPolicy( "noindex,nofollow" );
177                         $wgOut->setArticleRelated( false );
178                         $wgOut->enableClientCache( false );
179                         $wgOut->redirect( '' );
180                         $wgOut->clearHTML();
181
182                         $hookResult = $this->runHooks( get_class( $this ) );
183                         if ( $hookResult ) {
184                                 $wgOut->addHTML( $hookResult );
185                         } else {
186                                 $wgOut->addHTML( $this->getHTML() );
187                         }
188
189                         $wgOut->output();
190                 } else {
191                         $hookResult = $this->runHooks( get_class( $this ) . "Raw" );
192                         if ( $hookResult ) {
193                                 die( $hookResult );
194                         }
195
196                         if ( defined( 'MEDIAWIKI_INSTALL' ) || $this->htmlBodyOnly() ) {
197                                 echo $this->getHTML();
198                         } else {
199                                 echo $this->htmlHeader();
200                                 echo $this->getHTML();
201                                 echo $this->htmlFooter();
202                         }
203                 }
204         }
205
206         /**
207          * Output a report about the exception and takes care of formatting.
208          * It will be either HTML or plain text based on isCommandLine().
209          */
210         function report() {
211                 $log = $this->getLogMessage();
212
213                 if ( $log ) {
214                         wfDebugLog( 'exception', $log );
215                 }
216
217                 if ( self::isCommandLine() ) {
218                         wfPrintError( $this->getText() );
219                 } else {
220                         $this->reportHTML();
221                 }
222         }
223
224         /**
225          * Send headers and output the beginning of the html page if not using
226          * $wgOut to output the exception.
227          */
228         function htmlHeader() {
229                 global $wgLogo, $wgOutputEncoding;
230
231                 if ( !headers_sent() ) {
232                         header( 'HTTP/1.0 500 Internal Server Error' );
233                         header( 'Content-type: text/html; charset=' . $wgOutputEncoding );
234                         /* Don't cache error pages!  They cause no end of trouble... */
235                         header( 'Cache-control: none' );
236                         header( 'Pragma: nocache' );
237                 }
238
239                 $logo = htmlspecialchars( $wgLogo, ENT_QUOTES );
240                 $title = htmlspecialchars( $this->getPageTitle() );
241
242                 return "<html>
243                 <head>
244                 <title>$title</title>
245                 </head>
246                 <body>
247                 <h1><img src='$logo' style='float:left;margin-right:1em' alt=''/>$title</h1>
248                 ";
249         }
250
251         /**
252          * print the end of the html page if not using $wgOut.
253          */
254         function htmlFooter() {
255                 return "</body></html>";
256         }
257
258         /**
259          * headers handled by subclass?
260          */
261         function htmlBodyOnly() {
262                 return false;
263         }
264
265         static function isCommandLine() {
266                 return !empty( $GLOBALS['wgCommandLineMode'] ) && !defined( 'MEDIAWIKI_INSTALL' );
267         }
268 }
269
270 /**
271  * Exception class which takes an HTML error message, and does not
272  * produce a backtrace. Replacement for OutputPage::fatalError().
273  * @ingroup Exception
274  */
275 class FatalError extends MWException {
276         function getHTML() {
277                 return $this->getMessage();
278         }
279
280         function getText() {
281                 return $this->getMessage();
282         }
283 }
284
285 /**
286  * @ingroup Exception
287  */
288 class ErrorPageError extends MWException {
289         public $title, $msg;
290
291         /**
292          * Note: these arguments are keys into wfMsg(), not text!
293          */
294         function __construct( $title, $msg ) {
295                 $this->title = $title;
296                 $this->msg = $msg;
297                 parent::__construct( wfMsg( $msg ) );
298         }
299
300         function report() {
301                 global $wgOut;
302
303                 $wgOut->showErrorPage( $this->title, $this->msg );
304                 $wgOut->output();
305         }
306 }
307
308 /**
309  * Install an exception handler for MediaWiki exception types.
310  */
311 function wfInstallExceptionHandler() {
312         set_exception_handler( 'wfExceptionHandler' );
313 }
314
315 /**
316  * Report an exception to the user
317  */
318 function wfReportException( Exception $e ) {
319         global $wgShowExceptionDetails;
320
321         $cmdLine = MWException::isCommandLine();
322
323         if ( $e instanceof MWException ) {
324                 try {
325                         $e->report();
326                 } catch ( Exception $e2 ) {
327                         // Exception occurred from within exception handler
328                         // Show a simpler error message for the original exception,
329                         // don't try to invoke report()
330                         $message = "MediaWiki internal error.\n\n";
331
332                         if ( $wgShowExceptionDetails ) {
333                                 $message .= 'Original exception: ' . $e->__toString() . "\n\n" .
334                                         'Exception caught inside exception handler: ' . $e2->__toString();
335                         } else {
336                                 $message .= "Exception caught inside exception handler.\n\n" .
337                                         "Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " .
338                                         "to show detailed debugging information.";
339                         }
340
341                         $message .= "\n";
342
343                         if ( $cmdLine ) {
344                                 wfPrintError( $message );
345                         } else {
346                                 echo nl2br( htmlspecialchars( $message ) ) . "\n";
347                         }
348                 }
349         } else {
350                 $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" .
351                         $e->__toString() . "\n";
352
353                 if ( $wgShowExceptionDetails ) {
354                         $message .= "\n" . $e->getTraceAsString() . "\n";
355                 }
356
357                 if ( $cmdLine ) {
358                         wfPrintError( $message );
359                 } else {
360                         echo nl2br( htmlspecialchars( $message ) ) . "\n";
361                 }
362         }
363 }
364
365 /**
366  * Print a message, if possible to STDERR.
367  * Use this in command line mode only (see isCommandLine)
368  */
369 function wfPrintError( $message ) {
370         # NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602).
371         #      Try to produce meaningful output anyway. Using echo may corrupt output to STDOUT though.
372         if ( defined( 'STDERR' ) ) {
373                 fwrite( STDERR, $message );
374         } else {
375                 echo( $message );
376         }
377 }
378
379 /**
380  * Exception handler which simulates the appropriate catch() handling:
381  *
382  *   try {
383  *       ...
384  *   } catch ( MWException $e ) {
385  *       $e->report();
386  *   } catch ( Exception $e ) {
387  *       echo $e->__toString();
388  *   }
389  */
390 function wfExceptionHandler( $e ) {
391         global $wgFullyInitialised;
392
393         wfReportException( $e );
394
395         // Final cleanup
396         if ( $wgFullyInitialised ) {
397                 try {
398                         wfLogProfilingData(); // uses $wgRequest, hence the $wgFullyInitialised condition
399                 } catch ( Exception $e ) {}
400         }
401
402         // Exit value should be nonzero for the benefit of shell jobs
403         exit( 1 );
404 }