MediaWiki  master
SiteStats.php
Go to the documentation of this file.
1 <?php
24 
25 use JobQueueError;
28 use stdClass;
32 
36 class SiteStats {
38  private static $row;
39 
43  public static function unload() {
44  self::$row = null;
45  }
46 
47  protected static function load() {
48  if ( self::$row === null ) {
49  self::$row = self::loadAndLazyInit();
50  }
51  }
52 
56  protected static function loadAndLazyInit() {
57  $config = MediaWikiServices::getInstance()->getMainConfig();
58 
59  $lb = self::getLB();
60  $dbr = $lb->getConnectionRef( DB_REPLICA );
61  wfDebug( __METHOD__ . ": reading site_stats from replica DB" );
62  $row = self::doLoadFromDB( $dbr );
63 
64  if ( !self::isRowSensible( $row ) && $lb->hasOrMadeRecentPrimaryChanges() ) {
65  // Might have just been initialized during this request? Underflow?
66  wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB" );
67  $row = self::doLoadFromDB( $lb->getConnectionRef( DB_PRIMARY ) );
68  }
69 
70  if ( !self::isRowSensible( $row ) ) {
71  if ( $config->get( MainConfigNames::MiserMode ) ) {
72  // Start off with all zeroes, assuming that this is a new wiki or any
73  // repopulations where done manually via script.
75  } else {
76  // Normally the site_stats table is initialized at install time.
77  // Some manual construction scenarios may leave the table empty or
78  // broken, however, for instance when importing from a dump into a
79  // clean schema with mwdumper.
80  wfDebug( __METHOD__ . ": initializing damaged or missing site_stats" );
82  }
83 
84  $row = self::doLoadFromDB( $lb->getConnectionRef( DB_PRIMARY ) );
85  }
86 
87  return $row;
88  }
89 
93  public static function edits() {
94  self::load();
95 
96  return (int)self::$row->ss_total_edits;
97  }
98 
102  public static function articles() {
103  self::load();
104 
105  return (int)self::$row->ss_good_articles;
106  }
107 
111  public static function pages() {
112  self::load();
113 
114  return (int)self::$row->ss_total_pages;
115  }
116 
120  public static function users() {
121  self::load();
122 
123  return (int)self::$row->ss_users;
124  }
125 
129  public static function activeUsers() {
130  self::load();
131 
132  return (int)self::$row->ss_active_users;
133  }
134 
138  public static function images() {
139  self::load();
140 
141  return (int)self::$row->ss_images;
142  }
143 
149  public static function numberingroup( $group ) {
150  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
151  $fname = __METHOD__;
152 
153  return $cache->getWithSetCallback(
154  $cache->makeKey( 'SiteStats', 'groupcounts', $group ),
155  $cache::TTL_HOUR,
156  static function ( $oldValue, &$ttl, array &$setOpts ) use ( $group, $fname ) {
157  $dbr = self::getLB()->getConnectionRef( DB_REPLICA );
158  $setOpts += Database::getCacheSetOptions( $dbr );
159  return (int)$dbr->newSelectQueryBuilder()
160  ->select( 'COUNT(*)' )
161  ->from( 'user_groups' )
162  ->where(
163  [
164  'ug_group' => $group,
165  'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
166  ]
167  )
168  ->caller( $fname )
169  ->fetchField();
170  },
171  [ 'pcTTL' => $cache::TTL_PROC_LONG ]
172  );
173  }
174 
179  public static function jobs() {
180  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
181 
182  return $cache->getWithSetCallback(
183  $cache->makeKey( 'SiteStats', 'jobscount' ),
184  $cache::TTL_MINUTE,
185  static function ( $oldValue, &$ttl, array &$setOpts ) {
186  try {
187  $jobs = array_sum( MediaWikiServices::getInstance()->getJobQueueGroup()->getQueueSizes() );
188  } catch ( JobQueueError $e ) {
189  $jobs = 0;
190  }
191  return $jobs;
192  },
193  [ 'pcTTL' => $cache::TTL_PROC_LONG ]
194  );
195  }
196 
201  public static function pagesInNs( $ns ) {
202  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
203  $fname = __METHOD__;
204 
205  return $cache->getWithSetCallback(
206  $cache->makeKey( 'SiteStats', 'page-in-namespace', $ns ),
207  $cache::TTL_HOUR,
208  static function ( $oldValue, &$ttl, array &$setOpts ) use ( $ns, $fname ) {
209  $dbr = self::getLB()->getConnectionRef( DB_REPLICA );
210  $setOpts += Database::getCacheSetOptions( $dbr );
211 
212  return (int)$dbr->newSelectQueryBuilder()
213  ->select( 'COUNT(*)' )
214  ->from( 'page' )
215  ->where( [ 'page_namespace' => $ns ] )
216  ->caller( $fname )->fetchField();
217  },
218  [ 'pcTTL' => $cache::TTL_PROC_LONG ]
219  );
220  }
221 
225  public static function selectFields() {
226  return [
227  'ss_total_edits',
228  'ss_good_articles',
229  'ss_total_pages',
230  'ss_users',
231  'ss_active_users',
232  'ss_images',
233  ];
234  }
235 
240  private static function doLoadFromDB( IDatabase $db ) {
241  $fields = self::selectFields();
242  $rows = $db->newSelectQueryBuilder()
243  ->select( $fields )
244  ->from( 'site_stats' )
245  ->caller( __METHOD__ )
246  ->fetchResultSet();
247  if ( !$rows->numRows() ) {
248  return false;
249  }
250  $finalRow = new stdClass();
251  foreach ( $rows as $row ) {
252  foreach ( $fields as $field ) {
253  $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 
297  private static function getLB() {
298  return MediaWikiServices::getInstance()->getDBLoadBalancer();
299  }
300 }
301 
305 class_alias( SiteStats::class, 'SiteStats' );
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.
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
static doPlaceholderInit()
Insert a dummy row with all zeroes if no row is present.
static doAllAndCommit( $database, array $options=[])
Do all updates and commit them.
Static accessor class for site_stats and related things.
Definition: SiteStats.php:36
static unload()
Trigger a reload next time a field is accessed.
Definition: SiteStats.php:43
static jobs()
Total number of jobs in the job queue.
Definition: SiteStats.php:179
static numberingroup( $group)
Find the number of users in a given user group.
Definition: SiteStats.php:149
static getCacheSetOptions(?IReadableDatabase ... $dbs)
Merge the result of getSessionLagStatus() for several DBs using the most pessimistic values to estima...
Definition: Database.php:2755
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:36
This class is a delegate to ILBFactory for a given database cluster.
newSelectQueryBuilder()
Create an empty SelectQueryBuilder which can be used to run queries against this connection.
const DB_REPLICA
Definition: defines.php:26
const DB_PRIMARY
Definition: defines.php:28