These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * This program is free software; you can redistribute it and/or modify |
||
4 | * it under the terms of the GNU General Public License as published by |
||
5 | * the Free Software Foundation; either version 2 of the License, or |
||
6 | * (at your option) any later version. |
||
7 | * |
||
8 | * This program is distributed in the hope that it will be useful, |
||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
11 | * GNU General Public License for more details. |
||
12 | * |
||
13 | * You should have received a copy of the GNU General Public License along |
||
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
16 | * http://www.gnu.org/copyleft/gpl.html |
||
17 | * |
||
18 | * @file |
||
19 | */ |
||
20 | |||
21 | /** |
||
22 | * MediaWiki exception |
||
23 | * |
||
24 | * @ingroup Exception |
||
25 | */ |
||
26 | class MWException extends Exception { |
||
27 | /** |
||
28 | * Should the exception use $wgOut to output the error? |
||
29 | * |
||
30 | * @return bool |
||
31 | */ |
||
32 | public function useOutputPage() { |
||
0 ignored issues
–
show
|
|||
33 | return $this->useMessageCache() && |
||
34 | !empty( $GLOBALS['wgFullyInitialised'] ) && |
||
35 | !empty( $GLOBALS['wgOut'] ) && |
||
36 | !defined( 'MEDIAWIKI_INSTALL' ); |
||
37 | } |
||
38 | |||
39 | /** |
||
40 | * Whether to log this exception in the exception debug log. |
||
41 | * |
||
42 | * @since 1.23 |
||
43 | * @return bool |
||
44 | */ |
||
45 | public function isLoggable() { |
||
46 | return true; |
||
47 | } |
||
48 | |||
49 | /** |
||
50 | * Can the extension use the Message class/wfMessage to get i18n-ed messages? |
||
51 | * |
||
52 | * @return bool |
||
53 | */ |
||
54 | public function useMessageCache() { |
||
55 | global $wgLang; |
||
56 | |||
57 | View Code Duplication | foreach ( $this->getTrace() as $frame ) { |
|
58 | if ( isset( $frame['class'] ) && $frame['class'] === 'LocalisationCache' ) { |
||
59 | return false; |
||
60 | } |
||
61 | } |
||
62 | |||
63 | return $wgLang instanceof Language; |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * Run hook to allow extensions to modify the text of the exception |
||
68 | * |
||
69 | * @param string $name Class name of the exception |
||
70 | * @param array $args Arguments to pass to the callback functions |
||
71 | * @return string|null String to output or null if any hook has been called |
||
72 | */ |
||
73 | public function runHooks( $name, $args = [] ) { |
||
74 | return MWExceptionRenderer::runHooks( $this, $name, $args ); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Get a message from i18n |
||
79 | * |
||
80 | * @param string $key Message name |
||
81 | * @param string $fallback Default message if the message cache can't be |
||
82 | * called by the exception |
||
83 | * The function also has other parameters that are arguments for the message |
||
84 | * @return string Message with arguments replaced |
||
85 | */ |
||
86 | View Code Duplication | public function msg( $key, $fallback /*[, params...] */ ) { |
|
87 | $args = array_slice( func_get_args(), 2 ); |
||
88 | |||
89 | if ( $this->useMessageCache() ) { |
||
90 | try { |
||
91 | return wfMessage( $key, $args )->text(); |
||
92 | } catch ( Exception $e ) { |
||
93 | } |
||
94 | } |
||
95 | return wfMsgReplaceArgs( $fallback, $args ); |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * If $wgShowExceptionDetails is true, return a HTML message with a |
||
100 | * backtrace to the error, otherwise show a message to ask to set it to true |
||
101 | * to show that information. |
||
102 | * |
||
103 | * @return string Html to output |
||
104 | */ |
||
105 | public function getHTML() { |
||
106 | global $wgShowExceptionDetails; |
||
107 | |||
108 | if ( $wgShowExceptionDetails ) { |
||
109 | return '<p>' . nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $this ) ) ) . |
||
110 | '</p><p>Backtrace:</p><p>' . |
||
111 | nl2br( htmlspecialchars( MWExceptionHandler::getRedactedTraceAsString( $this ) ) ) . |
||
112 | "</p>\n"; |
||
113 | } else { |
||
114 | $logId = WebRequest::getRequestId(); |
||
115 | $type = get_class( $this ); |
||
116 | return "<div class=\"errorbox\">" . |
||
117 | '[' . $logId . '] ' . |
||
118 | gmdate( 'Y-m-d H:i:s' ) . ": " . |
||
119 | $this->msg( "internalerror-fatal-exception", |
||
120 | "Fatal exception of type $1", |
||
121 | $type, |
||
122 | $logId, |
||
123 | MWExceptionHandler::getURL( $this ) |
||
124 | ) . "</div>\n" . |
||
125 | "<!-- Set \$wgShowExceptionDetails = true; " . |
||
126 | "at the bottom of LocalSettings.php to show detailed " . |
||
127 | "debugging information. -->"; |
||
128 | } |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Get the text to display when reporting the error on the command line. |
||
133 | * If $wgShowExceptionDetails is true, return a text message with a |
||
134 | * backtrace to the error. |
||
135 | * |
||
136 | * @return string |
||
137 | */ |
||
138 | View Code Duplication | public function getText() { |
|
139 | global $wgShowExceptionDetails; |
||
140 | |||
141 | if ( $wgShowExceptionDetails ) { |
||
142 | return MWExceptionHandler::getLogMessage( $this ) . |
||
143 | "\nBacktrace:\n" . MWExceptionHandler::getRedactedTraceAsString( $this ) . "\n"; |
||
144 | } else { |
||
145 | return "Set \$wgShowExceptionDetails = true; " . |
||
146 | "in LocalSettings.php to show detailed debugging information.\n"; |
||
147 | } |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Return the title of the page when reporting this error in a HTTP response. |
||
152 | * |
||
153 | * @return string |
||
154 | */ |
||
155 | public function getPageTitle() { |
||
156 | return $this->msg( 'internalerror', 'Internal error' ); |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * Output the exception report using HTML. |
||
161 | */ |
||
162 | public function reportHTML() { |
||
163 | global $wgOut, $wgSitename; |
||
164 | if ( $this->useOutputPage() ) { |
||
165 | $wgOut->prepareErrorPage( $this->getPageTitle() ); |
||
166 | |||
167 | $hookResult = $this->runHooks( get_class( $this ) ); |
||
168 | if ( $hookResult ) { |
||
0 ignored issues
–
show
The expression
$hookResult of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
Loading history...
|
|||
169 | $wgOut->addHTML( $hookResult ); |
||
170 | } else { |
||
171 | $wgOut->addHTML( $this->getHTML() ); |
||
172 | } |
||
173 | |||
174 | $wgOut->output(); |
||
175 | } else { |
||
176 | self::header( 'Content-Type: text/html; charset=utf-8' ); |
||
177 | echo "<!DOCTYPE html>\n" . |
||
178 | '<html><head>' . |
||
179 | // Mimick OutputPage::setPageTitle behaviour |
||
180 | '<title>' . |
||
181 | htmlspecialchars( $this->msg( 'pagetitle', "$1 - $wgSitename", $this->getPageTitle() ) ) . |
||
182 | '</title>' . |
||
183 | '<style>body { font-family: sans-serif; margin: 0; padding: 0.5em 2em; }</style>' . |
||
184 | "</head><body>\n"; |
||
185 | |||
186 | $hookResult = $this->runHooks( get_class( $this ) . 'Raw' ); |
||
187 | if ( $hookResult ) { |
||
0 ignored issues
–
show
The expression
$hookResult of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
Loading history...
|
|||
188 | echo $hookResult; |
||
189 | } else { |
||
190 | echo $this->getHTML(); |
||
191 | } |
||
192 | |||
193 | echo "</body></html>\n"; |
||
194 | } |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Output a report about the exception and takes care of formatting. |
||
199 | * It will be either HTML or plain text based on isCommandLine(). |
||
200 | */ |
||
201 | public function report() { |
||
202 | global $wgMimeType; |
||
203 | |||
204 | if ( defined( 'MW_API' ) ) { |
||
205 | // Unhandled API exception, we can't be sure that format printer is alive |
||
206 | self::header( 'MediaWiki-API-Error: internal_api_error_' . get_class( $this ) ); |
||
207 | wfHttpError( 500, 'Internal Server Error', $this->getText() ); |
||
208 | } elseif ( self::isCommandLine() ) { |
||
209 | $message = $this->getText(); |
||
210 | // T17602: STDERR may not be available |
||
211 | if ( defined( 'STDERR' ) ) { |
||
212 | fwrite( STDERR, $message ); |
||
213 | } else { |
||
214 | echo $message; |
||
215 | } |
||
216 | } else { |
||
217 | self::statusHeader( 500 ); |
||
218 | self::header( "Content-Type: $wgMimeType; charset=utf-8" ); |
||
219 | |||
220 | $this->reportHTML(); |
||
221 | } |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * Check whether we are in command line mode or not to report the exception |
||
226 | * in the correct format. |
||
227 | * |
||
228 | * @return bool |
||
229 | */ |
||
230 | public static function isCommandLine() { |
||
0 ignored issues
–
show
isCommandLine uses the super-global variable $GLOBALS which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
Loading history...
|
|||
231 | return !empty( $GLOBALS['wgCommandLineMode'] ); |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * Send a header, if we haven't already sent them. We shouldn't, |
||
236 | * but sometimes we might in a weird case like Export |
||
237 | * @param string $header |
||
238 | */ |
||
239 | private static function header( $header ) { |
||
240 | if ( !headers_sent() ) { |
||
241 | header( $header ); |
||
242 | } |
||
243 | } |
||
244 | private static function statusHeader( $code ) { |
||
245 | if ( !headers_sent() ) { |
||
246 | HttpStatus::header( $code ); |
||
247 | } |
||
248 | } |
||
249 | } |
||
250 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: