MediaWiki  master
SiteStats.php
Go to the documentation of this file.
1 <?php
28 
32 class SiteStats {
34  private static $row;
35 
39  public static function unload() {
40  self::$row = null;
41  }
42 
43  protected static function load() {
44  if ( self::$row === null ) {
45  self::$row = self::loadAndLazyInit();
46  }
47  }
48 
52  protected static function loadAndLazyInit() {
53  $config = MediaWikiServices::getInstance()->getMainConfig();
54 
55  $lb = self::getLB();
56  $dbr = $lb->getConnectionRef( DB_REPLICA );
57  wfDebug( __METHOD__ . ": reading site_stats from replica DB" );
58  $row = self::doLoadFromDB( $dbr );
59 
60  if ( !self::isRowSensible( $row ) && $lb->hasOrMadeRecentPrimaryChanges() ) {
61  // Might have just been initialized during this request? Underflow?
62  wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB" );
63  $row = self::doLoadFromDB( $lb->getConnectionRef( DB_PRIMARY ) );
64  }
65 
66  if ( !self::isRowSensible( $row ) ) {
67  if ( $config->get( MainConfigNames::MiserMode ) ) {
68  // Start off with all zeroes, assuming that this is a new wiki or any
69  // repopulations where done manually via script.
71  } else {
72  // Normally the site_stats table is initialized at install time.
73  // Some manual construction scenarios may leave the table empty or
74  // broken, however, for instance when importing from a dump into a
75  // clean schema with mwdumper.
76  wfDebug( __METHOD__ . ": initializing damaged or missing site_stats" );
78  }
79 
80  $row = self::doLoadFromDB( $lb->getConnectionRef( DB_PRIMARY ) );
81  }
82 
83  if ( !self::isRowSensible( $row ) ) {
84  wfDebug( __METHOD__ . ": site_stats persistently nonsensical o_O" );
85  // Always return a row-like object
86  $row = self::salvageIncorrectRow( $row );
87  }
88 
89  return $row;
90  }
91 
95  public static function edits() {
96  self::load();
97 
98  return (int)self::$row->ss_total_edits;
99  }
100 
104  public static function articles() {
105  self::load();
106 
107  return (int)self::$row->ss_good_articles;
108  }
109 
113  public static function pages() {
114  self::load();
115 
116  return (int)self::$row->ss_total_pages;
117  }
118 
122  public static function users() {
123  self::load();
124 
125  return (int)self::$row->ss_users;
126  }
127 
131  public static function activeUsers() {
132  self::load();
133 
134  return (int)self::$row->ss_active_users;
135  }
136 
140  public static function images() {
141  self::load();
142 
143  return (int)self::$row->ss_images;
144  }
145 
151  public static function numberingroup( $group ) {
152  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
153  $fname = __METHOD__;
154 
155  return $cache->getWithSetCallback(
156  $cache->makeKey( 'SiteStats', 'groupcounts', $group ),
157  $cache::TTL_HOUR,
158  function ( $oldValue, &$ttl, array &$setOpts ) use ( $group, $fname ) {
159  $dbr = self::getLB()->getConnectionRef( DB_REPLICA );
160  $setOpts += Database::getCacheSetOptions( $dbr );
161  return (int)$dbr->newSelectQueryBuilder()
162  ->select( 'COUNT(*)' )
163  ->from( 'user_groups' )
164  ->where(
165  [
166  'ug_group' => $group,
167  'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
168  ]
169  )
170  ->caller( $fname )
171  ->fetchField();
172  },
173  [ 'pcTTL' => $cache::TTL_PROC_LONG ]
174  );
175  }
176 
181  public static function jobs() {
182  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
183 
184  return $cache->getWithSetCallback(
185  $cache->makeKey( 'SiteStats', 'jobscount' ),
186  $cache::TTL_MINUTE,
187  static function ( $oldValue, &$ttl, array &$setOpts ) {
188  try {
189  $jobs = array_sum( MediaWikiServices::getInstance()->getJobQueueGroup()->getQueueSizes() );
190  } catch ( JobQueueError $e ) {
191  $jobs = 0;
192  }
193  return $jobs;
194  },
195  [ 'pcTTL' => $cache::TTL_PROC_LONG ]
196  );
197  }
198 
203  public static function pagesInNs( $ns ) {
204  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
205  $fname = __METHOD__;
206 
207  return $cache->getWithSetCallback(
208  $cache->makeKey( 'SiteStats', 'page-in-namespace', $ns ),
209  $cache::TTL_HOUR,
210  function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns, $fname ) {
211  $dbr = self::getLB()->getConnectionRef( DB_REPLICA );
212  $setOpts += Database::getCacheSetOptions( $dbr );
213 
214  return (int)$dbr->selectField(
215  'page',
216  'COUNT(*)',
217  [ 'page_namespace' => $ns ],
218  $fname
219  );
220  },
221  [ 'pcTTL' => $cache::TTL_PROC_LONG ]
222  );
223  }
224 
228  public static function selectFields() {
229  return [
230  'ss_total_edits',
231  'ss_good_articles',
232  'ss_total_pages',
233  'ss_users',
234  'ss_active_users',
235  'ss_images',
236  ];
237  }
238 
243  private static function doLoadFromDB( IDatabase $db ) {
244  $fields = self::selectFields();
245  $rows = $db->newSelectQueryBuilder()
246  ->select( $fields )
247  ->from( 'site_stats' )
248  ->caller( __METHOD__ )
249  ->fetchResultSet();
250  $finalRow = new stdClass();
251  foreach ( $rows as $row ) {
252  foreach ( $fields as $field ) {
253  $finalRow->$field = $finalRow->$field ?? 0;
254  if ( $row->$field ) {
255  $finalRow->$field += $row->$field;
256  }
257  }
258 
259  }
260  return $finalRow;
261  }
262 
271  private static function isRowSensible( $row ) {
272  if ( $row === false
273  || $row->ss_total_pages < $row->ss_good_articles
274  || $row->ss_total_edits < $row->ss_total_pages
275  ) {
276  return false;
277  }
278  // Now check for underflow/overflow
279  foreach ( [
280  'ss_total_edits',
281  'ss_good_articles',
282  'ss_total_pages',
283  'ss_users',
284  'ss_images',
285  ] as $member ) {
286  if ( $row->$member < 0 ) {
287  return false;
288  }
289  }
290 
291  return true;
292  }
293 
298  private static function salvageIncorrectRow( $row ) {
299  $map = $row ? (array)$row : [];
300  // Fill in any missing values with zero
301  $map += array_fill_keys( self::selectFields(), 0 );
302  // Convert negative values to zero
303  foreach ( $map as $field => $value ) {
304  $map[$field] = max( 0, $value );
305  }
306 
307  return (object)$row;
308  }
309 
313  private static function getLB() {
314  return MediaWikiServices::getInstance()->getDBLoadBalancer();
315  }
316 }
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
static doAllAndCommit( $database, array $options=[])
Do all updates and commit them.
static doPlaceholderInit()
Insert a dummy row with all zeroes if no row is present.
Static accessor class for site_stats and related things.
Definition: SiteStats.php:32
static articles()
Definition: SiteStats.php:104
static jobs()
Total number of jobs in the job queue.
Definition: SiteStats.php:181
static pagesInNs( $ns)
Definition: SiteStats.php:203
static images()
Definition: SiteStats.php:140
static load()
Definition: SiteStats.php:43
static selectFields()
Definition: SiteStats.php:228
static edits()
Definition: SiteStats.php:95
static loadAndLazyInit()
Definition: SiteStats.php:52
static users()
Definition: SiteStats.php:122
static pages()
Definition: SiteStats.php:113
static unload()
Trigger a reload next time a field is accessed.
Definition: SiteStats.php:39
static numberingroup( $group)
Find the number of users in a given user group.
Definition: SiteStats.php:151
static activeUsers()
Definition: SiteStats.php:131
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:39
newSelectQueryBuilder()
Create an empty SelectQueryBuilder which can be used to run queries against this connection.
Create and track the database connections and transactions for a given database cluster.
$cache
Definition: mcc.php:33
const DB_REPLICA
Definition: defines.php:26
const DB_PRIMARY
Definition: defines.php:28