Provide command to adjust phpunit.xml for code coverage
[lhc/web/wiklou.git] / includes / deferred / SiteStatsUpdate.php
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20 use MediaWiki\MediaWikiServices;
21 use Wikimedia\Assert\Assert;
22 use Wikimedia\Rdbms\IDatabase;
23
24 /**
25 * Class for handling updates to the site_stats table
26 */
27 class SiteStatsUpdate implements DeferrableUpdate, MergeableUpdate {
28 /** @var int */
29 protected $edits = 0;
30 /** @var int */
31 protected $pages = 0;
32 /** @var int */
33 protected $articles = 0;
34 /** @var int */
35 protected $users = 0;
36 /** @var int */
37 protected $images = 0;
38
39 /** @var string[] Map of (table column => counter type) */
40 private static $counters = [
41 'ss_total_edits' => 'edits',
42 'ss_total_pages' => 'pages',
43 'ss_good_articles' => 'articles',
44 'ss_users' => 'users',
45 'ss_images' => 'images'
46 ];
47
48 // @todo deprecate this constructor
49 function __construct( $views, $edits, $good, $pages = 0, $users = 0 ) {
50 $this->edits = $edits;
51 $this->articles = $good;
52 $this->pages = $pages;
53 $this->users = $users;
54 }
55
56 public function merge( MergeableUpdate $update ) {
57 /** @var SiteStatsUpdate $update */
58 Assert::parameterType( __CLASS__, $update, '$update' );
59
60 foreach ( self::$counters as $field ) {
61 $this->$field += $update->$field;
62 }
63 }
64
65 /**
66 * @param int[] $deltas Map of (counter type => integer delta)
67 * @return SiteStatsUpdate
68 * @throws UnexpectedValueException
69 */
70 public static function factory( array $deltas ) {
71 $update = new self( 0, 0, 0 );
72
73 foreach ( $deltas as $name => $unused ) {
74 if ( !in_array( $name, self::$counters ) ) { // T187585
75 throw new UnexpectedValueException( __METHOD__ . ": no field called '$name'" );
76 }
77 }
78
79 foreach ( self::$counters as $field ) {
80 $update->$field = $deltas[$field] ?? 0;
81 }
82
83 return $update;
84 }
85
86 public function doUpdate() {
87 $services = MediaWikiServices::getInstance();
88 $stats = $services->getStatsdDataFactory();
89
90 $deltaByType = [];
91 foreach ( self::$counters as $type ) {
92 $delta = $this->$type;
93 if ( $delta !== 0 ) {
94 $stats->updateCount( "site.$type", $delta );
95 }
96 $deltaByType[$type] = $delta;
97 }
98
99 ( new AutoCommitUpdate(
100 $services->getDBLoadBalancer()->getConnectionRef( DB_MASTER ),
101 __METHOD__,
102 function ( IDatabase $dbw, $fname ) use ( $deltaByType ) {
103 $set = [];
104 foreach ( self::$counters as $column => $type ) {
105 $delta = (int)$deltaByType[$type];
106 if ( $delta > 0 ) {
107 $set[] = "$column=$column+" . abs( $delta );
108 } elseif ( $delta < 0 ) {
109 $set[] = "$column=$column-" . abs( $delta );
110 }
111 }
112
113 if ( $set ) {
114 $dbw->update( 'site_stats', $set, [ 'ss_row_id' => 1 ], $fname );
115 }
116 }
117 ) )->doUpdate();
118
119 // Invalidate cache used by parser functions
120 SiteStats::unload();
121 }
122
123 /**
124 * @param IDatabase $dbw
125 * @return bool|mixed
126 */
127 public static function cacheUpdate( IDatabase $dbw ) {
128 $services = MediaWikiServices::getInstance();
129 $config = $services->getMainConfig();
130
131 $dbr = $services->getDBLoadBalancer()->getConnectionRef( DB_REPLICA, 'vslow' );
132 # Get non-bot users than did some recent action other than making accounts.
133 # If account creation is included, the number gets inflated ~20+ fold on enwiki.
134 $rcQuery = RecentChange::getQueryInfo();
135 $activeUsers = $dbr->selectField(
136 $rcQuery['tables'],
137 'COUNT( DISTINCT ' . $rcQuery['fields']['rc_user_text'] . ' )',
138 [
139 'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Exclude external (Wikidata)
140 ActorMigration::newMigration()->isNotAnon( $rcQuery['fields']['rc_user'] ),
141 'rc_bot' => 0,
142 'rc_log_type != ' . $dbr->addQuotes( 'newusers' ) . ' OR rc_log_type IS NULL',
143 'rc_timestamp >= ' . $dbr->addQuotes(
144 $dbr->timestamp( time() - $config->get( 'ActiveUserDays' ) * 24 * 3600 ) ),
145 ],
146 __METHOD__,
147 [],
148 $rcQuery['joins']
149 );
150 $dbw->update(
151 'site_stats',
152 [ 'ss_active_users' => intval( $activeUsers ) ],
153 [ 'ss_row_id' => 1 ],
154 __METHOD__
155 );
156
157 // Invalid cache used by parser functions
158 SiteStats::unload();
159
160 return $activeUsers;
161 }
162 }