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 | * 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 | * @ingroup Pager |
||
20 | */ |
||
21 | |||
22 | /** |
||
23 | * Use TablePager for prettified output. We have to pretend that we're |
||
24 | * getting data from a table when in fact not all of it comes from the database. |
||
25 | * |
||
26 | * @ingroup Pager |
||
27 | */ |
||
28 | class AllMessagesTablePager extends TablePager { |
||
29 | |||
30 | protected $filter, $prefix, $langcode, $displayPrefix; |
||
0 ignored issues
–
show
|
|||
31 | |||
32 | public $mLimitsShown; |
||
33 | |||
34 | /** |
||
35 | * @var Language |
||
36 | */ |
||
37 | public $lang; |
||
38 | |||
39 | /** |
||
40 | * @var null|bool |
||
41 | */ |
||
42 | public $custom; |
||
43 | |||
44 | function __construct( $page, $conds, $langObj = null ) { |
||
45 | parent::__construct( $page->getContext() ); |
||
46 | $this->mIndexField = 'am_title'; |
||
47 | $this->mPage = $page; |
||
0 ignored issues
–
show
The property
mPage 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;
![]() |
|||
48 | $this->mConds = $conds; |
||
0 ignored issues
–
show
The property
mConds 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;
![]() |
|||
49 | // FIXME: Why does this need to be set to DIR_DESCENDING to produce ascending ordering? |
||
50 | $this->mDefaultDirection = IndexPager::DIR_DESCENDING; |
||
51 | $this->mLimitsShown = [ 20, 50, 100, 250, 500, 5000 ]; |
||
52 | |||
53 | global $wgContLang; |
||
54 | |||
55 | $this->talk = $this->msg( 'talkpagelinktext' )->escaped(); |
||
0 ignored issues
–
show
The property
talk 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;
![]() |
|||
56 | |||
57 | $this->lang = ( $langObj ? $langObj : $wgContLang ); |
||
58 | $this->langcode = $this->lang->getCode(); |
||
59 | $this->foreign = !$this->lang->equals( $wgContLang ); |
||
0 ignored issues
–
show
The property
foreign 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;
![]() |
|||
60 | |||
61 | $request = $this->getRequest(); |
||
62 | |||
63 | $this->filter = $request->getVal( 'filter', 'all' ); |
||
64 | if ( $this->filter === 'all' ) { |
||
65 | $this->custom = null; // So won't match in either case |
||
66 | } else { |
||
67 | $this->custom = ( $this->filter === 'unmodified' ); |
||
68 | } |
||
69 | |||
70 | $prefix = $this->getLanguage()->ucfirst( $request->getVal( 'prefix', '' ) ); |
||
71 | $prefix = $prefix !== '' ? |
||
72 | Title::makeTitleSafe( NS_MEDIAWIKI, $request->getVal( 'prefix', null ) ) : |
||
73 | null; |
||
74 | |||
75 | if ( $prefix !== null ) { |
||
76 | $this->displayPrefix = $prefix->getDBkey(); |
||
77 | $this->prefix = '/^' . preg_quote( $this->displayPrefix, '/' ) . '/i'; |
||
78 | } else { |
||
79 | $this->displayPrefix = false; |
||
80 | $this->prefix = false; |
||
81 | } |
||
82 | |||
83 | // The suffix that may be needed for message names if we're in a |
||
84 | // different language (eg [[MediaWiki:Foo/fr]]: $suffix = '/fr' |
||
85 | if ( $this->foreign ) { |
||
86 | $this->suffix = '/' . $this->langcode; |
||
0 ignored issues
–
show
The property
suffix 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;
![]() |
|||
87 | } else { |
||
88 | $this->suffix = ''; |
||
89 | } |
||
90 | } |
||
91 | |||
92 | function buildForm() { |
||
93 | $attrs = [ 'id' => 'mw-allmessages-form-lang', 'name' => 'lang' ]; |
||
94 | $msg = wfMessage( 'allmessages-language' ); |
||
95 | $langSelect = Xml::languageSelector( $this->langcode, false, null, $attrs, $msg ); |
||
96 | |||
97 | $out = Xml::openElement( 'form', [ |
||
98 | 'method' => 'get', |
||
99 | 'action' => $this->getConfig()->get( 'Script' ), |
||
100 | 'id' => 'mw-allmessages-form' |
||
101 | ] ) . |
||
102 | Xml::fieldset( $this->msg( 'allmessages-filter-legend' )->text() ) . |
||
103 | Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . |
||
104 | Xml::openElement( 'table', [ 'class' => 'mw-allmessages-table' ] ) . "\n" . |
||
105 | '<tr> |
||
106 | <td class="mw-label">' . |
||
107 | Xml::label( $this->msg( 'allmessages-prefix' )->text(), 'mw-allmessages-form-prefix' ) . |
||
108 | "</td>\n |
||
109 | <td class=\"mw-input\">" . |
||
110 | Xml::input( |
||
111 | 'prefix', |
||
112 | 20, |
||
113 | str_replace( '_', ' ', $this->displayPrefix ), |
||
114 | [ 'id' => 'mw-allmessages-form-prefix' ] |
||
115 | ) . |
||
116 | "</td>\n |
||
117 | </tr> |
||
118 | <tr>\n |
||
119 | <td class='mw-label'>" . |
||
120 | $this->msg( 'allmessages-filter' )->escaped() . |
||
121 | "</td>\n |
||
122 | <td class='mw-input'>" . |
||
123 | Xml::radioLabel( $this->msg( 'allmessages-filter-unmodified' )->text(), |
||
124 | 'filter', |
||
125 | 'unmodified', |
||
126 | 'mw-allmessages-form-filter-unmodified', |
||
127 | ( $this->filter === 'unmodified' ) |
||
128 | ) . |
||
129 | Xml::radioLabel( $this->msg( 'allmessages-filter-all' )->text(), |
||
130 | 'filter', |
||
131 | 'all', |
||
132 | 'mw-allmessages-form-filter-all', |
||
133 | ( $this->filter === 'all' ) |
||
134 | ) . |
||
135 | Xml::radioLabel( $this->msg( 'allmessages-filter-modified' )->text(), |
||
136 | 'filter', |
||
137 | 'modified', |
||
138 | 'mw-allmessages-form-filter-modified', |
||
139 | ( $this->filter === 'modified' ) |
||
140 | ) . |
||
141 | "</td>\n |
||
142 | </tr> |
||
143 | <tr>\n |
||
144 | <td class=\"mw-label\">" . $langSelect[0] . "</td>\n |
||
145 | <td class=\"mw-input\">" . $langSelect[1] . "</td>\n |
||
146 | </tr>" . |
||
147 | |||
148 | '<tr> |
||
149 | <td class="mw-label">' . |
||
150 | Xml::label( $this->msg( 'table_pager_limit_label' )->text(), 'mw-table_pager_limit_label' ) . |
||
151 | '</td> |
||
152 | <td class="mw-input">' . |
||
153 | $this->getLimitSelect( [ 'id' => 'mw-table_pager_limit_label' ] ) . |
||
154 | '</td> |
||
155 | <tr> |
||
156 | <td></td> |
||
157 | <td>' . |
||
158 | Xml::submitButton( $this->msg( 'allmessages-filter-submit' )->text() ) . |
||
159 | "</td>\n |
||
160 | </tr>" . |
||
161 | |||
162 | Xml::closeElement( 'table' ) . |
||
163 | $this->getHiddenFields( [ 'title', 'prefix', 'filter', 'lang', 'limit' ] ) . |
||
164 | Xml::closeElement( 'fieldset' ) . |
||
165 | Xml::closeElement( 'form' ); |
||
166 | |||
167 | return $out; |
||
168 | } |
||
169 | |||
170 | function getAllMessages( $descending ) { |
||
171 | $messageNames = Language::getLocalisationCache()->getSubitemList( 'en', 'messages' ); |
||
172 | |||
173 | // Normalise message names so they look like page titles and sort correctly - T86139 |
||
174 | $messageNames = array_map( [ $this->lang, 'ucfirst' ], $messageNames ); |
||
175 | |||
176 | if ( $descending ) { |
||
177 | rsort( $messageNames ); |
||
178 | } else { |
||
179 | asort( $messageNames ); |
||
180 | } |
||
181 | |||
182 | return $messageNames; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Determine which of the MediaWiki and MediaWiki_talk namespace pages exist. |
||
187 | * Returns [ 'pages' => ..., 'talks' => ... ], where the subarrays have |
||
188 | * an entry for each existing page, with the key being the message name and |
||
189 | * value arbitrary. |
||
190 | * |
||
191 | * @param array $messageNames |
||
192 | * @param string $langcode What language code |
||
193 | * @param bool $foreign Whether the $langcode is not the content language |
||
194 | * @return array A 'pages' and 'talks' array with the keys of existing pages |
||
195 | */ |
||
196 | public static function getCustomisedStatuses( $messageNames, $langcode = 'en', $foreign = false ) { |
||
197 | // FIXME: This function should be moved to Language:: or something. |
||
198 | |||
199 | $dbr = wfGetDB( DB_REPLICA ); |
||
200 | $res = $dbr->select( 'page', |
||
201 | [ 'page_namespace', 'page_title' ], |
||
202 | [ 'page_namespace' => [ NS_MEDIAWIKI, NS_MEDIAWIKI_TALK ] ], |
||
203 | __METHOD__, |
||
204 | [ 'USE INDEX' => 'name_title' ] |
||
205 | ); |
||
206 | $xNames = array_flip( $messageNames ); |
||
207 | |||
208 | $pageFlags = $talkFlags = []; |
||
209 | |||
210 | foreach ( $res as $s ) { |
||
211 | $exists = false; |
||
212 | |||
213 | if ( $foreign ) { |
||
214 | $titleParts = explode( '/', $s->page_title ); |
||
215 | if ( count( $titleParts ) === 2 && |
||
216 | $langcode === $titleParts[1] && |
||
217 | isset( $xNames[$titleParts[0]] ) |
||
218 | ) { |
||
219 | $exists = $titleParts[0]; |
||
220 | } |
||
221 | } elseif ( isset( $xNames[$s->page_title] ) ) { |
||
222 | $exists = $s->page_title; |
||
223 | } |
||
224 | |||
225 | $title = Title::newFromRow( $s ); |
||
226 | if ( $exists && $title->inNamespace( NS_MEDIAWIKI ) ) { |
||
227 | $pageFlags[$exists] = true; |
||
228 | } elseif ( $exists && $title->inNamespace( NS_MEDIAWIKI_TALK ) ) { |
||
229 | $talkFlags[$exists] = true; |
||
230 | } |
||
231 | } |
||
232 | |||
233 | return [ 'pages' => $pageFlags, 'talks' => $talkFlags ]; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * This function normally does a database query to get the results; we need |
||
238 | * to make a pretend result using a FakeResultWrapper. |
||
239 | * @param string $offset |
||
240 | * @param int $limit |
||
241 | * @param bool $descending |
||
242 | * @return FakeResultWrapper |
||
243 | */ |
||
244 | function reallyDoQuery( $offset, $limit, $descending ) { |
||
245 | $result = new FakeResultWrapper( [] ); |
||
246 | |||
247 | $messageNames = $this->getAllMessages( $descending ); |
||
248 | $statuses = self::getCustomisedStatuses( $messageNames, $this->langcode, $this->foreign ); |
||
249 | |||
250 | $count = 0; |
||
251 | foreach ( $messageNames as $key ) { |
||
252 | $customised = isset( $statuses['pages'][$key] ); |
||
253 | if ( $customised !== $this->custom && |
||
254 | ( $descending && ( $key < $offset || !$offset ) || !$descending && $key > $offset ) && |
||
255 | ( ( $this->prefix && preg_match( $this->prefix, $key ) ) || $this->prefix === false ) |
||
0 ignored issues
–
show
The expression
$this->prefix of type string|false is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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
![]() |
|||
256 | ) { |
||
257 | $actual = wfMessage( $key )->inLanguage( $this->langcode )->plain(); |
||
258 | $default = wfMessage( $key )->inLanguage( $this->langcode )->useDatabase( false )->plain(); |
||
259 | $result->result[] = [ |
||
260 | 'am_title' => $key, |
||
261 | 'am_actual' => $actual, |
||
262 | 'am_default' => $default, |
||
263 | 'am_customised' => $customised, |
||
264 | 'am_talk_exists' => isset( $statuses['talks'][$key] ) |
||
265 | ]; |
||
266 | $count++; |
||
267 | } |
||
268 | |||
269 | if ( $count === $limit ) { |
||
270 | break; |
||
271 | } |
||
272 | } |
||
273 | |||
274 | return $result; |
||
275 | } |
||
276 | |||
277 | function getStartBody() { |
||
278 | $tableClass = $this->getTableClass(); |
||
279 | return Xml::openElement( 'table', [ |
||
280 | 'class' => "mw-datatable $tableClass", |
||
281 | 'id' => 'mw-allmessagestable' |
||
282 | ] ) . |
||
283 | "\n" . |
||
284 | "<thead><tr> |
||
285 | <th rowspan=\"2\">" . |
||
286 | $this->msg( 'allmessagesname' )->escaped() . " |
||
287 | </th> |
||
288 | <th>" . |
||
289 | $this->msg( 'allmessagesdefault' )->escaped() . |
||
290 | "</th> |
||
291 | </tr>\n |
||
292 | <tr> |
||
293 | <th>" . |
||
294 | $this->msg( 'allmessagescurrent' )->escaped() . |
||
295 | "</th> |
||
296 | </tr></thead><tbody>\n"; |
||
297 | } |
||
298 | |||
299 | function formatValue( $field, $value ) { |
||
300 | switch ( $field ) { |
||
301 | case 'am_title' : |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
302 | $title = Title::makeTitle( NS_MEDIAWIKI, $value . $this->suffix ); |
||
303 | $talk = Title::makeTitle( NS_MEDIAWIKI_TALK, $value . $this->suffix ); |
||
304 | $translation = Linker::makeExternalLink( |
||
305 | 'https://translatewiki.net/w/i.php?' . wfArrayToCgi( [ |
||
306 | 'title' => 'Special:SearchTranslations', |
||
307 | 'group' => 'mediawiki', |
||
308 | 'grouppath' => 'mediawiki', |
||
309 | 'language' => $this->getLanguage()->getCode(), |
||
310 | 'query' => $value . ' ' . $this->msg( $value )->plain() |
||
311 | ] ), |
||
312 | $this->msg( 'allmessages-filter-translate' )->text() |
||
313 | ); |
||
314 | |||
315 | if ( $this->mCurrentRow->am_customised ) { |
||
316 | $title = Linker::linkKnown( $title, $this->getLanguage()->lcfirst( $value ) ); |
||
317 | } else { |
||
318 | $title = Linker::link( |
||
319 | $title, |
||
320 | $this->getLanguage()->lcfirst( $value ), |
||
321 | [], |
||
322 | [], |
||
323 | [ 'broken' ] |
||
324 | ); |
||
325 | } |
||
326 | if ( $this->mCurrentRow->am_talk_exists ) { |
||
327 | $talk = Linker::linkKnown( $talk, $this->talk ); |
||
328 | } else { |
||
329 | $talk = Linker::link( |
||
330 | $talk, |
||
331 | $this->talk, |
||
332 | [], |
||
333 | [], |
||
334 | [ 'broken' ] |
||
335 | ); |
||
336 | } |
||
337 | |||
338 | return $title . ' ' . |
||
339 | $this->msg( 'parentheses' )->rawParams( $talk )->escaped() . |
||
340 | ' ' . |
||
341 | $this->msg( 'parentheses' )->rawParams( $translation )->escaped(); |
||
342 | |||
343 | case 'am_default' : |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
344 | case 'am_actual' : |
||
0 ignored issues
–
show
There must be no space before the colon in a CASE statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements. switch ($selector) {
case "A": //right
doSomething();
break;
case "B" : //wrong
doSomethingElse();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
345 | return Sanitizer::escapeHtmlAllowEntities( $value ); |
||
346 | } |
||
347 | |||
348 | return ''; |
||
349 | } |
||
350 | |||
351 | function formatRow( $row ) { |
||
352 | // Do all the normal stuff |
||
353 | $s = parent::formatRow( $row ); |
||
0 ignored issues
–
show
It seems like
$row defined by parameter $row on line 351 can also be of type array ; however, TablePager::formatRow() does only seem to accept object<stdClass> , maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. ![]() |
|||
354 | |||
355 | // But if there's a customised message, add that too. |
||
356 | if ( $row->am_customised ) { |
||
357 | $s .= Xml::openElement( 'tr', $this->getRowAttrs( $row, true ) ); |
||
358 | $formatted = strval( $this->formatValue( 'am_actual', $row->am_actual ) ); |
||
359 | |||
360 | if ( $formatted === '' ) { |
||
361 | $formatted = ' '; |
||
362 | } |
||
363 | |||
364 | $s .= Xml::tags( 'td', $this->getCellAttrs( 'am_actual', $row->am_actual ), $formatted ) |
||
365 | . "</tr>\n"; |
||
366 | } |
||
367 | |||
368 | return $s; |
||
369 | } |
||
370 | |||
371 | function getRowAttrs( $row, $isSecond = false ) { |
||
372 | $arr = []; |
||
373 | |||
374 | if ( $row->am_customised ) { |
||
375 | $arr['class'] = 'allmessages-customised'; |
||
376 | } |
||
377 | |||
378 | if ( !$isSecond ) { |
||
379 | $arr['id'] = Sanitizer::escapeId( 'msg_' . $this->getLanguage()->lcfirst( $row->am_title ) ); |
||
380 | } |
||
381 | |||
382 | return $arr; |
||
383 | } |
||
384 | |||
385 | function getCellAttrs( $field, $value ) { |
||
386 | if ( $this->mCurrentRow->am_customised && $field === 'am_title' ) { |
||
387 | return [ 'rowspan' => '2', 'class' => $field ]; |
||
388 | } elseif ( $field === 'am_title' ) { |
||
389 | return [ 'class' => $field ]; |
||
390 | } else { |
||
391 | return [ |
||
392 | 'lang' => $this->lang->getHtmlCode(), |
||
393 | 'dir' => $this->lang->getDir(), |
||
394 | 'class' => $field |
||
395 | ]; |
||
396 | } |
||
397 | } |
||
398 | |||
399 | // This is not actually used, as getStartBody is overridden above |
||
400 | function getFieldNames() { |
||
401 | return [ |
||
402 | 'am_title' => $this->msg( 'allmessagesname' )->text(), |
||
403 | 'am_default' => $this->msg( 'allmessagesdefault' )->text() |
||
404 | ]; |
||
405 | } |
||
406 | |||
407 | function getTitle() { |
||
408 | return SpecialPage::getTitleFor( 'Allmessages', false ); |
||
409 | } |
||
410 | |||
411 | function isFieldSortable( $x ) { |
||
412 | return false; |
||
413 | } |
||
414 | |||
415 | function getDefaultSort() { |
||
416 | return ''; |
||
417 | } |
||
418 | |||
419 | function getQueryInfo() { |
||
420 | return ''; |
||
421 | } |
||
422 | |||
423 | } |
||
424 |
Only declaring a single property per statement allows you to later on add doc comments more easily.
It is also recommended by PSR2, so it is a common style that many people expect.