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 | * Efficient paging for SQL queries. |
||
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 | * @ingroup Pager |
||
22 | */ |
||
23 | |||
24 | /** |
||
25 | * Table-based display with a user-selectable sort order |
||
26 | * @ingroup Pager |
||
27 | */ |
||
28 | abstract class TablePager extends IndexPager { |
||
29 | protected $mSort; |
||
30 | |||
31 | protected $mCurrentRow; |
||
32 | |||
33 | public function __construct( IContextSource $context = null ) { |
||
34 | if ( $context ) { |
||
35 | $this->setContext( $context ); |
||
36 | } |
||
37 | |||
38 | $this->mSort = $this->getRequest()->getText( 'sort' ); |
||
39 | if ( !array_key_exists( $this->mSort, $this->getFieldNames() ) |
||
40 | || !$this->isFieldSortable( $this->mSort ) |
||
41 | ) { |
||
42 | $this->mSort = $this->getDefaultSort(); |
||
43 | } |
||
44 | if ( $this->getRequest()->getBool( 'asc' ) ) { |
||
45 | $this->mDefaultDirection = IndexPager::DIR_ASCENDING; |
||
46 | } elseif ( $this->getRequest()->getBool( 'desc' ) ) { |
||
47 | $this->mDefaultDirection = IndexPager::DIR_DESCENDING; |
||
48 | } /* Else leave it at whatever the class default is */ |
||
49 | |||
50 | parent::__construct(); |
||
51 | } |
||
52 | |||
53 | /** |
||
54 | * Get the formatted result list. Calls getStartBody(), formatRow() and getEndBody(), concatenates |
||
55 | * the results and returns them. |
||
56 | * |
||
57 | * Also adds the required styles to our OutputPage object (this means that if context wasn't |
||
58 | * passed to constructor or otherwise set up, you will get a pager with missing styles). |
||
59 | * |
||
60 | * This method has been made 'final' in 1.24. There's no reason to override it, and if there exist |
||
61 | * any subclasses that do, the style loading hack is probably broken in them. Let's fail fast |
||
62 | * rather than mysteriously render things wrong. |
||
63 | * |
||
64 | * @deprecated since 1.24, use getBodyOutput() or getFullOutput() instead |
||
65 | * @return string |
||
66 | */ |
||
67 | final public function getBody() { |
||
68 | $this->getOutput()->addModuleStyles( $this->getModuleStyles() ); |
||
69 | return parent::getBody(); |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * Get the formatted result list. |
||
74 | * |
||
75 | * Calls getBody() and getModuleStyles() and builds a ParserOutput object. (This is a bit hacky |
||
76 | * but works well.) |
||
77 | * |
||
78 | * @since 1.24 |
||
79 | * @return ParserOutput |
||
80 | */ |
||
81 | public function getBodyOutput() { |
||
82 | $body = parent::getBody(); |
||
0 ignored issues
–
show
|
|||
83 | |||
84 | $pout = new ParserOutput; |
||
85 | $pout->setText( $body ); |
||
86 | $pout->addModuleStyles( $this->getModuleStyles() ); |
||
87 | return $pout; |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Get the formatted result list, with navigation bars. |
||
92 | * |
||
93 | * Calls getBody(), getNavigationBar() and getModuleStyles() and |
||
94 | * builds a ParserOutput object. (This is a bit hacky but works well.) |
||
95 | * |
||
96 | * @since 1.24 |
||
97 | * @return ParserOutput |
||
98 | */ |
||
99 | public function getFullOutput() { |
||
100 | $navigation = $this->getNavigationBar(); |
||
101 | $body = parent::getBody(); |
||
0 ignored issues
–
show
It seems like you call parent on a different method (
getBody() instead of getFullOutput() ). Are you sure this is correct? If so, you might want to change this to $this->getBody() .
This check looks for a call to a parent method whose name is different than the method from which it is called. Consider the following code: class Daddy
{
protected function getFirstName()
{
return "Eidur";
}
protected function getSurName()
{
return "Gudjohnsen";
}
}
class Son
{
public function getFirstName()
{
return parent::getSurname();
}
}
The ![]() |
|||
102 | |||
103 | $pout = new ParserOutput; |
||
104 | $pout->setText( $navigation . $body . $navigation ); |
||
105 | $pout->addModuleStyles( $this->getModuleStyles() ); |
||
106 | return $pout; |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * @protected |
||
111 | * @return string |
||
112 | */ |
||
113 | function getStartBody() { |
||
114 | $sortClass = $this->getSortHeaderClass(); |
||
115 | |||
116 | $s = ''; |
||
117 | $fields = $this->getFieldNames(); |
||
118 | |||
119 | // Make table header |
||
120 | foreach ( $fields as $field => $name ) { |
||
121 | if ( strval( $name ) == '' ) { |
||
122 | $s .= Html::rawElement( 'th', [], ' ' ) . "\n"; |
||
123 | } elseif ( $this->isFieldSortable( $field ) ) { |
||
124 | $query = [ 'sort' => $field, 'limit' => $this->mLimit ]; |
||
125 | $linkType = null; |
||
126 | $class = null; |
||
127 | |||
128 | if ( $this->mSort == $field ) { |
||
129 | // The table is sorted by this field already, make a link to sort in the other direction |
||
130 | // We don't actually know in which direction other fields will be sorted by default… |
||
131 | if ( $this->mDefaultDirection == IndexPager::DIR_DESCENDING ) { |
||
132 | $linkType = 'asc'; |
||
133 | $class = "$sortClass TablePager_sort-descending"; |
||
134 | $query['asc'] = '1'; |
||
135 | $query['desc'] = ''; |
||
136 | } else { |
||
137 | $linkType = 'desc'; |
||
138 | $class = "$sortClass TablePager_sort-ascending"; |
||
139 | $query['asc'] = ''; |
||
140 | $query['desc'] = '1'; |
||
141 | } |
||
142 | } |
||
143 | |||
144 | $link = $this->makeLink( htmlspecialchars( $name ), $query, $linkType ); |
||
145 | $s .= Html::rawElement( 'th', [ 'class' => $class ], $link ) . "\n"; |
||
146 | } else { |
||
147 | $s .= Html::element( 'th', [], $name ) . "\n"; |
||
148 | } |
||
149 | } |
||
150 | |||
151 | $tableClass = $this->getTableClass(); |
||
152 | $ret = Html::openElement( 'table', [ |
||
153 | 'class' => "mw-datatable $tableClass" ] |
||
154 | ); |
||
155 | $ret .= Html::rawElement( 'thead', [], Html::rawElement( 'tr', [], "\n" . $s . "\n" ) ); |
||
156 | $ret .= Html::openElement( 'tbody' ) . "\n"; |
||
157 | |||
158 | return $ret; |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * @protected |
||
163 | * @return string |
||
164 | */ |
||
165 | function getEndBody() { |
||
166 | return "</tbody></table>\n"; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * @protected |
||
171 | * @return string |
||
172 | */ |
||
173 | function getEmptyBody() { |
||
174 | $colspan = count( $this->getFieldNames() ); |
||
175 | $msgEmpty = $this->msg( 'table_pager_empty' )->text(); |
||
176 | return Html::rawElement( 'tr', [], |
||
177 | Html::element( 'td', [ 'colspan' => $colspan ], $msgEmpty ) ); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @protected |
||
182 | * @param stdClass $row |
||
183 | * @return string HTML |
||
184 | */ |
||
185 | function formatRow( $row ) { |
||
186 | $this->mCurrentRow = $row; // In case formatValue etc need to know |
||
187 | $s = Html::openElement( 'tr', $this->getRowAttrs( $row ) ) . "\n"; |
||
188 | $fieldNames = $this->getFieldNames(); |
||
189 | |||
190 | foreach ( $fieldNames as $field => $name ) { |
||
191 | $value = isset( $row->$field ) ? $row->$field : null; |
||
192 | $formatted = strval( $this->formatValue( $field, $value ) ); |
||
193 | |||
194 | if ( $formatted == '' ) { |
||
195 | $formatted = ' '; |
||
196 | } |
||
197 | |||
198 | $s .= Html::rawElement( 'td', $this->getCellAttrs( $field, $value ), $formatted ) . "\n"; |
||
199 | } |
||
200 | |||
201 | $s .= Html::closeElement( 'tr' ) . "\n"; |
||
202 | |||
203 | return $s; |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Get a class name to be applied to the given row. |
||
208 | * |
||
209 | * @protected |
||
210 | * |
||
211 | * @param object $row The database result row |
||
212 | * @return string |
||
213 | */ |
||
214 | function getRowClass( $row ) { |
||
215 | return ''; |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * Get attributes to be applied to the given row. |
||
220 | * |
||
221 | * @protected |
||
222 | * |
||
223 | * @param object $row The database result row |
||
224 | * @return array Array of attribute => value |
||
225 | */ |
||
226 | function getRowAttrs( $row ) { |
||
227 | $class = $this->getRowClass( $row ); |
||
228 | if ( $class === '' ) { |
||
229 | // Return an empty array to avoid clutter in HTML like class="" |
||
230 | return []; |
||
231 | } else { |
||
232 | return [ 'class' => $this->getRowClass( $row ) ]; |
||
233 | } |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * @return stdClass |
||
238 | */ |
||
239 | protected function getCurrentRow() { |
||
240 | return $this->mCurrentRow; |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Get any extra attributes to be applied to the given cell. Don't |
||
245 | * take this as an excuse to hardcode styles; use classes and |
||
246 | * CSS instead. Row context is available in $this->mCurrentRow |
||
247 | * |
||
248 | * @protected |
||
249 | * |
||
250 | * @param string $field The column |
||
251 | * @param string $value The cell contents |
||
252 | * @return array Array of attr => value |
||
253 | */ |
||
254 | function getCellAttrs( $field, $value ) { |
||
255 | return [ 'class' => 'TablePager_col_' . $field ]; |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * @protected |
||
260 | * @return string |
||
261 | */ |
||
262 | function getIndexField() { |
||
263 | return $this->mSort; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * @return string |
||
268 | */ |
||
269 | protected function getTableClass() { |
||
270 | return 'TablePager'; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * @return string |
||
275 | */ |
||
276 | protected function getNavClass() { |
||
277 | return 'TablePager_nav'; |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * @return string |
||
282 | */ |
||
283 | protected function getSortHeaderClass() { |
||
284 | return 'TablePager_sort'; |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * A navigation bar with images |
||
289 | * @return string HTML |
||
290 | */ |
||
291 | public function getNavigationBar() { |
||
292 | if ( !$this->isNavigationBarShown() ) { |
||
293 | return ''; |
||
294 | } |
||
295 | |||
296 | $labels = [ |
||
297 | 'first' => 'table_pager_first', |
||
298 | 'prev' => 'table_pager_prev', |
||
299 | 'next' => 'table_pager_next', |
||
300 | 'last' => 'table_pager_last', |
||
301 | ]; |
||
302 | |||
303 | $linkTexts = []; |
||
304 | $disabledTexts = []; |
||
305 | foreach ( $labels as $type => $label ) { |
||
306 | $msgLabel = $this->msg( $label )->escaped(); |
||
307 | $linkTexts[$type] = "<div class='TablePager_nav-enabled'>$msgLabel</div>"; |
||
308 | $disabledTexts[$type] = "<div class='TablePager_nav-disabled'>$msgLabel</div>"; |
||
309 | } |
||
310 | $links = $this->getPagingLinks( $linkTexts, $disabledTexts ); |
||
311 | |||
312 | $s = Html::openElement( 'table', [ 'class' => $this->getNavClass() ] ); |
||
313 | $s .= Html::openElement( 'tr' ) . "\n"; |
||
314 | $width = 100 / count( $links ) . '%'; |
||
315 | foreach ( $labels as $type => $label ) { |
||
316 | // We want every cell to have the same width. We could use table-layout: fixed; in CSS, |
||
317 | // but it only works if we specify the width of a cell or the table and we don't want to. |
||
318 | // There is no better way. <https://www.w3.org/TR/CSS2/tables.html#fixed-table-layout> |
||
319 | $s .= Html::rawElement( 'td', |
||
320 | [ 'style' => "width: $width;", 'class' => "TablePager_nav-$type" ], |
||
321 | $links[$type] ) . "\n"; |
||
322 | } |
||
323 | $s .= Html::closeElement( 'tr' ) . Html::closeElement( 'table' ) . "\n"; |
||
324 | return $s; |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * ResourceLoader modules that must be loaded to provide correct styling for this pager |
||
329 | * @since 1.24 |
||
330 | * @return string[] |
||
331 | */ |
||
332 | public function getModuleStyles() { |
||
333 | return [ 'mediawiki.pager.tablePager' ]; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * Get a "<select>" element which has options for each of the allowed limits |
||
338 | * |
||
339 | * @param string $attribs Extra attributes to set |
||
340 | * @return string HTML fragment |
||
341 | */ |
||
342 | public function getLimitSelect( $attribs = [] ) { |
||
343 | $select = new XmlSelect( 'limit', false, $this->mLimit ); |
||
344 | $select->addOptions( $this->getLimitSelectList() ); |
||
345 | foreach ( $attribs as $name => $value ) { |
||
0 ignored issues
–
show
The expression
$attribs of type string|array is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
346 | $select->setAttribute( $name, $value ); |
||
347 | } |
||
348 | return $select->getHTML(); |
||
349 | } |
||
350 | |||
351 | /** |
||
352 | * Get a list of items to show in a "<select>" element of limits. |
||
353 | * This can be passed directly to XmlSelect::addOptions(). |
||
354 | * |
||
355 | * @since 1.22 |
||
356 | * @return array |
||
357 | */ |
||
358 | public function getLimitSelectList() { |
||
359 | # Add the current limit from the query string |
||
360 | # to avoid that the limit is lost after clicking Go next time |
||
361 | if ( !in_array( $this->mLimit, $this->mLimitsShown ) ) { |
||
362 | $this->mLimitsShown[] = $this->mLimit; |
||
363 | sort( $this->mLimitsShown ); |
||
364 | } |
||
365 | $ret = []; |
||
366 | foreach ( $this->mLimitsShown as $key => $value ) { |
||
367 | # The pair is either $index => $limit, in which case the $value |
||
368 | # will be numeric, or $limit => $text, in which case the $value |
||
369 | # will be a string. |
||
370 | if ( is_int( $value ) ) { |
||
371 | $limit = $value; |
||
372 | $text = $this->getLanguage()->formatNum( $limit ); |
||
373 | } else { |
||
374 | $limit = $key; |
||
375 | $text = $value; |
||
376 | } |
||
377 | $ret[$text] = $limit; |
||
378 | } |
||
379 | return $ret; |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * Get \<input type="hidden"\> elements for use in a method="get" form. |
||
384 | * Resubmits all defined elements of the query string, except for a |
||
385 | * blacklist, passed in the $blacklist parameter. |
||
386 | * |
||
387 | * @param array $blacklist Parameters from the request query which should not be resubmitted |
||
388 | * @return string HTML fragment |
||
389 | */ |
||
390 | function getHiddenFields( $blacklist = [] ) { |
||
391 | $blacklist = (array)$blacklist; |
||
392 | $query = $this->getRequest()->getQueryValues(); |
||
393 | foreach ( $blacklist as $name ) { |
||
394 | unset( $query[$name] ); |
||
395 | } |
||
396 | $s = ''; |
||
397 | foreach ( $query as $name => $value ) { |
||
398 | $s .= Html::hidden( $name, $value ) . "\n"; |
||
399 | } |
||
400 | return $s; |
||
401 | } |
||
402 | |||
403 | /** |
||
404 | * Get a form containing a limit selection dropdown |
||
405 | * |
||
406 | * @return string HTML fragment |
||
407 | */ |
||
408 | function getLimitForm() { |
||
409 | return Html::rawElement( |
||
410 | 'form', |
||
411 | [ |
||
412 | 'method' => 'get', |
||
413 | 'action' => wfScript(), |
||
414 | ], |
||
415 | "\n" . $this->getLimitDropdown() |
||
416 | ) . "\n"; |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Gets a limit selection dropdown |
||
421 | * |
||
422 | * @return string |
||
423 | */ |
||
424 | function getLimitDropdown() { |
||
425 | # Make the select with some explanatory text |
||
426 | $msgSubmit = $this->msg( 'table_pager_limit_submit' )->escaped(); |
||
427 | |||
428 | return $this->msg( 'table_pager_limit' ) |
||
429 | ->rawParams( $this->getLimitSelect() )->escaped() . |
||
430 | "\n<input type=\"submit\" value=\"$msgSubmit\"/>\n" . |
||
431 | $this->getHiddenFields( [ 'limit' ] ); |
||
432 | } |
||
433 | |||
434 | /** |
||
435 | * Return true if the named field should be sortable by the UI, false |
||
436 | * otherwise |
||
437 | * |
||
438 | * @param string $field |
||
439 | */ |
||
440 | abstract function isFieldSortable( $field ); |
||
441 | |||
442 | /** |
||
443 | * Format a table cell. The return value should be HTML, but use an empty |
||
444 | * string not   for empty cells. Do not include the <td> and </td>. |
||
445 | * |
||
446 | * The current result row is available as $this->mCurrentRow, in case you |
||
447 | * need more context. |
||
448 | * |
||
449 | * @protected |
||
450 | * |
||
451 | * @param string $name The database field name |
||
452 | * @param string $value The value retrieved from the database |
||
453 | */ |
||
454 | abstract function formatValue( $name, $value ); |
||
455 | |||
456 | /** |
||
457 | * The database field name used as a default sort order. |
||
458 | * |
||
459 | * @protected |
||
460 | * |
||
461 | * @return string |
||
462 | */ |
||
463 | abstract function getDefaultSort(); |
||
464 | |||
465 | /** |
||
466 | * An array mapping database field names to a textual description of the |
||
467 | * field name, for use in the table header. The description should be plain |
||
468 | * text, it will be HTML-escaped later. |
||
469 | * |
||
470 | * @return array |
||
471 | */ |
||
472 | abstract function getFieldNames(); |
||
473 | } |
||
474 |
This check looks for a call to a parent method whose name is different than the method from which it is called.
Consider the following code:
The
getFirstName()
method in theSon
calls the wrong method in the parent class.