actorNormalization = $actorNormalization; $this->dbProvider = $dbProvider; $this->jobQueueGroup = $jobQueueGroup; } /** * Get a user's edit count from the user_editcount field, falling back to initialize * * @param UserIdentity $user * @return int|null Null for anonymous users */ public function getUserEditCount( UserIdentity $user ): ?int { $userId = $user->getId(); if ( !$userId ) { return null; } $cacheKey = 'u' . $userId; if ( isset( $this->userEditCountCache[ $cacheKey ] ) ) { return $this->userEditCountCache[ $cacheKey ]; } $count = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder() ->select( 'user_editcount' ) ->from( 'user' ) ->where( [ 'user_id' => $userId ] ) ->caller( __METHOD__ )->fetchField(); if ( $count === null ) { // it has not been initialized. do so. $count = $this->initializeUserEditCount( $user ); } $this->userEditCountCache[ $cacheKey ] = $count; return $count; } /** * @internal For use in UserEditCountUpdate class * @param UserIdentity $user * @return int */ public function initializeUserEditCount( UserIdentity $user ): int { $dbr = $this->dbProvider->getReplicaDatabase(); $count = (int)$dbr->newSelectQueryBuilder() ->select( 'COUNT(*)' ) ->from( 'revision' ) ->where( [ 'rev_actor' => $this->actorNormalization->findActorId( $user, $dbr ) ] ) ->caller( __METHOD__ ) ->fetchField(); // Defer updating the edit count via a job (T259719) $this->jobQueueGroup->push( new UserEditCountInitJob( [ 'userId' => $user->getId(), 'editCount' => $count, ] ) ); return $count; } /** * Schedule a job to increase a user's edit count * * @since 1.37 * @param UserIdentity $user */ public function incrementUserEditCount( UserIdentity $user ) { if ( !$user->getId() ) { // Can't store editcount without user row (i.e. unregistered) return; } DeferredUpdates::addUpdate( new UserEditCountUpdate( $user, 1 ), DeferredUpdates::POSTSEND ); } /** * Get the user's first edit timestamp * * @param UserIdentity $user * @param int $flags bit field, see IDBAccessObject::READ_XXX * @return string|false Timestamp of first edit, or false for non-existent/anonymous user * accounts. */ public function getFirstEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) { return $this->getUserEditTimestamp( $user, self::FIRST_EDIT, $flags ); } /** * Get the user's latest edit timestamp * * @param UserIdentity $user * @param int $flags bit field, see IDBAccessObject::READ_XXX * @return string|false Timestamp of latest edit, or false for non-existent/anonymous user * accounts. */ public function getLatestEditTimestamp( UserIdentity $user, int $flags = IDBAccessObject::READ_NORMAL ) { return $this->getUserEditTimestamp( $user, self::LATEST_EDIT, $flags ); } /** * Get the timestamp of a user's edit, either their first or latest * * @param UserIdentity $user * @param int $type either self::FIRST_EDIT or ::LATEST_EDIT * @param int $flags bit field, see IDBAccessObject::READ_XXX * @return string|false Timestamp of edit, or false for non-existent/anonymous user accounts. */ private function getUserEditTimestamp( UserIdentity $user, int $type, int $flags = IDBAccessObject::READ_NORMAL ) { if ( !$user->getId() ) { return false; } $db = DBAccessObjectUtils::getDBFromRecency( $this->dbProvider, $flags ); $sortOrder = ( $type === self::FIRST_EDIT ) ? SelectQueryBuilder::SORT_ASC : SelectQueryBuilder::SORT_DESC; $time = $db->newSelectQueryBuilder() ->select( 'rev_timestamp' ) ->from( 'revision' ) ->where( [ 'rev_actor' => $this->actorNormalization->findActorId( $user, $db ) ] ) ->orderBy( 'rev_timestamp', $sortOrder ) ->caller( __METHOD__ ) ->fetchField(); if ( !$time ) { return false; // no edits } return ConvertibleTimestamp::convert( TS_MW, $time ); } /** * @internal For use by User::clearInstanceCache() * @param UserIdentity $user */ public function clearUserEditCache( UserIdentity $user ) { $userId = $user->getId(); if ( !$userId ) { return; } $cacheKey = 'u' . $userId; unset( $this->userEditCountCache[ $cacheKey ] ); } /** * @internal For use by User::loadFromRow() and tests * @param UserIdentity $user * @param int $editCount * @throws InvalidArgumentException If the user is not registered */ public function setCachedUserEditCount( UserIdentity $user, int $editCount ) { $userId = $user->getId(); if ( !$userId ) { throw new InvalidArgumentException( __METHOD__ . ' with an anonymous user' ); } $cacheKey = 'u' . $userId; $this->userEditCountCache[ $cacheKey ] = $editCount; } }