MediaWiki  master
UserEditTracker.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\User;
4 
8 use InvalidArgumentException;
9 use JobQueueGroup;
13 use Wikimedia\Timestamp\ConvertibleTimestamp;
14 
23 
24  private const FIRST_EDIT = 1;
25  private const LATEST_EDIT = 2;
26 
28  private $actorMigration;
29 
31  private $loadBalancer;
32 
34  private $jobQueueGroup;
35 
42  private $userEditCountCache = [];
43 
49  public function __construct(
50  ActorMigration $actorMigration,
51  ILoadBalancer $loadBalancer,
52  JobQueueGroup $jobQueueGroup
53  ) {
54  $this->actorMigration = $actorMigration;
55  $this->loadBalancer = $loadBalancer;
56  $this->jobQueueGroup = $jobQueueGroup;
57  }
58 
65  public function getUserEditCount( UserIdentity $user ): ?int {
66  if ( !$user->isRegistered() ) {
67  return null;
68  }
69 
70  $userId = $user->getId();
71  $cacheKey = 'u' . $userId;
72 
73  if ( isset( $this->userEditCountCache[ $cacheKey ] ) ) {
74  return $this->userEditCountCache[ $cacheKey ];
75  }
76 
77  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
78  $count = $dbr->selectField(
79  'user',
80  'user_editcount',
81  [ 'user_id' => $userId ],
82  __METHOD__
83  );
84 
85  if ( $count === null ) {
86  // it has not been initialized. do so.
87  $count = $this->initializeUserEditCount( $user );
88  }
89 
90  $this->userEditCountCache[ $cacheKey ] = $count;
91  return $count;
92  }
93 
99  public function initializeUserEditCount( UserIdentity $user ): int {
100  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
101  $actorWhere = $this->actorMigration->getWhere( $dbr, 'rev_user', $user );
102 
103  $count = (int)$dbr->selectField(
104  [ 'revision' ] + $actorWhere['tables'],
105  'COUNT(*)',
106  [ $actorWhere['conds'] ],
107  __METHOD__,
108  [],
109  $actorWhere['joins']
110  );
111 
112  // Defer updating the edit count via a job (T259719)
113  $this->jobQueueGroup->push( new UserEditCountInitJob( [
114  'userId' => $user->getId(),
115  'editCount' => $count,
116  ] ) );
117 
118  return $count;
119  }
120 
127  public function incrementUserEditCount( UserIdentity $user ) {
128  if ( !$user->isRegistered() ) {
129  // Anonymous users don't have edit counts
130  return;
131  }
132 
134  new UserEditCountUpdate( $user, 1 ),
135  DeferredUpdates::POSTSEND
136  );
137  }
138 
147  public function getFirstEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) {
148  return $this->getUserEditTimestamp( $user, self::FIRST_EDIT, $flags );
149  }
150 
159  public function getLatestEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) {
160  return $this->getUserEditTimestamp( $user, self::LATEST_EDIT, $flags );
161  }
162 
171  private function getUserEditTimestamp( UserIdentity $user, int $type, int $flags = IDBAccessObject::READ_NORMAL ) {
172  if ( !$user->isRegistered() ) {
173  return false;
174  }
175  list( $index ) = DBAccessObjectUtils::getDBOptions( $flags );
176 
177  $db = $this->loadBalancer->getConnectionRef( $index );
178  $actorWhere = $this->actorMigration->getWhere( $db, 'rev_user', $user );
179 
180  $sortOrder = ( $type === self::FIRST_EDIT ) ? 'ASC' : 'DESC';
181  $time = $db->selectField(
182  [ 'revision' ] + $actorWhere['tables'],
183  'rev_timestamp',
184  [ $actorWhere['conds'] ],
185  __METHOD__,
186  [ 'ORDER BY' => "rev_timestamp $sortOrder" ],
187  $actorWhere['joins']
188  );
189 
190  if ( !$time ) {
191  return false; // no edits
192  }
193 
194  return ConvertibleTimestamp::convert( TS_MW, $time );
195  }
196 
201  public function clearUserEditCache( UserIdentity $user ) {
202  if ( !$user->isRegistered() ) {
203  return;
204  }
205 
206  $userId = $user->getId();
207  $cacheKey = 'u' . $userId;
208 
209  unset( $this->userEditCountCache[ $cacheKey ] );
210  }
211 
218  public function setCachedUserEditCount( UserIdentity $user, int $editCount ) {
219  if ( !$user->isRegistered() ) {
220  throw new InvalidArgumentException( __METHOD__ . ' with an anonymous user' );
221  }
222 
223  $userId = $user->getId();
224  $cacheKey = 'u' . $userId;
225 
226  $this->userEditCountCache[ $cacheKey ] = $editCount;
227  }
228 
229 }
if(!defined('MW_SETUP_CALLBACK'))
Definition: WebStart.php:88
Helper class for DAO classes.
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
Class for managing the deferral of updates within the scope of a PHP script invocation.
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the pending update queue for execution at the appropriate time.
Class to handle enqueueing of background jobs.
This is not intended to be a long-term part of MediaWiki; it will be deprecated and removed once acto...
Track info about user edit counts and timings.
getLatestEditTimestamp(UserIdentity $user, int $flags=IDBAccessObject::READ_NORMAL)
Get the user's latest edit timestamp.
__construct(ActorMigration $actorMigration, ILoadBalancer $loadBalancer, JobQueueGroup $jobQueueGroup)
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)
setCachedUserEditCount(UserIdentity $user, int $editCount)
Job that initializes an user's edit count.
Handles increment the edit count for a given set of users.
Interface for database access objects.
Interface for objects representing user identity.
getId( $wikiId=self::LOCAL)
This class is a delegate to ILBFactory for a given database cluster.
const DB_REPLICA
Definition: defines.php:26