]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/SiteStats.php
MediaWiki 1.15.4-scripts
[autoinstalls/mediawiki.git] / includes / SiteStats.php
1 <?php
2
3 /**
4  * Static accessor class for site_stats and related things
5  */
6 class SiteStats {
7         static $row, $loaded = false;
8         static $admins, $jobs;
9         static $pageCount = array();
10         static $groupMemberCounts = array();
11
12         static function recache() {
13                 self::load( true );
14         }
15
16         static function load( $recache = false ) {
17                 if ( self::$loaded && !$recache ) {
18                         return;
19                 }
20
21                 self::$row = self::loadAndLazyInit();
22
23                 # This code is somewhat schema-agnostic, because I'm changing it in a minor release -- TS
24                 if ( !isset( self::$row->ss_total_pages ) && self::$row->ss_total_pages == -1 ) {
25                         # Update schema
26                         $u = new SiteStatsUpdate( 0, 0, 0 );
27                         $u->doUpdate();
28                         $dbr = wfGetDB( DB_SLAVE );
29                         self::$row = $dbr->selectRow( 'site_stats', '*', false, __METHOD__ );
30                 }
31
32                 self::$loaded = true;
33         }
34
35         static function loadAndLazyInit() {
36                 wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
37                 $row = self::doLoad( wfGetDB( DB_SLAVE ) );
38
39                 if( !self::isSane( $row ) ) {
40                         // Might have just been initialized during this request? Underflow?
41                         wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" );
42                         $row = self::doLoad( wfGetDB( DB_MASTER ) );
43                 }
44
45                 if( !self::isSane( $row ) ) {
46                         // Normally the site_stats table is initialized at install time.
47                         // Some manual construction scenarios may leave the table empty or
48                         // broken, however, for instance when importing from a dump into a
49                         // clean schema with mwdumper.
50                         wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
51
52                         global $IP;
53                         require_once "$IP/maintenance/initStats.inc";
54
55                         ob_start();
56                         wfInitStats();
57                         ob_end_clean();
58
59                         $row = self::doLoad( wfGetDB( DB_MASTER ) );
60                 }
61
62                 if( !self::isSane( $row ) ) {
63                         wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O\n" );
64                 }
65                 return $row;
66         }
67
68         static function doLoad( $db ) {
69                 return $db->selectRow( 'site_stats', '*', false, __METHOD__ );
70         }
71
72         static function views() {
73                 self::load();
74                 return self::$row->ss_total_views;
75         }
76
77         static function edits() {
78                 self::load();
79                 return self::$row->ss_total_edits;
80         }
81
82         static function articles() {
83                 self::load();
84                 return self::$row->ss_good_articles;
85         }
86
87         static function pages() {
88                 self::load();
89                 return self::$row->ss_total_pages;
90         }
91
92         static function users() {
93                 self::load();
94                 return self::$row->ss_users;
95         }
96         
97         static function activeUsers() {
98                 self::load();
99                 return self::$row->ss_active_users;
100         }
101
102         static function images() {
103                 self::load();
104                 return self::$row->ss_images;
105         }
106
107         /**
108          * @deprecated Use self::numberingroup('sysop') instead
109          */
110         static function admins() {
111                 wfDeprecated(__METHOD__);
112                 return self::numberingroup('sysop');
113         }
114         
115         /**
116          * Find the number of users in a given user group.
117          * @param string $group Name of group
118          * @return int
119          */
120         static function numberingroup($group) {
121                 if ( !isset( self::$groupMemberCounts[$group] ) ) {
122                         global $wgMemc;
123                         $key = wfMemcKey( 'SiteStats', 'groupcounts', $group );
124                         $hit = $wgMemc->get( $key );
125                         if ( !$hit ) {
126                                 $dbr = wfGetDB( DB_SLAVE );
127                                 $hit = $dbr->selectField( 'user_groups', 'COUNT(*)', 
128                                                                                                         array( 'ug_group' => $group ), __METHOD__ );
129                                 $wgMemc->set( $key, $hit, 3600 );
130                         }
131                         self::$groupMemberCounts[$group] = $hit;
132                 }
133                 return self::$groupMemberCounts[$group];                
134         }
135
136         static function jobs() {
137                 if ( !isset( self::$jobs ) ) {
138                         $dbr = wfGetDB( DB_SLAVE );
139                         self::$jobs = $dbr->estimateRowCount('job');
140                         /* Zero rows still do single row read for row that doesn't exist, but people are annoyed by that */
141                         if (self::$jobs == 1) {
142                                 self::$jobs = 0;
143                         }
144                 }
145                 return self::$jobs;
146         }
147
148         static function pagesInNs( $ns ) {
149                 wfProfileIn( __METHOD__ );
150                 if( !isset( self::$pageCount[$ns] ) ) {
151                         $dbr = wfGetDB( DB_SLAVE );
152                         $pageCount[$ns] = (int)$dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $ns ), __METHOD__ );
153                 }
154                 wfProfileOut( __METHOD__ );
155                 return $pageCount[$ns];
156         }
157
158         /** Is the provided row of site stats sane, or should it be regenerated? */
159         private static function isSane( $row ) {
160                 if(
161                         $row === false
162                         or $row->ss_total_pages < $row->ss_good_articles
163                         or $row->ss_total_edits < $row->ss_total_pages
164                         or $row->ss_users       < $row->ss_admins
165                 ) {
166                         return false;
167                 }
168                 // Now check for underflow/overflow
169                 foreach( array( 'total_views', 'total_edits', 'good_articles',
170                 'total_pages', 'users', 'admins', 'images' ) as $member ) {
171                         if(
172                                    $row->{"ss_$member"} > 2000000000
173                                 or $row->{"ss_$member"} < 0
174                         ) {
175                                 return false;
176                         }
177                 }
178                 return true;
179         }
180 }
181
182
183 /**
184  *
185  */
186 class SiteStatsUpdate {
187
188         var $mViews, $mEdits, $mGood, $mPages, $mUsers;
189
190         function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
191                 $this->mViews = $views;
192                 $this->mEdits = $edits;
193                 $this->mGood = $good;
194                 $this->mPages = $pages;
195                 $this->mUsers = $users;
196         }
197
198         function appendUpdate( &$sql, $field, $delta ) {
199                 if ( $delta ) {
200                         if ( $sql ) {
201                                 $sql .= ',';
202                         }
203                         if ( $delta < 0 ) {
204                                 $sql .= "$field=$field-1";
205                         } else {
206                                 $sql .= "$field=$field+1";
207                         }
208                 }
209         }
210
211         function doUpdate() {
212                 $fname = 'SiteStatsUpdate::doUpdate';
213                 $dbw = wfGetDB( DB_MASTER );
214
215                 $updates = '';
216
217                 $this->appendUpdate( $updates, 'ss_total_views', $this->mViews );
218                 $this->appendUpdate( $updates, 'ss_total_edits', $this->mEdits );
219                 $this->appendUpdate( $updates, 'ss_good_articles', $this->mGood );
220                 $this->appendUpdate( $updates, 'ss_total_pages', $this->mPages );
221                 $this->appendUpdate( $updates, 'ss_users', $this->mUsers );
222
223                 if ( $updates ) {
224                         $site_stats = $dbw->tableName( 'site_stats' );
225                         $sql = "UPDATE $site_stats SET $updates";
226
227                         # Need a separate transaction because this a global lock
228                         $dbw->begin();
229                         $dbw->query( $sql, $fname );
230                         $dbw->commit();
231                 }
232         }
233         
234         public static function cacheUpdate( $dbw ) {
235                 $dbr = wfGetDB( DB_SLAVE, array( 'SpecialStatistics', 'vslow') );
236                 # Get non-bot users than did some recent action other than making accounts.
237                 # If account creation is included, the number gets inflated ~20+ fold on enwiki.
238                 $activeUsers = $dbr->selectField( 'recentchanges', 'COUNT( DISTINCT rc_user_text )',
239                         array( 'rc_user != 0', 'rc_bot' => 0, "rc_log_type != 'newusers' OR rc_log_type IS NULL" ),
240                         __METHOD__ );
241                 $dbw->update( 'site_stats', 
242                         array( 'ss_active_users' => intval($activeUsers) ),
243                         array( 'ss_row_id' => 1 ), __METHOD__
244                 );
245         }
246 }