00001 <?php
00002
00006 class SiteStats {
00007 static $row, $loaded = false;
00008 static $admins, $jobs;
00009 static $pageCount = array();
00010 static $groupMemberCounts = array();
00011
00012 static function recache() {
00013 self::load( true );
00014 }
00015
00016 static function load( $recache = false ) {
00017 if ( self::$loaded && !$recache ) {
00018 return;
00019 }
00020
00021 self::$row = self::loadAndLazyInit();
00022
00023 # This code is somewhat schema-agnostic, because I'm changing it in a minor release -- TS
00024 if ( !isset( self::$row->ss_total_pages ) && self::$row->ss_total_pages == -1 ) {
00025 # Update schema
00026 $u = new SiteStatsUpdate( 0, 0, 0 );
00027 $u->doUpdate();
00028 $dbr = wfGetDB( DB_SLAVE );
00029 self::$row = $dbr->selectRow( 'site_stats', '*', false, __METHOD__ );
00030 }
00031
00032 self::$loaded = true;
00033 }
00034
00035 static function loadAndLazyInit() {
00036 wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
00037 $row = self::doLoad( wfGetDB( DB_SLAVE ) );
00038
00039 if( !self::isSane( $row ) ) {
00040
00041 wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" );
00042 $row = self::doLoad( wfGetDB( DB_MASTER ) );
00043 }
00044
00045 if( !self::isSane( $row ) ) {
00046
00047
00048
00049
00050 wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
00051
00052 global $IP;
00053 require_once "$IP/maintenance/initStats.inc";
00054
00055 ob_start();
00056 wfInitStats();
00057 ob_end_clean();
00058
00059 $row = self::doLoad( wfGetDB( DB_MASTER ) );
00060 }
00061
00062 if( !self::isSane( $row ) ) {
00063 wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O\n" );
00064 }
00065 return $row;
00066 }
00067
00068 static function doLoad( $db ) {
00069 return $db->selectRow( 'site_stats', '*', false, __METHOD__ );
00070 }
00071
00072 static function views() {
00073 self::load();
00074 return self::$row->ss_total_views;
00075 }
00076
00077 static function edits() {
00078 self::load();
00079 return self::$row->ss_total_edits;
00080 }
00081
00082 static function articles() {
00083 self::load();
00084 return self::$row->ss_good_articles;
00085 }
00086
00087 static function pages() {
00088 self::load();
00089 return self::$row->ss_total_pages;
00090 }
00091
00092 static function users() {
00093 self::load();
00094 return self::$row->ss_users;
00095 }
00096
00097 static function activeUsers() {
00098 self::load();
00099 return self::$row->ss_active_users;
00100 }
00101
00102 static function images() {
00103 self::load();
00104 return self::$row->ss_images;
00105 }
00106
00110 static function admins() {
00111 wfDeprecated(__METHOD__);
00112 return self::numberingroup('sysop');
00113 }
00114
00120 static function numberingroup($group) {
00121 if ( !isset( self::$groupMemberCounts[$group] ) ) {
00122 global $wgMemc;
00123 $key = wfMemcKey( 'SiteStats', 'groupcounts', $group );
00124 $hit = $wgMemc->get( $key );
00125 if ( !$hit ) {
00126 $dbr = wfGetDB( DB_SLAVE );
00127 $hit = $dbr->selectField( 'user_groups', 'COUNT(*)',
00128 array( 'ug_group' => $group ), __METHOD__ );
00129 $wgMemc->set( $key, $hit, 3600 );
00130 }
00131 self::$groupMemberCounts[$group] = $hit;
00132 }
00133 return self::$groupMemberCounts[$group];
00134 }
00135
00136 static function jobs() {
00137 if ( !isset( self::$jobs ) ) {
00138 $dbr = wfGetDB( DB_SLAVE );
00139 self::$jobs = $dbr->estimateRowCount('job');
00140
00141 if (self::$jobs == 1) {
00142 self::$jobs = 0;
00143 }
00144 }
00145 return self::$jobs;
00146 }
00147
00148 static function pagesInNs( $ns ) {
00149 wfProfileIn( __METHOD__ );
00150 if( !isset( self::$pageCount[$ns] ) ) {
00151 $dbr = wfGetDB( DB_SLAVE );
00152 $pageCount[$ns] = (int)$dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $ns ), __METHOD__ );
00153 }
00154 wfProfileOut( __METHOD__ );
00155 return $pageCount[$ns];
00156 }
00157
00159 private static function isSane( $row ) {
00160 if(
00161 $row === false
00162 or $row->ss_total_pages < $row->ss_good_articles
00163 or $row->ss_total_edits < $row->ss_total_pages
00164 or $row->ss_users < $row->ss_admins
00165 ) {
00166 return false;
00167 }
00168
00169 foreach( array( 'total_views', 'total_edits', 'good_articles',
00170 'total_pages', 'users', 'admins', 'images' ) as $member ) {
00171 if(
00172 $row->{"ss_$member"} > 2000000000
00173 or $row->{"ss_$member"} < 0
00174 ) {
00175 return false;
00176 }
00177 }
00178 return true;
00179 }
00180 }
00181
00182
00186 class SiteStatsUpdate {
00187
00188 var $mViews, $mEdits, $mGood, $mPages, $mUsers;
00189
00190 function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
00191 $this->mViews = $views;
00192 $this->mEdits = $edits;
00193 $this->mGood = $good;
00194 $this->mPages = $pages;
00195 $this->mUsers = $users;
00196 }
00197
00198 function appendUpdate( &$sql, $field, $delta ) {
00199 if ( $delta ) {
00200 if ( $sql ) {
00201 $sql .= ',';
00202 }
00203 if ( $delta < 0 ) {
00204 $sql .= "$field=$field-1";
00205 } else {
00206 $sql .= "$field=$field+1";
00207 }
00208 }
00209 }
00210
00211 function doUpdate() {
00212 $fname = 'SiteStatsUpdate::doUpdate';
00213 $dbw = wfGetDB( DB_MASTER );
00214
00215 $updates = '';
00216
00217 $this->appendUpdate( $updates, 'ss_total_views', $this->mViews );
00218 $this->appendUpdate( $updates, 'ss_total_edits', $this->mEdits );
00219 $this->appendUpdate( $updates, 'ss_good_articles', $this->mGood );
00220 $this->appendUpdate( $updates, 'ss_total_pages', $this->mPages );
00221 $this->appendUpdate( $updates, 'ss_users', $this->mUsers );
00222
00223 if ( $updates ) {
00224 $site_stats = $dbw->tableName( 'site_stats' );
00225 $sql = "UPDATE $site_stats SET $updates";
00226
00227 # Need a separate transaction because this a global lock
00228 $dbw->begin();
00229 $dbw->query( $sql, $fname );
00230 $dbw->commit();
00231 }
00232 }
00233
00234 public static function cacheUpdate( $dbw ) {
00235 $dbr = wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow') );
00236 # Get non-bot users than did some recent action other than making accounts.
00237 # If account creation is included, the number gets inflated ~20+ fold on enwiki.
00238 $activeUsers = $dbr->selectField( 'recentchanges', 'COUNT( DISTINCT rc_user_text )',
00239 array( 'rc_user != 0', 'rc_bot' => 0, "rc_log_type != 'newusers' OR rc_log_type IS NULL" ),
00240 __METHOD__ );
00241 $dbw->update( 'site_stats',
00242 array( 'ss_active_users' => intval($activeUsers) ),
00243 array( 'ss_row_id' => 1 ), __METHOD__
00244 );
00245 }
00246 }