MediaWiki master
UserEditTracker.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\User;
4
5use InvalidArgumentException;
14use Wikimedia\Timestamp\ConvertibleTimestamp;
15
24
25 private const FIRST_EDIT = 1;
26 private const LATEST_EDIT = 2;
27
28 private ActorNormalization $actorNormalization;
29 private IConnectionProvider $dbProvider;
30 private JobQueueGroup $jobQueueGroup;
31
38 private $userEditCountCache = [];
39
45 public function __construct(
46 ActorNormalization $actorNormalization,
47 IConnectionProvider $dbProvider,
48 JobQueueGroup $jobQueueGroup
49 ) {
50 $this->actorNormalization = $actorNormalization;
51 $this->dbProvider = $dbProvider;
52 $this->jobQueueGroup = $jobQueueGroup;
53 }
54
61 public function getUserEditCount( UserIdentity $user ): ?int {
62 $userId = $user->getId();
63 if ( !$userId ) {
64 return null;
65 }
66
67 $cacheKey = 'u' . $userId;
68 if ( isset( $this->userEditCountCache[ $cacheKey ] ) ) {
69 return $this->userEditCountCache[ $cacheKey ];
70 }
71
72 $count = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
73 ->select( 'user_editcount' )
74 ->from( 'user' )
75 ->where( [ 'user_id' => $userId ] )
76 ->caller( __METHOD__ )->fetchField();
77
78 if ( $count === null ) {
79 // it has not been initialized. do so.
80 $count = $this->initializeUserEditCount( $user );
81 }
82
83 $this->userEditCountCache[ $cacheKey ] = $count;
84 return $count;
85 }
86
92 public function initializeUserEditCount( UserIdentity $user ): int {
93 $dbr = $this->dbProvider->getReplicaDatabase();
94 $count = (int)$dbr->newSelectQueryBuilder()
95 ->select( 'COUNT(*)' )
96 ->from( 'revision' )
97 ->where( [ 'rev_actor' => $this->actorNormalization->findActorId( $user, $dbr ) ] )
98 ->caller( __METHOD__ )
99 ->fetchField();
100
101 // Defer updating the edit count via a job (T259719)
102 $this->jobQueueGroup->push( new UserEditCountInitJob( [
103 'userId' => $user->getId(),
104 'editCount' => $count,
105 ] ) );
106
107 return $count;
108 }
109
116 public function incrementUserEditCount( UserIdentity $user ) {
117 if ( !$user->getId() ) {
118 // Can't store editcount without user row (i.e. unregistered)
119 return;
120 }
121
122 DeferredUpdates::addUpdate(
123 new UserEditCountUpdate( $user, 1 ),
124 DeferredUpdates::POSTSEND
125 );
126 }
127
136 public function getFirstEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) {
137 return $this->getUserEditTimestamp( $user, self::FIRST_EDIT, $flags );
138 }
139
148 public function getLatestEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) {
149 return $this->getUserEditTimestamp( $user, self::LATEST_EDIT, $flags );
150 }
151
160 private function getUserEditTimestamp( UserIdentity $user, int $type, int $flags = IDBAccessObject::READ_NORMAL ) {
161 if ( !$user->getId() ) {
162 return false;
163 }
164 $db = DBAccessObjectUtils::getDBFromRecency( $this->dbProvider, $flags );
165
166 $sortOrder = ( $type === self::FIRST_EDIT ) ? SelectQueryBuilder::SORT_ASC : SelectQueryBuilder::SORT_DESC;
167 $time = $db->newSelectQueryBuilder()
168 ->select( 'rev_timestamp' )
169 ->from( 'revision' )
170 ->where( [ 'rev_actor' => $this->actorNormalization->findActorId( $user, $db ) ] )
171 ->orderBy( 'rev_timestamp', $sortOrder )
172 ->caller( __METHOD__ )
173 ->fetchField();
174
175 if ( !$time ) {
176 return false; // no edits
177 }
178
179 return ConvertibleTimestamp::convert( TS_MW, $time );
180 }
181
186 public function clearUserEditCache( UserIdentity $user ) {
187 $userId = $user->getId();
188 if ( !$userId ) {
189 return;
190 }
191
192 $cacheKey = 'u' . $userId;
193 unset( $this->userEditCountCache[ $cacheKey ] );
194 }
195
202 public function setCachedUserEditCount( UserIdentity $user, int $editCount ) {
203 $userId = $user->getId();
204 if ( !$userId ) {
205 throw new InvalidArgumentException( __METHOD__ . ' with an anonymous user' );
206 }
207
208 $cacheKey = 'u' . $userId;
209 $this->userEditCountCache[ $cacheKey ] = $editCount;
210 }
211
212}
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.
Service for dealing with the actor table.
Interface for objects representing user identity.
getId( $wikiId=self::LOCAL)
Provide primary and replica IDatabase connections.
Interface for database access objects.