MediaWiki master
UserEditTracker.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\User;
4
7use InvalidArgumentException;
14use Wikimedia\Timestamp\ConvertibleTimestamp;
15
23
24 private const FIRST_EDIT = 1;
25 private const LATEST_EDIT = 2;
26
27 private ActorNormalization $actorNormalization;
28 private IConnectionProvider $dbProvider;
29 private JobQueueGroup $jobQueueGroup;
30
37 private $userEditCountCache = [];
38
44 public function __construct(
45 ActorNormalization $actorNormalization,
46 IConnectionProvider $dbProvider,
47 JobQueueGroup $jobQueueGroup
48 ) {
49 $this->actorNormalization = $actorNormalization;
50 $this->dbProvider = $dbProvider;
51 $this->jobQueueGroup = $jobQueueGroup;
52 }
53
60 public function getUserEditCount( UserIdentity $user ): ?int {
61 $userId = $user->getId();
62 if ( !$userId ) {
63 return null;
64 }
65
66 $cacheKey = 'u' . $userId;
67 if ( isset( $this->userEditCountCache[ $cacheKey ] ) ) {
68 return $this->userEditCountCache[ $cacheKey ];
69 }
70
71 $count = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
72 ->select( 'user_editcount' )
73 ->from( 'user' )
74 ->where( [ 'user_id' => $userId ] )
75 ->caller( __METHOD__ )->fetchField();
76
77 if ( $count === null ) {
78 // it has not been initialized. do so.
79 $count = $this->initializeUserEditCount( $user );
80 }
81
82 $this->userEditCountCache[ $cacheKey ] = $count;
83 return $count;
84 }
85
91 public function initializeUserEditCount( UserIdentity $user ): int {
92 $dbr = $this->dbProvider->getReplicaDatabase();
93 $count = (int)$dbr->newSelectQueryBuilder()
94 ->select( 'COUNT(*)' )
95 ->from( 'revision' )
96 ->where( [ 'rev_actor' => $this->actorNormalization->findActorId( $user, $dbr ) ] )
97 ->caller( __METHOD__ )
98 ->fetchField();
99
100 // Defer updating the edit count via a job (T259719)
101 $this->jobQueueGroup->push( new UserEditCountInitJob( [
102 'userId' => $user->getId(),
103 'editCount' => $count,
104 ] ) );
105
106 return $count;
107 }
108
115 public function incrementUserEditCount( UserIdentity $user ) {
116 if ( !$user->getId() ) {
117 // Can't store editcount without user row (i.e. unregistered)
118 return;
119 }
120
121 DeferredUpdates::addUpdate(
122 new UserEditCountUpdate( $user, 1 ),
123 DeferredUpdates::POSTSEND
124 );
125 }
126
135 public function getFirstEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) {
136 return $this->getUserEditTimestamp( $user, self::FIRST_EDIT, $flags );
137 }
138
147 public function getLatestEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) {
148 return $this->getUserEditTimestamp( $user, self::LATEST_EDIT, $flags );
149 }
150
159 private function getUserEditTimestamp( UserIdentity $user, int $type, int $flags = IDBAccessObject::READ_NORMAL ) {
160 if ( !$user->getId() ) {
161 return false;
162 }
163 $db = DBAccessObjectUtils::getDBFromRecency( $this->dbProvider, $flags );
164
165 $sortOrder = ( $type === self::FIRST_EDIT ) ? SelectQueryBuilder::SORT_ASC : SelectQueryBuilder::SORT_DESC;
166 $time = $db->newSelectQueryBuilder()
167 ->select( 'rev_timestamp' )
168 ->from( 'revision' )
169 ->where( [ 'rev_actor' => $this->actorNormalization->findActorId( $user, $db ) ] )
170 ->orderBy( 'rev_timestamp', $sortOrder )
171 ->caller( __METHOD__ )
172 ->fetchField();
173
174 if ( !$time ) {
175 return false; // no edits
176 }
177
178 return ConvertibleTimestamp::convert( TS_MW, $time );
179 }
180
185 public function clearUserEditCache( UserIdentity $user ) {
186 $userId = $user->getId();
187 if ( !$userId ) {
188 return;
189 }
190
191 $cacheKey = 'u' . $userId;
192 unset( $this->userEditCountCache[ $cacheKey ] );
193 }
194
201 public function setCachedUserEditCount( UserIdentity $user, int $editCount ) {
202 $userId = $user->getId();
203 if ( !$userId ) {
204 throw new InvalidArgumentException( __METHOD__ . ' with an anonymous user' );
205 }
206
207 $cacheKey = 'u' . $userId;
208 $this->userEditCountCache[ $cacheKey ] = $editCount;
209 }
210
211}
Helper class for DAO classes.
Handle enqueueing of background jobs.
Defer callable updates to run later in the PHP process.
Handles increment the edit count for a given set of users.
Track info about user edit counts and timings.
getLatestEditTimestamp(UserIdentity $user, int $flags=IDBAccessObject::READ_NORMAL)
Get the user's latest edit timestamp.
getUserEditCount(UserIdentity $user)
Get a user's edit count from the user_editcount field, falling back to initialize.
getFirstEditTimestamp(UserIdentity $user, int $flags=IDBAccessObject::READ_NORMAL)
Get the user's first edit timestamp.
incrementUserEditCount(UserIdentity $user)
Schedule a job to increase a user's edit count.
initializeUserEditCount(UserIdentity $user)
clearUserEditCache(UserIdentity $user)
__construct(ActorNormalization $actorNormalization, IConnectionProvider $dbProvider, JobQueueGroup $jobQueueGroup)
setCachedUserEditCount(UserIdentity $user, int $editCount)
Job that initializes an user's edit count.
Build SELECT queries with a fluent interface.
Interface for database access objects.
Service for dealing with the actor table.
Interface for objects representing user identity.
getId( $wikiId=self::LOCAL)
Provide primary and replica IDatabase connections.
Utility class for bot passwords.