MediaWiki  REL1_23
SiteStats.php
Go to the documentation of this file.
00001 <?php
00026 class SiteStats {
00027     static $row, $loaded = false;
00028     static $jobs;
00029     static $pageCount = array();
00030     static $groupMemberCounts = array();
00031 
00032     static function recache() {
00033         self::load( true );
00034     }
00035 
00039     static function load( $recache = false ) {
00040         if ( self::$loaded && !$recache ) {
00041             return;
00042         }
00043 
00044         self::$row = self::loadAndLazyInit();
00045 
00046         # This code is somewhat schema-agnostic, because I'm changing it in a minor release -- TS
00047         if ( !isset( self::$row->ss_total_pages ) && self::$row->ss_total_pages == -1 ) {
00048             # Update schema
00049             $u = new SiteStatsUpdate( 0, 0, 0 );
00050             $u->doUpdate();
00051             self::$row = self::doLoad( wfGetDB( DB_SLAVE ) );
00052         }
00053 
00054         self::$loaded = true;
00055     }
00056 
00060     static function loadAndLazyInit() {
00061         wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
00062         $row = self::doLoad( wfGetDB( DB_SLAVE ) );
00063 
00064         if ( !self::isSane( $row ) ) {
00065             // Might have just been initialized during this request? Underflow?
00066             wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" );
00067             $row = self::doLoad( wfGetDB( DB_MASTER ) );
00068         }
00069 
00070         if ( !self::isSane( $row ) ) {
00071             // Normally the site_stats table is initialized at install time.
00072             // Some manual construction scenarios may leave the table empty or
00073             // broken, however, for instance when importing from a dump into a
00074             // clean schema with mwdumper.
00075             wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
00076 
00077             SiteStatsInit::doAllAndCommit( wfGetDB( DB_SLAVE ) );
00078 
00079             $row = self::doLoad( wfGetDB( DB_MASTER ) );
00080         }
00081 
00082         if ( !self::isSane( $row ) ) {
00083             wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O\n" );
00084         }
00085         return $row;
00086     }
00087 
00092     static function doLoad( $db ) {
00093         return $db->selectRow( 'site_stats', array(
00094                 'ss_row_id',
00095                 'ss_total_views',
00096                 'ss_total_edits',
00097                 'ss_good_articles',
00098                 'ss_total_pages',
00099                 'ss_users',
00100                 'ss_active_users',
00101                 'ss_images',
00102             ), false, __METHOD__ );
00103     }
00104 
00108     static function views() {
00109         self::load();
00110         return self::$row->ss_total_views;
00111     }
00112 
00116     static function edits() {
00117         self::load();
00118         return self::$row->ss_total_edits;
00119     }
00120 
00124     static function articles() {
00125         self::load();
00126         return self::$row->ss_good_articles;
00127     }
00128 
00132     static function pages() {
00133         self::load();
00134         return self::$row->ss_total_pages;
00135     }
00136 
00140     static function users() {
00141         self::load();
00142         return self::$row->ss_users;
00143     }
00144 
00148     static function activeUsers() {
00149         self::load();
00150         return self::$row->ss_active_users;
00151     }
00152 
00156     static function images() {
00157         self::load();
00158         return self::$row->ss_images;
00159     }
00160 
00166     static function numberingroup( $group ) {
00167         if ( !isset( self::$groupMemberCounts[$group] ) ) {
00168             global $wgMemc;
00169             $key = wfMemcKey( 'SiteStats', 'groupcounts', $group );
00170             $hit = $wgMemc->get( $key );
00171             if ( !$hit ) {
00172                 $dbr = wfGetDB( DB_SLAVE );
00173                 $hit = $dbr->selectField(
00174                     'user_groups',
00175                     'COUNT(*)',
00176                     array( 'ug_group' => $group ),
00177                     __METHOD__
00178                 );
00179                 $wgMemc->set( $key, $hit, 3600 );
00180             }
00181             self::$groupMemberCounts[$group] = $hit;
00182         }
00183         return self::$groupMemberCounts[$group];
00184     }
00185 
00189     static function jobs() {
00190         if ( !isset( self::$jobs ) ) {
00191             $dbr = wfGetDB( DB_SLAVE );
00192             self::$jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
00193             /* Zero rows still do single row read for row that doesn't exist, but people are annoyed by that */
00194             if ( self::$jobs == 1 ) {
00195                 self::$jobs = 0;
00196             }
00197         }
00198         return self::$jobs;
00199     }
00200 
00206     static function pagesInNs( $ns ) {
00207         wfProfileIn( __METHOD__ );
00208         if ( !isset( self::$pageCount[$ns] ) ) {
00209             $dbr = wfGetDB( DB_SLAVE );
00210             self::$pageCount[$ns] = (int)$dbr->selectField(
00211                 'page',
00212                 'COUNT(*)',
00213                 array( 'page_namespace' => $ns ),
00214                 __METHOD__
00215             );
00216         }
00217         wfProfileOut( __METHOD__ );
00218         return self::$pageCount[$ns];
00219     }
00220 
00230     private static function isSane( $row ) {
00231         if ( $row === false
00232             || $row->ss_total_pages < $row->ss_good_articles
00233             || $row->ss_total_edits < $row->ss_total_pages
00234         ) {
00235             return false;
00236         }
00237         // Now check for underflow/overflow
00238         foreach ( array(
00239             'ss_total_views',
00240             'ss_total_edits',
00241             'ss_good_articles',
00242             'ss_total_pages',
00243             'ss_users',
00244             'ss_images',
00245         ) as $member ) {
00246             if ( $row->$member > 2000000000 || $row->$member < 0 ) {
00247                 return false;
00248             }
00249         }
00250         return true;
00251     }
00252 }
00253 
00257 class SiteStatsInit {
00258 
00259     // Database connection
00260     private $db;
00261 
00262     // Various stats
00263     private $mEdits = null, $mArticles = null, $mPages = null;
00264     private $mUsers = null, $mViews = null, $mFiles = null;
00265 
00272     public function __construct( $database = false ) {
00273         if ( $database instanceof DatabaseBase ) {
00274             $this->db = $database;
00275         } else {
00276             $this->db = wfGetDB( $database ? DB_MASTER : DB_SLAVE );
00277         }
00278     }
00279 
00284     public function edits() {
00285         $this->mEdits = $this->db->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
00286         $this->mEdits += $this->db->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
00287         return $this->mEdits;
00288     }
00289 
00294     public function articles() {
00295         global $wgArticleCountMethod;
00296 
00297         $tables = array( 'page' );
00298         $conds = array(
00299             'page_namespace' => MWNamespace::getContentNamespaces(),
00300             'page_is_redirect' => 0,
00301         );
00302 
00303         if ( $wgArticleCountMethod == 'link' ) {
00304             $tables[] = 'pagelinks';
00305             $conds[] = 'pl_from=page_id';
00306         } elseif ( $wgArticleCountMethod == 'comma' ) {
00307             // To make a correct check for this, we would need, for each page,
00308             // to load the text, maybe uncompress it, maybe decode it and then
00309             // check if there's one comma.
00310             // But one thing we are sure is that if the page is empty, it can't
00311             // contain a comma :)
00312             $conds[] = 'page_len > 0';
00313         }
00314 
00315         $this->mArticles = $this->db->selectField( $tables, 'COUNT(DISTINCT page_id)',
00316             $conds, __METHOD__ );
00317         return $this->mArticles;
00318     }
00319 
00324     public function pages() {
00325         $this->mPages = $this->db->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
00326         return $this->mPages;
00327     }
00328 
00333     public function users() {
00334         $this->mUsers = $this->db->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
00335         return $this->mUsers;
00336     }
00337 
00342     public function views() {
00343         $this->mViews = $this->db->selectField( 'page', 'SUM(page_counter)', '', __METHOD__ );
00344         return $this->mViews;
00345     }
00346 
00351     public function files() {
00352         $this->mFiles = $this->db->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
00353         return $this->mFiles;
00354     }
00355 
00367     public static function doAllAndCommit( $database, array $options = array() ) {
00368         $options += array( 'update' => false, 'views' => true, 'activeUsers' => false );
00369 
00370         // Grab the object and count everything
00371         $counter = new SiteStatsInit( $database );
00372 
00373         $counter->edits();
00374         $counter->articles();
00375         $counter->pages();
00376         $counter->users();
00377         $counter->files();
00378 
00379         // Only do views if we don't want to not count them
00380         if ( $options['views'] ) {
00381             $counter->views();
00382         }
00383 
00384         $counter->refresh();
00385 
00386         // Count active users if need be
00387         if ( $options['activeUsers'] ) {
00388             SiteStatsUpdate::cacheUpdate( wfGetDB( DB_MASTER ) );
00389         }
00390     }
00391 
00396     public function refresh() {
00397         $values = array(
00398             'ss_row_id' => 1,
00399             'ss_total_edits' => ( $this->mEdits === null ? $this->edits() : $this->mEdits ),
00400             'ss_good_articles' => ( $this->mArticles === null ? $this->articles() : $this->mArticles ),
00401             'ss_total_pages' => ( $this->mPages === null ? $this->pages() : $this->mPages ),
00402             'ss_users' => ( $this->mUsers === null ? $this->users() : $this->mUsers ),
00403             'ss_images' => ( $this->mFiles === null ? $this->files() : $this->mFiles ),
00404         ) + (
00405             $this->mViews ? array( 'ss_total_views' => $this->mViews ) : array()
00406         );
00407 
00408         $dbw = wfGetDB( DB_MASTER );
00409         $dbw->upsert( 'site_stats', $values, array( 'ss_row_id' ), $values, __METHOD__ );
00410     }
00411 }