[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/ -> SiteStats.php (source)

   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  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1