[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Accessors and mutators for the site-wide 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 */ 22 23 /** 24 * Static accessor class for site_stats and related things 25 */ 26 class SiteStats { 27 /** @var bool|ResultWrapper */ 28 private static $row; 29 30 /** @var bool */ 31 private static $loaded = false; 32 33 /** @var int */ 34 private static $jobs; 35 36 /** @var int[] */ 37 private static $pageCount = array(); 38 39 /** @var int[] */ 40 private static $groupMemberCounts = array(); 41 42 static function recache() { 43 self::load( true ); 44 } 45 46 /** 47 * @param bool $recache 48 */ 49 static function load( $recache = false ) { 50 if ( self::$loaded && !$recache ) { 51 return; 52 } 53 54 self::$row = self::loadAndLazyInit(); 55 56 # This code is somewhat schema-agnostic, because I'm changing it in a minor release -- TS 57 if ( !isset( self::$row->ss_total_pages ) && self::$row->ss_total_pages == -1 ) { 58 # Update schema 59 $u = new SiteStatsUpdate( 0, 0, 0 ); 60 $u->doUpdate(); 61 self::$row = self::doLoad( wfGetDB( DB_SLAVE ) ); 62 } 63 64 self::$loaded = true; 65 } 66 67 /** 68 * @return bool|ResultWrapper 69 */ 70 static function loadAndLazyInit() { 71 wfDebug( __METHOD__ . ": reading site_stats from slave\n" ); 72 $row = self::doLoad( wfGetDB( DB_SLAVE ) ); 73 74 if ( !self::isSane( $row ) ) { 75 // Might have just been initialized during this request? Underflow? 76 wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" ); 77 $row = self::doLoad( wfGetDB( DB_MASTER ) ); 78 } 79 80 if ( !self::isSane( $row ) ) { 81 // Normally the site_stats table is initialized at install time. 82 // Some manual construction scenarios may leave the table empty or 83 // broken, however, for instance when importing from a dump into a 84 // clean schema with mwdumper. 85 wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" ); 86 87 SiteStatsInit::doAllAndCommit( wfGetDB( DB_SLAVE ) ); 88 89 $row = self::doLoad( wfGetDB( DB_MASTER ) ); 90 } 91 92 if ( !self::isSane( $row ) ) { 93 wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O\n" ); 94 } 95 return $row; 96 } 97 98 /** 99 * @param DatabaseBase $db 100 * @return bool|ResultWrapper 101 */ 102 static function doLoad( $db ) { 103 return $db->selectRow( 'site_stats', array( 104 'ss_row_id', 105 'ss_total_views', 106 'ss_total_edits', 107 'ss_good_articles', 108 'ss_total_pages', 109 'ss_users', 110 'ss_active_users', 111 'ss_images', 112 ), false, __METHOD__ ); 113 } 114 115 /** 116 * @return int 117 */ 118 static function views() { 119 self::load(); 120 return self::$row->ss_total_views; 121 } 122 123 /** 124 * @return int 125 */ 126 static function edits() { 127 self::load(); 128 return self::$row->ss_total_edits; 129 } 130 131 /** 132 * @return int 133 */ 134 static function articles() { 135 self::load(); 136 return self::$row->ss_good_articles; 137 } 138 139 /** 140 * @return int 141 */ 142 static function pages() { 143 self::load(); 144 return self::$row->ss_total_pages; 145 } 146 147 /** 148 * @return int 149 */ 150 static function users() { 151 self::load(); 152 return self::$row->ss_users; 153 } 154 155 /** 156 * @return int 157 */ 158 static function activeUsers() { 159 self::load(); 160 return self::$row->ss_active_users; 161 } 162 163 /** 164 * @return int 165 */ 166 static function images() { 167 self::load(); 168 return self::$row->ss_images; 169 } 170 171 /** 172 * Find the number of users in a given user group. 173 * @param string $group Name of group 174 * @return int 175 */ 176 static function numberingroup( $group ) { 177 if ( !isset( self::$groupMemberCounts[$group] ) ) { 178 global $wgMemc; 179 $key = wfMemcKey( 'SiteStats', 'groupcounts', $group ); 180 $hit = $wgMemc->get( $key ); 181 if ( !$hit ) { 182 $dbr = wfGetDB( DB_SLAVE ); 183 $hit = $dbr->selectField( 184 'user_groups', 185 'COUNT(*)', 186 array( 'ug_group' => $group ), 187 __METHOD__ 188 ); 189 $wgMemc->set( $key, $hit, 3600 ); 190 } 191 self::$groupMemberCounts[$group] = $hit; 192 } 193 return self::$groupMemberCounts[$group]; 194 } 195 196 /** 197 * @return int 198 */ 199 static function jobs() { 200 if ( !isset( self::$jobs ) ) { 201 $dbr = wfGetDB( DB_SLAVE ); 202 self::$jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() ); 203 /** 204 * Zero rows still do single row read for row that doesn't exist, 205 * but people are annoyed by that 206 */ 207 if ( self::$jobs == 1 ) { 208 self::$jobs = 0; 209 } 210 } 211 return self::$jobs; 212 } 213 214 /** 215 * @param int $ns 216 * 217 * @return int 218 */ 219 static function pagesInNs( $ns ) { 220 wfProfileIn( __METHOD__ ); 221 if ( !isset( self::$pageCount[$ns] ) ) { 222 $dbr = wfGetDB( DB_SLAVE ); 223 self::$pageCount[$ns] = (int)$dbr->selectField( 224 'page', 225 'COUNT(*)', 226 array( 'page_namespace' => $ns ), 227 __METHOD__ 228 ); 229 } 230 wfProfileOut( __METHOD__ ); 231 return self::$pageCount[$ns]; 232 } 233 234 /** 235 * Is the provided row of site stats sane, or should it be regenerated? 236 * 237 * Checks only fields which are filled by SiteStatsInit::refresh. 238 * 239 * @param bool|object $row 240 * 241 * @return bool 242 */ 243 private static function isSane( $row ) { 244 if ( $row === false 245 || $row->ss_total_pages < $row->ss_good_articles 246 || $row->ss_total_edits < $row->ss_total_pages 247 ) { 248 return false; 249 } 250 // Now check for underflow/overflow 251 foreach ( array( 252 'ss_total_views', 253 'ss_total_edits', 254 'ss_good_articles', 255 'ss_total_pages', 256 'ss_users', 257 'ss_images', 258 ) as $member ) { 259 if ( $row->$member > 2000000000 || $row->$member < 0 ) { 260 return false; 261 } 262 } 263 return true; 264 } 265 } 266 267 /** 268 * Class designed for counting of stats. 269 */ 270 class SiteStatsInit { 271 272 // Database connection 273 private $db; 274 275 // Various stats 276 private $mEdits = null, $mArticles = null, $mPages = null; 277 private $mUsers = null, $mViews = null, $mFiles = null; 278 279 /** 280 * Constructor 281 * @param bool|DatabaseBase $database 282 * - Boolean: whether to use the master DB 283 * - DatabaseBase: database connection to use 284 */ 285 public function __construct( $database = false ) { 286 if ( $database instanceof DatabaseBase ) { 287 $this->db = $database; 288 } else { 289 $this->db = wfGetDB( $database ? DB_MASTER : DB_SLAVE ); 290 } 291 } 292 293 /** 294 * Count the total number of edits 295 * @return int 296 */ 297 public function edits() { 298 $this->mEdits = $this->db->selectField( 'revision', 'COUNT(*)', '', __METHOD__ ); 299 $this->mEdits += $this->db->selectField( 'archive', 'COUNT(*)', '', __METHOD__ ); 300 return $this->mEdits; 301 } 302 303 /** 304 * Count pages in article space(s) 305 * @return int 306 */ 307 public function articles() { 308 global $wgArticleCountMethod; 309 310 $tables = array( 'page' ); 311 $conds = array( 312 'page_namespace' => MWNamespace::getContentNamespaces(), 313 'page_is_redirect' => 0, 314 ); 315 316 if ( $wgArticleCountMethod == 'link' ) { 317 $tables[] = 'pagelinks'; 318 $conds[] = 'pl_from=page_id'; 319 } elseif ( $wgArticleCountMethod == 'comma' ) { 320 // To make a correct check for this, we would need, for each page, 321 // to load the text, maybe uncompress it, maybe decode it and then 322 // check if there's one comma. 323 // But one thing we are sure is that if the page is empty, it can't 324 // contain a comma :) 325 $conds[] = 'page_len > 0'; 326 } 327 328 $this->mArticles = $this->db->selectField( $tables, 'COUNT(DISTINCT page_id)', 329 $conds, __METHOD__ ); 330 return $this->mArticles; 331 } 332 333 /** 334 * Count total pages 335 * @return int 336 */ 337 public function pages() { 338 $this->mPages = $this->db->selectField( 'page', 'COUNT(*)', '', __METHOD__ ); 339 return $this->mPages; 340 } 341 342 /** 343 * Count total users 344 * @return int 345 */ 346 public function users() { 347 $this->mUsers = $this->db->selectField( 'user', 'COUNT(*)', '', __METHOD__ ); 348 return $this->mUsers; 349 } 350 351 /** 352 * Count views 353 * @return int 354 */ 355 public function views() { 356 $this->mViews = $this->db->selectField( 'page', 'SUM(page_counter)', '', __METHOD__ ); 357 return $this->mViews; 358 } 359 360 /** 361 * Count total files 362 * @return int 363 */ 364 public function files() { 365 $this->mFiles = $this->db->selectField( 'image', 'COUNT(*)', '', __METHOD__ ); 366 return $this->mFiles; 367 } 368 369 /** 370 * Do all updates and commit them. More or less a replacement 371 * for the original initStats, but without output. 372 * 373 * @param DatabaseBase|bool $database 374 * - Boolean: whether to use the master DB 375 * - DatabaseBase: database connection to use 376 * @param array $options Array of options, may contain the following values 377 * - views Boolean: when true, do not update the number of page views (default: true) 378 * - activeUsers Boolean: whether to update the number of active users (default: false) 379 */ 380 public static function doAllAndCommit( $database, array $options = array() ) { 381 $options += array( 'update' => false, 'views' => true, 'activeUsers' => false ); 382 383 // Grab the object and count everything 384 $counter = new SiteStatsInit( $database ); 385 386 $counter->edits(); 387 $counter->articles(); 388 $counter->pages(); 389 $counter->users(); 390 $counter->files(); 391 392 // Only do views if we don't want to not count them 393 if ( $options['views'] ) { 394 $counter->views(); 395 } 396 397 $counter->refresh(); 398 399 // Count active users if need be 400 if ( $options['activeUsers'] ) { 401 SiteStatsUpdate::cacheUpdate( wfGetDB( DB_MASTER ) ); 402 } 403 } 404 405 /** 406 * Refresh site_stats. If you want ss_total_views to be updated, be sure to 407 * call views() first. 408 */ 409 public function refresh() { 410 $values = array( 411 'ss_row_id' => 1, 412 'ss_total_edits' => ( $this->mEdits === null ? $this->edits() : $this->mEdits ), 413 'ss_good_articles' => ( $this->mArticles === null ? $this->articles() : $this->mArticles ), 414 'ss_total_pages' => ( $this->mPages === null ? $this->pages() : $this->mPages ), 415 'ss_users' => ( $this->mUsers === null ? $this->users() : $this->mUsers ), 416 'ss_images' => ( $this->mFiles === null ? $this->files() : $this->mFiles ), 417 ) + ( 418 $this->mViews ? array( 'ss_total_views' => $this->mViews ) : array() 419 ); 420 421 $dbw = wfGetDB( DB_MASTER ); 422 $dbw->upsert( 'site_stats', $values, array( 'ss_row_id' ), $values, __METHOD__ ); 423 } 424 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |