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 | * Implements Special:MediaStatistics |
||
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 SpecialPage |
||
22 | * @author Brian Wolff |
||
23 | */ |
||
24 | |||
25 | /** |
||
26 | * @ingroup SpecialPage |
||
27 | */ |
||
28 | class MediaStatisticsPage extends QueryPage { |
||
29 | protected $totalCount = 0, $totalBytes = 0; |
||
0 ignored issues
–
show
|
|||
30 | /** |
||
31 | * @var integer $totalPerType Combined file size of all files in a section |
||
32 | */ |
||
33 | protected $totalPerType = 0; |
||
34 | /** |
||
35 | * @var integer $totalSize Combined file size of all files |
||
36 | */ |
||
37 | protected $totalSize = 0; |
||
38 | |||
39 | function __construct( $name = 'MediaStatistics' ) { |
||
40 | parent::__construct( $name ); |
||
41 | // Generally speaking there is only a small number of file types, |
||
42 | // so just show all of them. |
||
43 | $this->limit = 5000; |
||
44 | $this->shownavigation = false; |
||
45 | } |
||
46 | |||
47 | public function isExpensive() { |
||
48 | return true; |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * Query to do. |
||
53 | * |
||
54 | * This abuses the query cache table by storing mime types as "titles". |
||
55 | * |
||
56 | * This will store entries like [[Media:BITMAP;image/jpeg;200;20000]] |
||
57 | * where the form is Media type;mime type;count;bytes. |
||
58 | * |
||
59 | * This relies on the behaviour that when value is tied, the order things |
||
60 | * come out of querycache table is the order they went in. Which is hacky. |
||
61 | * However, other special pages like Special:Deadendpages and |
||
62 | * Special:BrokenRedirects also rely on this. |
||
63 | */ |
||
64 | public function getQueryInfo() { |
||
65 | $dbr = wfGetDB( DB_REPLICA ); |
||
66 | $fakeTitle = $dbr->buildConcat( [ |
||
67 | 'img_media_type', |
||
68 | $dbr->addQuotes( ';' ), |
||
69 | 'img_major_mime', |
||
70 | $dbr->addQuotes( '/' ), |
||
71 | 'img_minor_mime', |
||
72 | $dbr->addQuotes( ';' ), |
||
73 | 'COUNT(*)', |
||
74 | $dbr->addQuotes( ';' ), |
||
75 | 'SUM( img_size )' |
||
76 | ] ); |
||
77 | return [ |
||
78 | 'tables' => [ 'image' ], |
||
79 | 'fields' => [ |
||
80 | 'title' => $fakeTitle, |
||
81 | 'namespace' => NS_MEDIA, /* needs to be something */ |
||
82 | 'value' => '1' |
||
83 | ], |
||
84 | 'conds' => [ |
||
85 | // WMF has a random null row in the db |
||
86 | 'img_media_type IS NOT NULL' |
||
87 | ], |
||
88 | 'options' => [ |
||
89 | 'GROUP BY' => [ |
||
90 | 'img_media_type', |
||
91 | 'img_major_mime', |
||
92 | 'img_minor_mime', |
||
93 | ] |
||
94 | ] |
||
95 | ]; |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * How to sort the results |
||
100 | * |
||
101 | * It's important that img_media_type come first, otherwise the |
||
102 | * tables will be fragmented. |
||
103 | * @return Array Fields to sort by |
||
104 | */ |
||
105 | function getOrderFields() { |
||
106 | return [ 'img_media_type', 'count(*)', 'img_major_mime', 'img_minor_mime' ]; |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Output the results of the query. |
||
111 | * |
||
112 | * @param OutputPage $out |
||
113 | * @param Skin $skin (deprecated presumably) |
||
114 | * @param IDatabase $dbr |
||
115 | * @param ResultWrapper $res Results from query |
||
116 | * @param int $num Number of results |
||
117 | * @param int $offset Paging offset (Should always be 0 in our case) |
||
118 | */ |
||
119 | protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) { |
||
120 | $prevMediaType = null; |
||
121 | foreach ( $res as $row ) { |
||
122 | $mediaStats = $this->splitFakeTitle( $row->title ); |
||
123 | if ( count( $mediaStats ) < 4 ) { |
||
124 | continue; |
||
125 | } |
||
126 | list( $mediaType, $mime, $totalCount, $totalBytes ) = $mediaStats; |
||
127 | if ( $prevMediaType !== $mediaType ) { |
||
128 | if ( $prevMediaType !== null ) { |
||
129 | // We're not at beginning, so we have to |
||
130 | // close the previous table. |
||
131 | $this->outputTableEnd(); |
||
132 | } |
||
133 | $this->outputMediaType( $mediaType ); |
||
134 | $this->totalPerType = 0; |
||
135 | $this->outputTableStart( $mediaType ); |
||
136 | $prevMediaType = $mediaType; |
||
137 | } |
||
138 | $this->outputTableRow( $mime, intval( $totalCount ), intval( $totalBytes ) ); |
||
139 | } |
||
140 | if ( $prevMediaType !== null ) { |
||
141 | $this->outputTableEnd(); |
||
142 | // add total size of all files |
||
143 | $this->outputMediaType( 'total' ); |
||
144 | $this->getOutput()->addWikiText( |
||
145 | $this->msg( 'mediastatistics-allbytes' ) |
||
146 | ->numParams( $this->totalSize ) |
||
147 | ->sizeParams( $this->totalSize ) |
||
148 | ->text() |
||
149 | ); |
||
150 | } |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * Output closing </table> |
||
155 | */ |
||
156 | protected function outputTableEnd() { |
||
157 | $this->getOutput()->addHTML( Html::closeElement( 'table' ) ); |
||
158 | $this->getOutput()->addWikiText( |
||
159 | $this->msg( 'mediastatistics-bytespertype' ) |
||
160 | ->numParams( $this->totalPerType ) |
||
161 | ->sizeParams( $this->totalPerType ) |
||
162 | ->numParams( $this->makePercentPretty( $this->totalPerType / $this->totalBytes ) ) |
||
163 | ->text() |
||
164 | ); |
||
165 | $this->totalSize += $this->totalPerType; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Output a row of the stats table |
||
170 | * |
||
171 | * @param string $mime mime type (e.g. image/jpeg) |
||
172 | * @param int $count Number of images of this type |
||
173 | * @param int $totalBytes Total space for images of this type |
||
0 ignored issues
–
show
There is no parameter named
$totalBytes . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not. ![]() |
|||
174 | */ |
||
175 | protected function outputTableRow( $mime, $count, $bytes ) { |
||
176 | $mimeSearch = SpecialPage::getTitleFor( 'MIMEsearch', $mime ); |
||
177 | $row = Html::rawElement( |
||
178 | 'td', |
||
179 | [], |
||
180 | Linker::link( $mimeSearch, htmlspecialchars( $mime ) ) |
||
181 | ); |
||
182 | $row .= Html::element( |
||
183 | 'td', |
||
184 | [], |
||
185 | $this->getExtensionList( $mime ) |
||
186 | ); |
||
187 | $row .= Html::rawElement( |
||
188 | 'td', |
||
189 | // Make sure js sorts it in numeric order |
||
190 | [ 'data-sort-value' => $count ], |
||
191 | $this->msg( 'mediastatistics-nfiles' ) |
||
192 | ->numParams( $count ) |
||
193 | /** @todo Check to be sure this really should have number formatting */ |
||
194 | ->numParams( $this->makePercentPretty( $count / $this->totalCount ) ) |
||
195 | ->parse() |
||
196 | ); |
||
197 | $row .= Html::rawElement( |
||
198 | 'td', |
||
199 | // Make sure js sorts it in numeric order |
||
200 | [ 'data-sort-value' => $bytes ], |
||
201 | $this->msg( 'mediastatistics-nbytes' ) |
||
202 | ->numParams( $bytes ) |
||
203 | ->sizeParams( $bytes ) |
||
204 | /** @todo Check to be sure this really should have number formatting */ |
||
205 | ->numParams( $this->makePercentPretty( $bytes / $this->totalBytes ) ) |
||
206 | ->parse() |
||
207 | ); |
||
208 | $this->totalPerType += $bytes; |
||
209 | $this->getOutput()->addHTML( Html::rawElement( 'tr', [], $row ) ); |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * @param float $decimal A decimal percentage (ie for 12.3%, this would be 0.123) |
||
214 | * @return String The percentage formatted so that 3 significant digits are shown. |
||
215 | */ |
||
216 | protected function makePercentPretty( $decimal ) { |
||
217 | $decimal *= 100; |
||
218 | // Always show three useful digits |
||
219 | if ( $decimal == 0 ) { |
||
220 | return '0'; |
||
221 | } |
||
222 | if ( $decimal >= 100 ) { |
||
223 | return '100'; |
||
224 | } |
||
225 | $percent = sprintf( "%." . max( 0, 2 - floor( log10( $decimal ) ) ) . "f", $decimal ); |
||
226 | // Then remove any trailing 0's |
||
227 | return preg_replace( '/\.?0*$/', '', $percent ); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Given a mime type, return a comma separated list of allowed extensions. |
||
232 | * |
||
233 | * @param string $mime mime type |
||
234 | * @return string Comma separated list of allowed extensions (e.g. ".ogg, .oga") |
||
235 | */ |
||
236 | private function getExtensionList( $mime ) { |
||
237 | $exts = MimeMagic::singleton()->getExtensionsForType( $mime ); |
||
238 | if ( $exts === null ) { |
||
239 | return ''; |
||
240 | } |
||
241 | $extArray = explode( ' ', $exts ); |
||
242 | $extArray = array_unique( $extArray ); |
||
243 | foreach ( $extArray as &$ext ) { |
||
244 | $ext = '.' . $ext; |
||
245 | } |
||
246 | |||
247 | return $this->getLanguage()->commaList( $extArray ); |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Output the start of the table |
||
252 | * |
||
253 | * Including opening <table>, and first <tr> with column headers. |
||
254 | */ |
||
255 | protected function outputTableStart( $mediaType ) { |
||
256 | $this->getOutput()->addHTML( |
||
257 | Html::openElement( |
||
258 | 'table', |
||
259 | [ 'class' => [ |
||
260 | 'mw-mediastats-table', |
||
261 | 'mw-mediastats-table-' . strtolower( $mediaType ), |
||
262 | 'sortable', |
||
263 | 'wikitable' |
||
264 | ] ] |
||
265 | ) |
||
266 | ); |
||
267 | $this->getOutput()->addHTML( $this->getTableHeaderRow() ); |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * Get (not output) the header row for the table |
||
272 | * |
||
273 | * @return String the header row of the able |
||
274 | */ |
||
275 | protected function getTableHeaderRow() { |
||
276 | $headers = [ 'mimetype', 'extensions', 'count', 'totalbytes' ]; |
||
277 | $ths = ''; |
||
278 | foreach ( $headers as $header ) { |
||
279 | $ths .= Html::rawElement( |
||
280 | 'th', |
||
281 | [], |
||
282 | // for grep: |
||
283 | // mediastatistics-table-mimetype, mediastatistics-table-extensions |
||
284 | // tatistics-table-count, mediastatistics-table-totalbytes |
||
285 | $this->msg( 'mediastatistics-table-' . $header )->parse() |
||
286 | ); |
||
287 | } |
||
288 | return Html::rawElement( 'tr', [], $ths ); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Output a header for a new media type section |
||
293 | * |
||
294 | * @param string $mediaType A media type (e.g. from the MEDIATYPE_xxx constants) |
||
295 | */ |
||
296 | protected function outputMediaType( $mediaType ) { |
||
297 | $this->getOutput()->addHTML( |
||
298 | Html::element( |
||
299 | 'h2', |
||
300 | [ 'class' => [ |
||
301 | 'mw-mediastats-mediatype', |
||
302 | 'mw-mediastats-mediatype-' . strtolower( $mediaType ) |
||
303 | ] ], |
||
304 | // for grep |
||
305 | // mediastatistics-header-unknown, mediastatistics-header-bitmap, |
||
306 | // mediastatistics-header-drawing, mediastatistics-header-audio, |
||
307 | // mediastatistics-header-video, mediastatistics-header-multimedia, |
||
308 | // mediastatistics-header-office, mediastatistics-header-text, |
||
309 | // mediastatistics-header-executable, mediastatistics-header-archive, |
||
310 | $this->msg( 'mediastatistics-header-' . strtolower( $mediaType ) )->text() |
||
311 | ) |
||
312 | ); |
||
313 | /** @todo Possibly could add a message here explaining what the different types are. |
||
314 | * not sure if it is needed though. |
||
315 | */ |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * parse the fake title format that this special page abuses querycache with. |
||
320 | * |
||
321 | * @param string $fakeTitle A string formatted as <media type>;<mime type>;<count>;<bytes> |
||
322 | * @return array The constituant parts of $fakeTitle |
||
323 | */ |
||
324 | private function splitFakeTitle( $fakeTitle ) { |
||
325 | return explode( ';', $fakeTitle, 4 ); |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * What group to put the page in |
||
330 | * @return string |
||
331 | */ |
||
332 | protected function getGroupName() { |
||
333 | return 'media'; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * This method isn't used, since we override outputResults, but |
||
338 | * we need to implement since abstract in parent class. |
||
339 | * |
||
340 | * @param Skin $skin |
||
341 | * @param stdObject $result Result row |
||
342 | * @return bool|string|void |
||
343 | * @throws MWException |
||
344 | */ |
||
345 | public function formatResult( $skin, $result ) { |
||
346 | throw new MWException( "unimplemented" ); |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * Initialize total values so we can figure out percentages later. |
||
351 | * |
||
352 | * @param IDatabase $dbr |
||
353 | * @param ResultWrapper $res |
||
354 | */ |
||
355 | public function preprocessResults( $dbr, $res ) { |
||
356 | $this->executeLBFromResultWrapper( $res ); |
||
357 | $this->totalCount = $this->totalBytes = 0; |
||
358 | foreach ( $res as $row ) { |
||
359 | $mediaStats = $this->splitFakeTitle( $row->title ); |
||
360 | $this->totalCount += isset( $mediaStats[2] ) ? $mediaStats[2] : 0; |
||
361 | $this->totalBytes += isset( $mediaStats[3] ) ? $mediaStats[3] : 0; |
||
362 | } |
||
363 | $res->seek( 0 ); |
||
364 | } |
||
365 | } |
||
366 |
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.