5use InvalidArgumentException;
15use Wikimedia\Timestamp\ConvertibleTimestamp;
16use Wikimedia\Timestamp\TimestampFormat as TS;
27 private const FIRST_EDIT = 1;
28 private const LATEST_EDIT = 2;
42 private $userEditCountCache = [];
54 $this->actorNormalization = $actorNormalization;
55 $this->dbProvider = $dbProvider;
56 $this->jobQueueGroup = $jobQueueGroup;
66 if ( !$user->isRegistered() ) {
70 $cacheKey = $this->getCacheKey( $user );
71 if ( isset( $this->userEditCountCache[ $cacheKey ] ) ) {
72 return $this->userEditCountCache[ $cacheKey ];
76 $userId = $user->
getId( $wikiId );
77 $count = $this->dbProvider->getReplicaDatabase( $wikiId )->newSelectQueryBuilder()
78 ->select(
'user_editcount' )
80 ->where( [
'user_id' => $userId ] )
81 ->caller( __METHOD__ )->fetchField();
83 if ( $count ===
null ) {
88 $this->userEditCountCache[ $cacheKey ] = $count;
110 foreach ( $users as $user ) {
112 $user->isRegistered() &&
115 $userIds[] = $user->getId();
119 $userIds = array_unique( $userIds );
121 $dbr = $this->dbProvider->getReplicaDatabase();
123 foreach ( array_chunk( $userIds, 500 ) as $batch ) {
124 $rows = $dbr->newSelectQueryBuilder()
125 ->select( [
'user_id',
'user_editcount' ] )
127 ->where( [
'user_id' => $batch ] )
128 ->caller( __METHOD__ )
131 foreach ( $rows as $row ) {
132 if ( $row->user_editcount !==
null ) {
133 $key = $this->getCacheKeyByUserId( (
int)$row->user_id );
135 $this->userEditCountCache[$key] = (int)$row->user_editcount;
149 throw new LogicException( __METHOD__ .
' only supports local users' );
152 $dbr = $this->dbProvider->getReplicaDatabase();
153 $count = (int)$dbr->newSelectQueryBuilder()
154 ->select(
'COUNT(*)' )
156 ->where( [
'rev_actor' => $this->actorNormalization->findActorId( $user, $dbr ) ] )
157 ->caller( __METHOD__ )
162 'userId' => $user->getId(),
163 'editCount' => $count,
181 DeferredUpdates::addUpdate(
183 DeferredUpdates::POSTSEND
196 return $this->getUserEditTimestamp( $user, self::FIRST_EDIT, $flags );
208 return $this->getUserEditTimestamp( $user, self::LATEST_EDIT, $flags );
219 private function getUserEditTimestamp(
UserIdentity $user,
int $type,
int $flags = IDBAccessObject::READ_NORMAL ) {
223 if ( $flags & IDBAccessObject::READ_LATEST ) {
224 $db = $this->dbProvider->getPrimaryDatabase( $user->
getWikiId() );
226 $db = $this->dbProvider->getReplicaDatabase( $user->
getWikiId() );
229 $sortOrder = ( $type === self::FIRST_EDIT ) ? SelectQueryBuilder::SORT_ASC : SelectQueryBuilder::SORT_DESC;
230 $time = $db->newSelectQueryBuilder()
231 ->select(
'rev_timestamp' )
233 ->where( [
'rev_actor' => $this->actorNormalization->findActorId( $user, $db ) ] )
234 ->orderBy(
'rev_timestamp', $sortOrder )
235 ->caller( __METHOD__ )
242 return ConvertibleTimestamp::convert( TS::MW, $time );
254 $cacheKey = $this->getCacheKey( $user );
255 unset( $this->userEditCountCache[ $cacheKey ] );
266 throw new InvalidArgumentException( __METHOD__ .
' with an anonymous user' );
269 $cacheKey = $this->getCacheKey( $user );
270 $this->userEditCountCache[ $cacheKey ] = $editCount;
280 private function getCacheKey(
UserIdentity $user ): string {
281 if ( !$user->isRegistered() ) {
282 throw new InvalidArgumentException(
'Cannot prepare cache key for an anonymous user' );
287 return $this->getCacheKeyByUserId( $user->
getId( $wikiId ), $wikiId );
299 private function getCacheKeyByUserId(
301 string|
bool $wikiId = WikiAwareEntity::LOCAL
303 $isRemoteWiki = ( $wikiId !== UserIdentity::LOCAL ) && !WikiMap::isCurrentWikiId( $wikiId );
304 if ( $isRemoteWiki ) {
305 return $wikiId .
':u' . $userId;
307 return 'u' . $userId;
if(!defined('MW_SETUP_CALLBACK'))