This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Contains classes for formatting log entries |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 2 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along |
||
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | * http://www.gnu.org/copyleft/gpl.html |
||
19 | * |
||
20 | * @file |
||
21 | * @author Niklas Laxström |
||
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later |
||
23 | * @since 1.19 |
||
24 | */ |
||
25 | |||
26 | /** |
||
27 | * Implements the default log formatting. |
||
28 | * |
||
29 | * Can be overridden by subclassing and setting: |
||
30 | * |
||
31 | * $wgLogActionsHandlers['type/subtype'] = 'class'; or |
||
32 | * $wgLogActionsHandlers['type/*'] = 'class'; |
||
33 | * |
||
34 | * @since 1.19 |
||
35 | */ |
||
36 | class LogFormatter { |
||
37 | // Audience options for viewing usernames, comments, and actions |
||
38 | const FOR_PUBLIC = 1; |
||
39 | const FOR_THIS_USER = 2; |
||
40 | |||
41 | // Static-> |
||
42 | |||
43 | /** |
||
44 | * Constructs a new formatter suitable for given entry. |
||
45 | * @param LogEntry $entry |
||
46 | * @return LogFormatter |
||
47 | */ |
||
48 | public static function newFromEntry( LogEntry $entry ) { |
||
49 | global $wgLogActionsHandlers; |
||
50 | $fulltype = $entry->getFullType(); |
||
51 | $wildcard = $entry->getType() . '/*'; |
||
52 | $handler = ''; |
||
53 | |||
54 | if ( isset( $wgLogActionsHandlers[$fulltype] ) ) { |
||
55 | $handler = $wgLogActionsHandlers[$fulltype]; |
||
56 | } elseif ( isset( $wgLogActionsHandlers[$wildcard] ) ) { |
||
57 | $handler = $wgLogActionsHandlers[$wildcard]; |
||
58 | } |
||
59 | |||
60 | if ( $handler !== '' && is_string( $handler ) && class_exists( $handler ) ) { |
||
61 | return new $handler( $entry ); |
||
62 | } |
||
63 | |||
64 | return new LegacyLogFormatter( $entry ); |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * Handy shortcut for constructing a formatter directly from |
||
69 | * database row. |
||
70 | * @param stdClass|array $row |
||
71 | * @see DatabaseLogEntry::getSelectQueryData |
||
72 | * @return LogFormatter |
||
73 | */ |
||
74 | public static function newFromRow( $row ) { |
||
75 | return self::newFromEntry( DatabaseLogEntry::newFromRow( $row ) ); |
||
76 | } |
||
77 | |||
78 | // Nonstatic-> |
||
79 | |||
80 | /** @var LogEntryBase */ |
||
81 | protected $entry; |
||
82 | |||
83 | /** @var int Constant for handling log_deleted */ |
||
84 | protected $audience = self::FOR_PUBLIC; |
||
85 | |||
86 | /** @var IContextSource Context for logging */ |
||
87 | public $context; |
||
88 | |||
89 | /** @var bool Whether to output user tool links */ |
||
90 | protected $linkFlood = false; |
||
91 | |||
92 | /** |
||
93 | * Set to true if we are constructing a message text that is going to |
||
94 | * be included in page history or send to IRC feed. Links are replaced |
||
95 | * with plaintext or with [[pagename]] kind of syntax, that is parsed |
||
96 | * by page histories and IRC feeds. |
||
97 | * @var string |
||
98 | */ |
||
99 | protected $plaintext = false; |
||
100 | |||
101 | /** @var string */ |
||
102 | protected $irctext = false; |
||
103 | |||
104 | protected function __construct( LogEntry $entry ) { |
||
105 | $this->entry = $entry; |
||
0 ignored issues
–
show
|
|||
106 | $this->context = RequestContext::getMain(); |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Replace the default context |
||
111 | * @param IContextSource $context |
||
112 | */ |
||
113 | public function setContext( IContextSource $context ) { |
||
114 | $this->context = $context; |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Set the visibility restrictions for displaying content. |
||
119 | * If set to public, and an item is deleted, then it will be replaced |
||
120 | * with a placeholder even if the context user is allowed to view it. |
||
121 | * @param int $audience Const self::FOR_THIS_USER or self::FOR_PUBLIC |
||
122 | */ |
||
123 | public function setAudience( $audience ) { |
||
124 | $this->audience = ( $audience == self::FOR_THIS_USER ) |
||
125 | ? self::FOR_THIS_USER |
||
126 | : self::FOR_PUBLIC; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Check if a log item can be displayed |
||
131 | * @param int $field LogPage::DELETED_* constant |
||
132 | * @return bool |
||
133 | */ |
||
134 | protected function canView( $field ) { |
||
135 | if ( $this->audience == self::FOR_THIS_USER ) { |
||
136 | return LogEventsList::userCanBitfield( |
||
137 | $this->entry->getDeleted(), $field, $this->context->getUser() ); |
||
138 | } else { |
||
139 | return !$this->entry->isDeleted( $field ); |
||
140 | } |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * If set to true, will produce user tool links after |
||
145 | * the user name. This should be replaced with generic |
||
146 | * CSS/JS solution. |
||
147 | * @param bool $value |
||
148 | */ |
||
149 | public function setShowUserToolLinks( $value ) { |
||
150 | $this->linkFlood = $value; |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * Ugly hack to produce plaintext version of the message. |
||
155 | * Usually you also want to set extraneous request context |
||
156 | * to avoid formatting for any particular user. |
||
157 | * @see getActionText() |
||
158 | * @return string Plain text |
||
159 | */ |
||
160 | public function getPlainActionText() { |
||
161 | $this->plaintext = true; |
||
0 ignored issues
–
show
The property
$plaintext was declared of type string , but true is of type boolean . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
162 | $text = $this->getActionText(); |
||
163 | $this->plaintext = false; |
||
0 ignored issues
–
show
The property
$plaintext was declared of type string , but false is of type false . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
164 | |||
165 | return $text; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Even uglier hack to maintain backwards compatibilty with IRC bots |
||
170 | * (bug 34508). |
||
171 | * @see getActionText() |
||
172 | * @return string Text |
||
173 | */ |
||
174 | public function getIRCActionComment() { |
||
175 | $actionComment = $this->getIRCActionText(); |
||
176 | $comment = $this->entry->getComment(); |
||
177 | |||
178 | if ( $comment != '' ) { |
||
179 | if ( $actionComment == '' ) { |
||
180 | $actionComment = $comment; |
||
181 | } else { |
||
182 | $actionComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $comment; |
||
183 | } |
||
184 | } |
||
185 | |||
186 | return $actionComment; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Even uglier hack to maintain backwards compatibilty with IRC bots |
||
191 | * (bug 34508). |
||
192 | * @see getActionText() |
||
193 | * @return string Text |
||
194 | */ |
||
195 | public function getIRCActionText() { |
||
196 | global $wgContLang; |
||
197 | |||
198 | $this->plaintext = true; |
||
0 ignored issues
–
show
The property
$plaintext was declared of type string , but true is of type boolean . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
199 | $this->irctext = true; |
||
0 ignored issues
–
show
The property
$irctext was declared of type string , but true is of type boolean . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
200 | |||
201 | $entry = $this->entry; |
||
202 | $parameters = $entry->getParameters(); |
||
203 | // @see LogPage::actionText() |
||
204 | // Text of title the action is aimed at. |
||
205 | $target = $entry->getTarget()->getPrefixedText(); |
||
206 | $text = null; |
||
207 | switch ( $entry->getType() ) { |
||
208 | case 'move': |
||
209 | switch ( $entry->getSubtype() ) { |
||
210 | case 'move': |
||
211 | $movesource = $parameters['4::target']; |
||
212 | $text = wfMessage( '1movedto2' ) |
||
213 | ->rawParams( $target, $movesource )->inContentLanguage()->escaped(); |
||
214 | break; |
||
215 | case 'move_redir': |
||
216 | $movesource = $parameters['4::target']; |
||
217 | $text = wfMessage( '1movedto2_redir' ) |
||
218 | ->rawParams( $target, $movesource )->inContentLanguage()->escaped(); |
||
219 | break; |
||
220 | case 'move-noredirect': |
||
221 | break; |
||
222 | case 'move_redir-noredirect': |
||
223 | break; |
||
224 | } |
||
225 | break; |
||
226 | |||
227 | View Code Duplication | case 'delete': |
|
228 | switch ( $entry->getSubtype() ) { |
||
229 | case 'delete': |
||
230 | $text = wfMessage( 'deletedarticle' ) |
||
231 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
232 | break; |
||
233 | case 'restore': |
||
234 | $text = wfMessage( 'undeletedarticle' ) |
||
235 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
236 | break; |
||
237 | // @codingStandardsIgnoreStart Long line |
||
238 | //case 'revision': // Revision deletion |
||
239 | //case 'event': // Log deletion |
||
240 | // see https://github.com/wikimedia/mediawiki/commit/a9c243b7b5289dad204278dbe7ed571fd914e395 |
||
241 | //default: |
||
242 | // @codingStandardsIgnoreEnd |
||
243 | } |
||
244 | break; |
||
245 | |||
246 | case 'patrol': |
||
247 | // @codingStandardsIgnoreStart Long line |
||
248 | // https://github.com/wikimedia/mediawiki/commit/1a05f8faf78675dc85984f27f355b8825b43efff |
||
249 | // @codingStandardsIgnoreEnd |
||
250 | // Create a diff link to the patrolled revision |
||
251 | if ( $entry->getSubtype() === 'patrol' ) { |
||
252 | $diffLink = htmlspecialchars( |
||
253 | wfMessage( 'patrol-log-diff', $parameters['4::curid'] ) |
||
254 | ->inContentLanguage()->text() ); |
||
255 | $text = wfMessage( 'patrol-log-line', $diffLink, "[[$target]]", "" ) |
||
256 | ->inContentLanguage()->text(); |
||
257 | } else { |
||
0 ignored issues
–
show
This
else statement is empty and can be removed.
This check looks for the These if (rand(1, 6) > 3) {
print "Check failed";
} else {
//print "Check succeeded";
}
could be turned into if (rand(1, 6) > 3) {
print "Check failed";
}
This is much more concise to read. ![]() |
|||
258 | // broken?? |
||
259 | } |
||
260 | break; |
||
261 | |||
262 | case 'protect': |
||
263 | switch ( $entry->getSubtype() ) { |
||
264 | case 'protect': |
||
265 | $text = wfMessage( 'protectedarticle' ) |
||
266 | ->rawParams( $target . ' ' . $parameters['4::description'] )->inContentLanguage()->escaped(); |
||
267 | break; |
||
268 | case 'unprotect': |
||
269 | $text = wfMessage( 'unprotectedarticle' ) |
||
270 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
271 | break; |
||
272 | case 'modify': |
||
273 | $text = wfMessage( 'modifiedarticleprotection' ) |
||
274 | ->rawParams( $target . ' ' . $parameters['4::description'] )->inContentLanguage()->escaped(); |
||
275 | break; |
||
276 | case 'move_prot': |
||
277 | $text = wfMessage( 'movedarticleprotection' ) |
||
278 | ->rawParams( $target, $parameters['4::oldtitle'] )->inContentLanguage()->escaped(); |
||
279 | break; |
||
280 | } |
||
281 | break; |
||
282 | |||
283 | case 'newusers': |
||
284 | switch ( $entry->getSubtype() ) { |
||
285 | case 'newusers': |
||
286 | case 'create': |
||
287 | $text = wfMessage( 'newuserlog-create-entry' ) |
||
288 | ->inContentLanguage()->escaped(); |
||
289 | break; |
||
290 | case 'create2': |
||
291 | case 'byemail': |
||
292 | $text = wfMessage( 'newuserlog-create2-entry' ) |
||
293 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
294 | break; |
||
295 | case 'autocreate': |
||
296 | $text = wfMessage( 'newuserlog-autocreate-entry' ) |
||
297 | ->inContentLanguage()->escaped(); |
||
298 | break; |
||
299 | } |
||
300 | break; |
||
301 | |||
302 | View Code Duplication | case 'upload': |
|
303 | switch ( $entry->getSubtype() ) { |
||
304 | case 'upload': |
||
305 | $text = wfMessage( 'uploadedimage' ) |
||
306 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
307 | break; |
||
308 | case 'overwrite': |
||
309 | $text = wfMessage( 'overwroteimage' ) |
||
310 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
311 | break; |
||
312 | } |
||
313 | break; |
||
314 | |||
315 | case 'rights': |
||
316 | View Code Duplication | if ( count( $parameters['4::oldgroups'] ) ) { |
|
317 | $oldgroups = implode( ', ', $parameters['4::oldgroups'] ); |
||
318 | } else { |
||
319 | $oldgroups = wfMessage( 'rightsnone' )->inContentLanguage()->escaped(); |
||
320 | } |
||
321 | View Code Duplication | if ( count( $parameters['5::newgroups'] ) ) { |
|
322 | $newgroups = implode( ', ', $parameters['5::newgroups'] ); |
||
323 | } else { |
||
324 | $newgroups = wfMessage( 'rightsnone' )->inContentLanguage()->escaped(); |
||
325 | } |
||
326 | switch ( $entry->getSubtype() ) { |
||
327 | case 'rights': |
||
328 | $text = wfMessage( 'rightslogentry' ) |
||
329 | ->rawParams( $target, $oldgroups, $newgroups )->inContentLanguage()->escaped(); |
||
330 | break; |
||
331 | case 'autopromote': |
||
332 | $text = wfMessage( 'rightslogentry-autopromote' ) |
||
333 | ->rawParams( $target, $oldgroups, $newgroups )->inContentLanguage()->escaped(); |
||
334 | break; |
||
335 | } |
||
336 | break; |
||
337 | |||
338 | case 'merge': |
||
339 | $text = wfMessage( 'pagemerge-logentry' ) |
||
340 | ->rawParams( $target, $parameters['4::dest'], $parameters['5::mergepoint'] ) |
||
341 | ->inContentLanguage()->escaped(); |
||
342 | break; |
||
343 | |||
344 | case 'block': |
||
345 | switch ( $entry->getSubtype() ) { |
||
346 | case 'block': |
||
347 | // Keep compatibility with extensions by checking for |
||
348 | // new key (5::duration/6::flags) or old key (0/optional 1) |
||
349 | if ( $entry->isLegacy() ) { |
||
350 | $rawDuration = $parameters[0]; |
||
351 | $rawFlags = isset( $parameters[1] ) ? $parameters[1] : ''; |
||
352 | } else { |
||
353 | $rawDuration = $parameters['5::duration']; |
||
354 | $rawFlags = $parameters['6::flags']; |
||
355 | } |
||
356 | $duration = $wgContLang->translateBlockExpiry( $rawDuration ); |
||
357 | $flags = BlockLogFormatter::formatBlockFlags( $rawFlags, $wgContLang ); |
||
358 | $text = wfMessage( 'blocklogentry' ) |
||
359 | ->rawParams( $target, $duration, $flags )->inContentLanguage()->escaped(); |
||
360 | break; |
||
361 | case 'unblock': |
||
362 | $text = wfMessage( 'unblocklogentry' ) |
||
363 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
364 | break; |
||
365 | case 'reblock': |
||
366 | $duration = $wgContLang->translateBlockExpiry( $parameters['5::duration'] ); |
||
367 | $flags = BlockLogFormatter::formatBlockFlags( $parameters['6::flags'], $wgContLang ); |
||
368 | $text = wfMessage( 'reblock-logentry' ) |
||
369 | ->rawParams( $target, $duration, $flags )->inContentLanguage()->escaped(); |
||
370 | break; |
||
371 | } |
||
372 | break; |
||
373 | |||
374 | View Code Duplication | case 'import': |
|
375 | switch ( $entry->getSubtype() ) { |
||
376 | case 'upload': |
||
377 | $text = wfMessage( 'import-logentry-upload' ) |
||
378 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
379 | break; |
||
380 | case 'interwiki': |
||
381 | $text = wfMessage( 'import-logentry-interwiki' ) |
||
382 | ->rawParams( $target )->inContentLanguage()->escaped(); |
||
383 | break; |
||
384 | } |
||
385 | break; |
||
386 | // case 'suppress' --private log -- aaron (so we know who to blame in a few years :-D) |
||
387 | // default: |
||
388 | } |
||
389 | if ( is_null( $text ) ) { |
||
390 | $text = $this->getPlainActionText(); |
||
391 | } |
||
392 | |||
393 | $this->plaintext = false; |
||
0 ignored issues
–
show
The property
$plaintext was declared of type string , but false is of type false . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
394 | $this->irctext = false; |
||
0 ignored issues
–
show
The property
$irctext was declared of type string , but false is of type false . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
395 | |||
396 | return $text; |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * Gets the log action, including username. |
||
401 | * @return string HTML |
||
402 | */ |
||
403 | public function getActionText() { |
||
404 | if ( $this->canView( LogPage::DELETED_ACTION ) ) { |
||
405 | $element = $this->getActionMessage(); |
||
406 | if ( $element instanceof Message ) { |
||
407 | $element = $this->plaintext ? $element->text() : $element->escaped(); |
||
408 | } |
||
409 | if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) { |
||
410 | $element = $this->styleRestricedElement( $element ); |
||
411 | } |
||
412 | } else { |
||
413 | $sep = $this->msg( 'word-separator' ); |
||
414 | $sep = $this->plaintext ? $sep->text() : $sep->escaped(); |
||
415 | $performer = $this->getPerformerElement(); |
||
416 | $element = $performer . $sep . $this->getRestrictedElement( 'rev-deleted-event' ); |
||
417 | } |
||
418 | |||
419 | return $element; |
||
420 | } |
||
421 | |||
422 | /** |
||
423 | * Returns a sentence describing the log action. Usually |
||
424 | * a Message object is returned, but old style log types |
||
425 | * and entries might return pre-escaped HTML string. |
||
426 | * @return Message|string Pre-escaped HTML |
||
427 | */ |
||
428 | protected function getActionMessage() { |
||
429 | $message = $this->msg( $this->getMessageKey() ); |
||
430 | $message->params( $this->getMessageParameters() ); |
||
431 | |||
432 | return $message; |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * Returns a key to be used for formatting the action sentence. |
||
437 | * Default is logentry-TYPE-SUBTYPE for modern logs. Legacy log |
||
438 | * types will use custom keys, and subclasses can also alter the |
||
439 | * key depending on the entry itself. |
||
440 | * @return string Message key |
||
441 | */ |
||
442 | protected function getMessageKey() { |
||
443 | $type = $this->entry->getType(); |
||
444 | $subtype = $this->entry->getSubtype(); |
||
445 | |||
446 | return "logentry-$type-$subtype"; |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * Returns extra links that comes after the action text, like "revert", etc. |
||
451 | * |
||
452 | * @return string |
||
453 | */ |
||
454 | public function getActionLinks() { |
||
455 | return ''; |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * Extracts the optional extra parameters for use in action messages. |
||
460 | * The array indexes start from number 3. |
||
461 | * @return array |
||
462 | */ |
||
463 | protected function extractParameters() { |
||
464 | $entry = $this->entry; |
||
465 | $params = []; |
||
466 | |||
467 | if ( $entry->isLegacy() ) { |
||
468 | foreach ( $entry->getParameters() as $index => $value ) { |
||
469 | $params[$index + 3] = $value; |
||
470 | } |
||
471 | } |
||
472 | |||
473 | // Filter out parameters which are not in format #:foo |
||
474 | foreach ( $entry->getParameters() as $key => $value ) { |
||
475 | if ( strpos( $key, ':' ) === false ) { |
||
476 | continue; |
||
477 | } |
||
478 | list( $index, $type, ) = explode( ':', $key, 3 ); |
||
479 | if ( ctype_digit( $index ) ) { |
||
480 | $params[$index - 1] = $this->formatParameterValue( $type, $value ); |
||
481 | } |
||
482 | } |
||
483 | |||
484 | /* Message class doesn't like non consecutive numbering. |
||
485 | * Fill in missing indexes with empty strings to avoid |
||
486 | * incorrect renumbering. |
||
487 | */ |
||
488 | if ( count( $params ) ) { |
||
489 | $max = max( array_keys( $params ) ); |
||
490 | // index 0 to 2 are added in getMessageParameters |
||
491 | for ( $i = 3; $i < $max; $i++ ) { |
||
492 | if ( !isset( $params[$i] ) ) { |
||
493 | $params[$i] = ''; |
||
494 | } |
||
495 | } |
||
496 | } |
||
497 | |||
498 | return $params; |
||
499 | } |
||
500 | |||
501 | /** |
||
502 | * Formats parameters intented for action message from |
||
503 | * array of all parameters. There are three hardcoded |
||
504 | * parameters (array is zero-indexed, this list not): |
||
505 | * - 1: user name with premade link |
||
506 | * - 2: usable for gender magic function |
||
507 | * - 3: target page with premade link |
||
508 | * @return array |
||
509 | */ |
||
510 | protected function getMessageParameters() { |
||
511 | if ( isset( $this->parsedParameters ) ) { |
||
512 | return $this->parsedParameters; |
||
0 ignored issues
–
show
The property
parsedParameters does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
513 | } |
||
514 | |||
515 | $entry = $this->entry; |
||
516 | $params = $this->extractParameters(); |
||
517 | $params[0] = Message::rawParam( $this->getPerformerElement() ); |
||
518 | $params[1] = $this->canView( LogPage::DELETED_USER ) ? $entry->getPerformer()->getName() : ''; |
||
519 | $params[2] = Message::rawParam( $this->makePageLink( $entry->getTarget() ) ); |
||
520 | |||
521 | // Bad things happens if the numbers are not in correct order |
||
522 | ksort( $params ); |
||
523 | |||
524 | $this->parsedParameters = $params; |
||
525 | return $this->parsedParameters; |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * Formats parameters values dependent to their type |
||
530 | * @param string $type The type of the value. |
||
531 | * Valid are currently: |
||
532 | * * - (empty) or plain: The value is returned as-is |
||
533 | * * raw: The value will be added to the log message |
||
534 | * as raw parameter (e.g. no escaping) |
||
535 | * Use this only if there is no other working |
||
536 | * type like user-link or title-link |
||
537 | * * msg: The value is a message-key, the output is |
||
538 | * the message in user language |
||
539 | * * msg-content: The value is a message-key, the output |
||
540 | * is the message in content language |
||
541 | * * user: The value is a user name, e.g. for GENDER |
||
542 | * * user-link: The value is a user name, returns a |
||
543 | * link for the user |
||
544 | * * title: The value is a page title, |
||
545 | * returns name of page |
||
546 | * * title-link: The value is a page title, |
||
547 | * returns link to this page |
||
548 | * * number: Format value as number |
||
549 | * * list: Format value as a comma-separated list |
||
550 | * @param mixed $value The parameter value that should be formatted |
||
551 | * @return string|array Formated value |
||
552 | * @since 1.21 |
||
553 | */ |
||
554 | protected function formatParameterValue( $type, $value ) { |
||
555 | $saveLinkFlood = $this->linkFlood; |
||
556 | |||
557 | switch ( strtolower( trim( $type ) ) ) { |
||
558 | case 'raw': |
||
559 | $value = Message::rawParam( $value ); |
||
560 | break; |
||
561 | case 'list': |
||
562 | $value = $this->context->getLanguage()->commaList( $value ); |
||
563 | break; |
||
564 | case 'msg': |
||
565 | $value = $this->msg( $value )->text(); |
||
566 | break; |
||
567 | case 'msg-content': |
||
568 | $value = $this->msg( $value )->inContentLanguage()->text(); |
||
569 | break; |
||
570 | case 'number': |
||
571 | $value = Message::numParam( $value ); |
||
572 | break; |
||
573 | case 'user': |
||
574 | $user = User::newFromName( $value ); |
||
575 | $value = $user->getName(); |
||
576 | break; |
||
577 | case 'user-link': |
||
578 | $this->setShowUserToolLinks( false ); |
||
579 | |||
580 | $user = User::newFromName( $value ); |
||
581 | $value = Message::rawParam( $this->makeUserLink( $user ) ); |
||
0 ignored issues
–
show
It seems like
$user defined by \User::newFromName($value) on line 580 can also be of type false ; however, LogFormatter::makeUserLink() does only seem to accept object<User> , did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new ![]() |
|||
582 | |||
583 | $this->setShowUserToolLinks( $saveLinkFlood ); |
||
584 | break; |
||
585 | case 'title': |
||
586 | $title = Title::newFromText( $value ); |
||
587 | $value = $title->getPrefixedText(); |
||
588 | break; |
||
589 | case 'title-link': |
||
590 | $title = Title::newFromText( $value ); |
||
591 | $value = Message::rawParam( $this->makePageLink( $title ) ); |
||
592 | break; |
||
593 | case 'plain': |
||
594 | // Plain text, nothing to do |
||
595 | default: |
||
596 | // Catch other types and use the old behavior (return as-is) |
||
597 | } |
||
598 | |||
599 | return $value; |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * Helper to make a link to the page, taking the plaintext |
||
604 | * value in consideration. |
||
605 | * @param Title $title The page |
||
606 | * @param array $parameters Query parameters |
||
607 | * @param string|null $html Linktext of the link as raw html |
||
608 | * @throws MWException |
||
609 | * @return string |
||
610 | */ |
||
611 | protected function makePageLink( Title $title = null, $parameters = [], $html = null ) { |
||
612 | View Code Duplication | if ( !$this->plaintext ) { |
|
613 | $link = Linker::link( $title, $html, [], $parameters ); |
||
0 ignored issues
–
show
It seems like
$title defined by parameter $title on line 611 can be null ; however, Linker::link() does not accept null , maybe add an additional type check?
It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null. We recommend to add an additional type check (or disallow null for the parameter): function notNullable(stdClass $x) { }
// Unsafe
function withoutCheck(stdClass $x = null) {
notNullable($x);
}
// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
if ($x instanceof stdClass) {
notNullable($x);
}
}
// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
notNullable($x);
}
![]() |
|||
614 | } else { |
||
615 | if ( !$title instanceof Title ) { |
||
616 | throw new MWException( "Expected title, got null" ); |
||
617 | } |
||
618 | $link = '[[' . $title->getPrefixedText() . ']]'; |
||
619 | } |
||
620 | |||
621 | return $link; |
||
622 | } |
||
623 | |||
624 | /** |
||
625 | * Provides the name of the user who performed the log action. |
||
626 | * Used as part of log action message or standalone, depending |
||
627 | * which parts of the log entry has been hidden. |
||
628 | * @return string |
||
629 | */ |
||
630 | View Code Duplication | public function getPerformerElement() { |
|
631 | if ( $this->canView( LogPage::DELETED_USER ) ) { |
||
632 | $performer = $this->entry->getPerformer(); |
||
633 | $element = $this->makeUserLink( $performer ); |
||
634 | if ( $this->entry->isDeleted( LogPage::DELETED_USER ) ) { |
||
635 | $element = $this->styleRestricedElement( $element ); |
||
636 | } |
||
637 | } else { |
||
638 | $element = $this->getRestrictedElement( 'rev-deleted-user' ); |
||
639 | } |
||
640 | |||
641 | return $element; |
||
642 | } |
||
643 | |||
644 | /** |
||
645 | * Gets the user provided comment |
||
646 | * @return string HTML |
||
647 | */ |
||
648 | View Code Duplication | public function getComment() { |
|
649 | if ( $this->canView( LogPage::DELETED_COMMENT ) ) { |
||
650 | $comment = Linker::commentBlock( $this->entry->getComment() ); |
||
651 | // No hard coded spaces thanx |
||
652 | $element = ltrim( $comment ); |
||
653 | if ( $this->entry->isDeleted( LogPage::DELETED_COMMENT ) ) { |
||
654 | $element = $this->styleRestricedElement( $element ); |
||
655 | } |
||
656 | } else { |
||
657 | $element = $this->getRestrictedElement( 'rev-deleted-comment' ); |
||
658 | } |
||
659 | |||
660 | return $element; |
||
661 | } |
||
662 | |||
663 | /** |
||
664 | * Helper method for displaying restricted element. |
||
665 | * @param string $message |
||
666 | * @return string HTML or wiki text |
||
667 | */ |
||
668 | protected function getRestrictedElement( $message ) { |
||
669 | if ( $this->plaintext ) { |
||
670 | return $this->msg( $message )->text(); |
||
671 | } |
||
672 | |||
673 | $content = $this->msg( $message )->escaped(); |
||
674 | $attribs = [ 'class' => 'history-deleted' ]; |
||
675 | |||
676 | return Html::rawElement( 'span', $attribs, $content ); |
||
677 | } |
||
678 | |||
679 | /** |
||
680 | * Helper method for styling restricted element. |
||
681 | * @param string $content |
||
682 | * @return string HTML or wiki text |
||
683 | */ |
||
684 | protected function styleRestricedElement( $content ) { |
||
685 | if ( $this->plaintext ) { |
||
686 | return $content; |
||
687 | } |
||
688 | $attribs = [ 'class' => 'history-deleted' ]; |
||
689 | |||
690 | return Html::rawElement( 'span', $attribs, $content ); |
||
691 | } |
||
692 | |||
693 | /** |
||
694 | * Shortcut for wfMessage which honors local context. |
||
695 | * @param string $key |
||
696 | * @return Message |
||
697 | */ |
||
698 | protected function msg( $key ) { |
||
699 | return $this->context->msg( $key ); |
||
700 | } |
||
701 | |||
702 | protected function makeUserLink( User $user, $toolFlags = 0 ) { |
||
703 | if ( $this->plaintext ) { |
||
704 | $element = $user->getName(); |
||
705 | } else { |
||
706 | $element = Linker::userLink( |
||
707 | $user->getId(), |
||
708 | $user->getName() |
||
709 | ); |
||
710 | |||
711 | if ( $this->linkFlood ) { |
||
712 | $element .= Linker::userToolLinks( |
||
713 | $user->getId(), |
||
714 | $user->getName(), |
||
715 | true, // redContribsWhenNoEdits |
||
716 | $toolFlags, |
||
717 | $user->getEditCount() |
||
718 | ); |
||
719 | } |
||
720 | } |
||
721 | |||
722 | return $element; |
||
723 | } |
||
724 | |||
725 | /** |
||
726 | * @return array Array of titles that should be preloaded with LinkBatch |
||
727 | */ |
||
728 | public function getPreloadTitles() { |
||
729 | return []; |
||
730 | } |
||
731 | |||
732 | /** |
||
733 | * @return array Output of getMessageParameters() for testing |
||
734 | */ |
||
735 | public function getMessageParametersForTesting() { |
||
736 | // This function was added because getMessageParameters() is |
||
737 | // protected and a change from protected to public caused |
||
738 | // problems with extensions |
||
739 | return $this->getMessageParameters(); |
||
740 | } |
||
741 | |||
742 | /** |
||
743 | * Get the array of parameters, converted from legacy format if necessary. |
||
744 | * @since 1.25 |
||
745 | * @return array |
||
746 | */ |
||
747 | protected function getParametersForApi() { |
||
748 | return $this->entry->getParameters(); |
||
749 | } |
||
750 | |||
751 | /** |
||
752 | * Format parameters for API output |
||
753 | * |
||
754 | * The result array should generally map named keys to values. Index and |
||
755 | * type should be omitted, e.g. "4::foo" should be returned as "foo" in the |
||
756 | * output. Values should generally be unformatted. |
||
757 | * |
||
758 | * Renames or removals of keys besides from the legacy numeric format to |
||
759 | * modern named style should be avoided. Any renames should be announced to |
||
760 | * the mediawiki-api-announce mailing list. |
||
761 | * |
||
762 | * @since 1.25 |
||
763 | * @return array |
||
764 | */ |
||
765 | public function formatParametersForApi() { |
||
766 | $logParams = []; |
||
767 | foreach ( $this->getParametersForApi() as $key => $value ) { |
||
768 | $vals = explode( ':', $key, 3 ); |
||
769 | if ( count( $vals ) !== 3 ) { |
||
770 | $logParams[$key] = $value; |
||
771 | continue; |
||
772 | } |
||
773 | $logParams += $this->formatParameterValueForApi( $vals[2], $vals[1], $value ); |
||
774 | } |
||
775 | ApiResult::setIndexedTagName( $logParams, 'param' ); |
||
776 | ApiResult::setArrayType( $logParams, 'assoc' ); |
||
777 | |||
778 | return $logParams; |
||
779 | } |
||
780 | |||
781 | /** |
||
782 | * Format a single parameter value for API output |
||
783 | * |
||
784 | * @since 1.25 |
||
785 | * @param string $name |
||
786 | * @param string $type |
||
787 | * @param string $value |
||
788 | * @return array |
||
789 | */ |
||
790 | protected function formatParameterValueForApi( $name, $type, $value ) { |
||
791 | $type = strtolower( trim( $type ) ); |
||
792 | switch ( $type ) { |
||
793 | case 'bool': |
||
794 | $value = (bool)$value; |
||
795 | break; |
||
796 | |||
797 | case 'number': |
||
798 | if ( ctype_digit( $value ) || is_int( $value ) ) { |
||
799 | $value = (int)$value; |
||
800 | } else { |
||
801 | $value = (float)$value; |
||
802 | } |
||
803 | break; |
||
804 | |||
805 | case 'array': |
||
806 | case 'assoc': |
||
807 | case 'kvp': |
||
808 | if ( is_array( $value ) ) { |
||
809 | ApiResult::setArrayType( $value, $type ); |
||
810 | } |
||
811 | break; |
||
812 | |||
813 | case 'timestamp': |
||
814 | $value = wfTimestamp( TS_ISO_8601, $value ); |
||
815 | break; |
||
816 | |||
817 | case 'msg': |
||
818 | case 'msg-content': |
||
819 | $msg = $this->msg( $value ); |
||
820 | if ( $type === 'msg-content' ) { |
||
821 | $msg->inContentLanguage(); |
||
822 | } |
||
823 | $value = []; |
||
824 | $value["{$name}_key"] = $msg->getKey(); |
||
825 | if ( $msg->getParams() ) { |
||
826 | $value["{$name}_params"] = $msg->getParams(); |
||
827 | } |
||
828 | $value["{$name}_text"] = $msg->text(); |
||
829 | return $value; |
||
830 | |||
831 | case 'title': |
||
832 | case 'title-link': |
||
833 | $title = Title::newFromText( $value ); |
||
834 | if ( $title ) { |
||
835 | $value = []; |
||
836 | ApiQueryBase::addTitleInfo( $value, $title, "{$name}_" ); |
||
837 | } |
||
838 | return $value; |
||
839 | |||
840 | case 'user': |
||
841 | case 'user-link': |
||
842 | $user = User::newFromName( $value ); |
||
843 | if ( $user ) { |
||
844 | $value = $user->getName(); |
||
845 | } |
||
846 | break; |
||
847 | |||
848 | default: |
||
849 | // do nothing |
||
850 | break; |
||
851 | } |
||
852 | |||
853 | return [ $name => $value ]; |
||
854 | } |
||
855 | } |
||
856 | |||
857 | /** |
||
858 | * This class formats all log entries for log types |
||
859 | * which have not been converted to the new system. |
||
860 | * This is not about old log entries which store |
||
861 | * parameters in a different format - the new |
||
862 | * LogFormatter classes have code to support formatting |
||
863 | * those too. |
||
864 | * @since 1.19 |
||
865 | */ |
||
866 | class LegacyLogFormatter extends LogFormatter { |
||
867 | /** |
||
868 | * Backward compatibility for extension changing the comment from |
||
869 | * the LogLine hook. This will be set by the first call on getComment(), |
||
870 | * then it might be modified by the hook when calling getActionLinks(), |
||
871 | * so that the modified value will be returned when calling getComment() |
||
872 | * a second time. |
||
873 | * |
||
874 | * @var string|null |
||
875 | */ |
||
876 | private $comment = null; |
||
877 | |||
878 | /** |
||
879 | * Cache for the result of getActionLinks() so that it does not need to |
||
880 | * run multiple times depending on the order that getComment() and |
||
881 | * getActionLinks() are called. |
||
882 | * |
||
883 | * @var string|null |
||
884 | */ |
||
885 | private $revert = null; |
||
886 | |||
887 | public function getComment() { |
||
888 | if ( $this->comment === null ) { |
||
889 | $this->comment = parent::getComment(); |
||
890 | } |
||
891 | |||
892 | // Make sure we execute the LogLine hook so that we immediately return |
||
893 | // the correct value. |
||
894 | if ( $this->revert === null ) { |
||
895 | $this->getActionLinks(); |
||
896 | } |
||
897 | |||
898 | return $this->comment; |
||
899 | } |
||
900 | |||
901 | protected function getActionMessage() { |
||
902 | $entry = $this->entry; |
||
903 | $action = LogPage::actionText( |
||
904 | $entry->getType(), |
||
905 | $entry->getSubtype(), |
||
906 | $entry->getTarget(), |
||
907 | $this->plaintext ? null : $this->context->getSkin(), |
||
908 | (array)$entry->getParameters(), |
||
909 | !$this->plaintext // whether to filter [[]] links |
||
910 | ); |
||
911 | |||
912 | $performer = $this->getPerformerElement(); |
||
913 | if ( !$this->irctext ) { |
||
914 | $sep = $this->msg( 'word-separator' ); |
||
915 | $sep = $this->plaintext ? $sep->text() : $sep->escaped(); |
||
916 | $action = $performer . $sep . $action; |
||
917 | } |
||
918 | |||
919 | return $action; |
||
920 | } |
||
921 | |||
922 | public function getActionLinks() { |
||
923 | if ( $this->revert !== null ) { |
||
924 | return $this->revert; |
||
925 | } |
||
926 | |||
927 | if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) { |
||
928 | $this->revert = ''; |
||
929 | return $this->revert; |
||
930 | } |
||
931 | |||
932 | $title = $this->entry->getTarget(); |
||
933 | $type = $this->entry->getType(); |
||
934 | $subtype = $this->entry->getSubtype(); |
||
935 | |||
936 | // Do nothing. The implementation is handled by the hook modifiying the |
||
937 | // passed-by-ref parameters. This also changes the default value so that |
||
938 | // getComment() and getActionLinks() do not call them indefinitely. |
||
939 | $this->revert = ''; |
||
940 | |||
941 | // This is to populate the $comment member of this instance so that it |
||
942 | // can be modified when calling the hook just below. |
||
943 | if ( $this->comment === null ) { |
||
944 | $this->getComment(); |
||
945 | } |
||
946 | |||
947 | $params = $this->entry->getParameters(); |
||
948 | |||
949 | Hooks::run( 'LogLine', [ $type, $subtype, $title, $params, |
||
950 | &$this->comment, &$this->revert, $this->entry->getTimestamp() ] ); |
||
951 | |||
952 | return $this->revert; |
||
953 | } |
||
954 | } |
||
955 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.
Either this assignment is in error or an instanceof check should be added for that assignment.