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:Statistics |
||
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 | */ |
||
23 | |||
24 | /** |
||
25 | * Special page lists various statistics, including the contents of |
||
26 | * `site_stats`, plus page view details if enabled |
||
27 | * |
||
28 | * @ingroup SpecialPage |
||
29 | */ |
||
30 | class SpecialStatistics extends SpecialPage { |
||
31 | private $edits, $good, $images, $total, $users, |
||
0 ignored issues
–
show
|
|||
32 | $activeUsers = 0; |
||
33 | |||
34 | public function __construct() { |
||
35 | parent::__construct( 'Statistics' ); |
||
36 | } |
||
37 | |||
38 | public function execute( $par ) { |
||
39 | $this->setHeaders(); |
||
40 | $this->getOutput()->addModuleStyles( 'mediawiki.special' ); |
||
41 | |||
42 | $this->edits = SiteStats::edits(); |
||
43 | $this->good = SiteStats::articles(); |
||
44 | $this->images = SiteStats::images(); |
||
45 | $this->total = SiteStats::pages(); |
||
46 | $this->users = SiteStats::users(); |
||
47 | $this->activeUsers = SiteStats::activeUsers(); |
||
48 | $this->hook = ''; |
||
0 ignored issues
–
show
The property
hook 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;
Loading history...
|
|||
49 | |||
50 | $text = Xml::openElement( 'table', [ 'class' => 'wikitable mw-statistics-table' ] ); |
||
51 | |||
52 | # Statistic - pages |
||
53 | $text .= $this->getPageStats(); |
||
54 | |||
55 | # Statistic - edits |
||
56 | $text .= $this->getEditStats(); |
||
57 | |||
58 | # Statistic - users |
||
59 | $text .= $this->getUserStats(); |
||
60 | |||
61 | # Statistic - usergroups |
||
62 | $text .= $this->getGroupStats(); |
||
63 | |||
64 | # Statistic - other |
||
65 | $extraStats = []; |
||
66 | if ( Hooks::run( 'SpecialStatsAddExtra', [ &$extraStats, $this->getContext() ] ) ) { |
||
67 | $text .= $this->getOtherStats( $extraStats ); |
||
68 | } |
||
69 | |||
70 | $text .= Xml::closeElement( 'table' ); |
||
71 | |||
72 | # Customizable footer |
||
73 | $footer = $this->msg( 'statistics-footer' ); |
||
74 | if ( !$footer->isBlank() ) { |
||
75 | $text .= "\n" . $footer->parse(); |
||
76 | } |
||
77 | |||
78 | $this->getOutput()->addHTML( $text ); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Format a row |
||
83 | * @param string $text Description of the row |
||
84 | * @param float $number A statistical number |
||
85 | * @param array $trExtraParams Params to table row, see Html::elememt |
||
86 | * @param string $descMsg Message key |
||
87 | * @param array|string $descMsgParam Message parameters |
||
88 | * @return string Table row in HTML format |
||
89 | */ |
||
90 | private function formatRow( $text, $number, $trExtraParams = [], |
||
91 | $descMsg = '', $descMsgParam = '' |
||
92 | ) { |
||
93 | if ( $descMsg ) { |
||
94 | $msg = $this->msg( $descMsg, $descMsgParam ); |
||
95 | if ( !$msg->isDisabled() ) { |
||
96 | $descriptionHtml = $this->msg( 'parentheses' )->rawParams( $msg->parse() ) |
||
97 | ->escaped(); |
||
98 | $text .= "<br />" . Html::rawElement( 'small', [ 'class' => 'mw-statistic-desc' ], |
||
99 | " $descriptionHtml" ); |
||
100 | } |
||
101 | } |
||
102 | |||
103 | return Html::rawElement( 'tr', $trExtraParams, |
||
104 | Html::rawElement( 'td', [], $text ) . |
||
105 | Html::rawElement( 'td', [ 'class' => 'mw-statistics-numbers' ], $number ) |
||
106 | ); |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Each of these methods is pretty self-explanatory, get a particular |
||
111 | * row for the table of statistics |
||
112 | * @return string |
||
113 | */ |
||
114 | private function getPageStats() { |
||
115 | $specialAllPagesTitle = SpecialPage::getTitleFor( 'Allpages' ); |
||
116 | $pageStatsHtml = Xml::openElement( 'tr' ) . |
||
117 | Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( 'statistics-header-pages' ) |
||
118 | ->parse() ) . |
||
119 | Xml::closeElement( 'tr' ) . |
||
120 | $this->formatRow( Linker::linkKnown( $specialAllPagesTitle, |
||
121 | $this->msg( 'statistics-articles' )->parse(), [], [ 'hideredirects' => 1 ] ), |
||
122 | $this->getLanguage()->formatNum( $this->good ), |
||
123 | [ 'class' => 'mw-statistics-articles' ], |
||
124 | 'statistics-articles-desc' ) . |
||
125 | $this->formatRow( Linker::linkKnown( $specialAllPagesTitle, |
||
126 | $this->msg( 'statistics-pages' )->parse() ), |
||
127 | $this->getLanguage()->formatNum( $this->total ), |
||
128 | [ 'class' => 'mw-statistics-pages' ], |
||
129 | 'statistics-pages-desc' ); |
||
130 | |||
131 | // Show the image row only, when there are files or upload is possible |
||
132 | if ( $this->images !== 0 || $this->getConfig()->get( 'EnableUploads' ) ) { |
||
133 | $pageStatsHtml .= $this->formatRow( |
||
134 | Linker::linkKnown( SpecialPage::getTitleFor( 'MediaStatistics' ), |
||
135 | $this->msg( 'statistics-files' )->parse() ), |
||
136 | $this->getLanguage()->formatNum( $this->images ), |
||
137 | [ 'class' => 'mw-statistics-files' ] ); |
||
138 | } |
||
139 | |||
140 | return $pageStatsHtml; |
||
141 | } |
||
142 | |||
143 | private function getEditStats() { |
||
144 | return Xml::openElement( 'tr' ) . |
||
145 | Xml::tags( 'th', [ 'colspan' => '2' ], |
||
146 | $this->msg( 'statistics-header-edits' )->parse() ) . |
||
147 | Xml::closeElement( 'tr' ) . |
||
148 | $this->formatRow( $this->msg( 'statistics-edits' )->parse(), |
||
149 | $this->getLanguage()->formatNum( $this->edits ), |
||
150 | [ 'class' => 'mw-statistics-edits' ] |
||
151 | ) . |
||
152 | $this->formatRow( $this->msg( 'statistics-edits-average' )->parse(), |
||
153 | $this->getLanguage() |
||
154 | ->formatNum( sprintf( '%.2f', $this->total ? $this->edits / $this->total : 0 ) ), |
||
155 | [ 'class' => 'mw-statistics-edits-average' ] |
||
156 | ); |
||
157 | } |
||
158 | |||
159 | private function getUserStats() { |
||
160 | return Xml::openElement( 'tr' ) . |
||
161 | Xml::tags( 'th', [ 'colspan' => '2' ], |
||
162 | $this->msg( 'statistics-header-users' )->parse() ) . |
||
163 | Xml::closeElement( 'tr' ) . |
||
164 | $this->formatRow( $this->msg( 'statistics-users' )->parse(), |
||
165 | $this->getLanguage()->formatNum( $this->users ), |
||
166 | [ 'class' => 'mw-statistics-users' ] |
||
167 | ) . |
||
168 | $this->formatRow( $this->msg( 'statistics-users-active' )->parse() . ' ' . |
||
169 | Linker::linkKnown( |
||
170 | SpecialPage::getTitleFor( 'Activeusers' ), |
||
171 | $this->msg( 'listgrouprights-members' )->escaped() |
||
172 | ), |
||
173 | $this->getLanguage()->formatNum( $this->activeUsers ), |
||
174 | [ 'class' => 'mw-statistics-users-active' ], |
||
175 | 'statistics-users-active-desc', |
||
176 | $this->getLanguage()->formatNum( $this->getConfig()->get( 'ActiveUserDays' ) ) |
||
177 | ); |
||
178 | } |
||
179 | |||
180 | private function getGroupStats() { |
||
181 | $text = ''; |
||
182 | foreach ( $this->getConfig()->get( 'GroupPermissions' ) as $group => $permissions ) { |
||
183 | # Skip generic * and implicit groups |
||
184 | if ( in_array( $group, $this->getConfig()->get( 'ImplicitGroups' ) ) || $group == '*' ) { |
||
185 | continue; |
||
186 | } |
||
187 | $groupname = htmlspecialchars( $group ); |
||
188 | $msg = $this->msg( 'group-' . $groupname ); |
||
189 | if ( $msg->isBlank() ) { |
||
190 | $groupnameLocalized = $groupname; |
||
191 | } else { |
||
192 | $groupnameLocalized = $msg->text(); |
||
193 | } |
||
194 | $msg = $this->msg( 'grouppage-' . $groupname )->inContentLanguage(); |
||
195 | if ( $msg->isBlank() ) { |
||
196 | $grouppageLocalized = MWNamespace::getCanonicalName( NS_PROJECT ) . ':' . $groupname; |
||
197 | } else { |
||
198 | $grouppageLocalized = $msg->text(); |
||
199 | } |
||
200 | $linkTarget = Title::newFromText( $grouppageLocalized ); |
||
201 | |||
202 | if ( $linkTarget ) { |
||
203 | $grouppage = Linker::link( |
||
204 | $linkTarget, |
||
205 | htmlspecialchars( $groupnameLocalized ) |
||
206 | ); |
||
207 | } else { |
||
208 | $grouppage = htmlspecialchars( $groupnameLocalized ); |
||
209 | } |
||
210 | |||
211 | $grouplink = Linker::linkKnown( |
||
212 | SpecialPage::getTitleFor( 'Listusers' ), |
||
213 | $this->msg( 'listgrouprights-members' )->escaped(), |
||
214 | [], |
||
215 | [ 'group' => $group ] |
||
216 | ); |
||
217 | # Add a class when a usergroup contains no members to allow hiding these rows |
||
218 | $classZero = ''; |
||
219 | $countUsers = SiteStats::numberingroup( $groupname ); |
||
220 | if ( $countUsers == 0 ) { |
||
221 | $classZero = ' statistics-group-zero'; |
||
222 | } |
||
223 | $text .= $this->formatRow( $grouppage . ' ' . $grouplink, |
||
224 | $this->getLanguage()->formatNum( $countUsers ), |
||
225 | [ 'class' => 'statistics-group-' . Sanitizer::escapeClass( $group ) . |
||
226 | $classZero ] ); |
||
227 | } |
||
228 | |||
229 | return $text; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * Conversion of external statistics into an internal representation |
||
234 | * Following a ([<header-message>][<item-message>] = number) pattern |
||
235 | * |
||
236 | * @param array $stats |
||
237 | * @return string |
||
238 | */ |
||
239 | private function getOtherStats( array $stats ) { |
||
240 | $return = ''; |
||
241 | |||
242 | foreach ( $stats as $header => $items ) { |
||
243 | // Identify the structure used |
||
244 | if ( is_array( $items ) ) { |
||
245 | |||
246 | // Ignore headers that are recursively set as legacy header |
||
247 | if ( $header !== 'statistics-header-hooks' ) { |
||
248 | $return .= $this->formatRowHeader( $header ); |
||
249 | } |
||
250 | |||
251 | // Collect all items that belong to the same header |
||
252 | foreach ( $items as $key => $value ) { |
||
253 | if ( is_array( $value ) ) { |
||
254 | $name = $value['name']; |
||
255 | $number = $value['number']; |
||
256 | } else { |
||
257 | $name = $this->msg( $key )->parse(); |
||
258 | $number = $value; |
||
259 | } |
||
260 | |||
261 | $return .= $this->formatRow( |
||
262 | $name, |
||
263 | $this->getLanguage()->formatNum( htmlspecialchars( $number ) ), |
||
264 | [ 'class' => 'mw-statistics-hook', 'id' => 'mw-' . $key ] |
||
265 | ); |
||
266 | } |
||
267 | } else { |
||
268 | // Create the legacy header only once |
||
269 | if ( $return === '' ) { |
||
270 | $return .= $this->formatRowHeader( 'statistics-header-hooks' ); |
||
271 | } |
||
272 | |||
273 | // Recursively remap the legacy structure |
||
274 | $return .= $this->getOtherStats( [ 'statistics-header-hooks' => |
||
275 | [ $header => $items ] ] ); |
||
276 | } |
||
277 | } |
||
278 | |||
279 | return $return; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Format row header |
||
284 | * |
||
285 | * @param string $header |
||
286 | * @return string |
||
287 | */ |
||
288 | private function formatRowHeader( $header ) { |
||
289 | return Xml::openElement( 'tr' ) . |
||
290 | Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( $header )->parse() ) . |
||
291 | Xml::closeElement( 'tr' ); |
||
292 | } |
||
293 | |||
294 | protected function getGroupName() { |
||
295 | return 'wiki'; |
||
296 | } |
||
297 | } |
||
298 |
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.