wiki.techinc.nl/includes/user/UserEditTracker.php
Umherirrender bc5cb7ae64 phan: Enable redundant_condition_detection
Remove duplicate casts
Suppress false positives

Bug: T248438
Change-Id: I2f89664a4bcd3b39b15e7cf850adda2f0c90ae6f
2020-07-01 20:13:07 +00:00

193 lines
4.5 KiB
PHP

<?php
namespace MediaWiki\User;
use ActorMigration;
use InvalidArgumentException;
use MWTimestamp;
use Wikimedia\Rdbms\ILoadBalancer;
/**
* Track info about user edit counts and timings
*
* @since 1.35
*
* @author DannyS712
*/
class UserEditTracker {
private const FIRST_EDIT = 1;
private const LATEST_EDIT = 2;
/** @var ActorMigration */
private $actorMigration;
/** @var ILoadBalancer */
private $loadBalancer;
/**
* @var array
*
* Mapping of user id to edit count for caching
* To avoid using non-sequential numerical keys, keys are in the form: `u⧼user id⧽`
*/
private $userEditCountCache = [];
/**
* @param ActorMigration $actorMigration
* @param ILoadBalancer $loadBalancer
*/
public function __construct(
ActorMigration $actorMigration,
ILoadBalancer $loadBalancer
) {
$this->actorMigration = $actorMigration;
$this->loadBalancer = $loadBalancer;
}
/**
* Get a user's edit count from the user_editcount field, falling back to initialize
*
* @param UserIdentity $user
* @return int
* @throws InvalidArgumentException if the user id is invalid
*/
public function getUserEditCount( UserIdentity $user ) : int {
if ( !$user->getId() ) {
throw new InvalidArgumentException(
__METHOD__ . ' requires Users with ids set'
);
}
$userId = $user->getId();
$cacheKey = 'u' . (string)$userId;
if ( isset( $this->userEditCountCache[ $cacheKey ] ) ) {
return $this->userEditCountCache[ $cacheKey ];
}
$dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
$count = $dbr->selectField(
'user',
'user_editcount',
[ 'user_id' => $userId ],
__METHOD__
);
if ( $count === null ) {
// it has not been initialized. do so.
$count = $this->initializeUserEditCount( $user );
}
$this->userEditCountCache[ $cacheKey ] = $count;
return $count;
}
/**
* @internal public only for use in UserEditCountUpdate
*
* @param UserIdentity $user
* @return int
*/
public function initializeUserEditCount( UserIdentity $user ) : int {
$dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
$actorWhere = $this->actorMigration->getWhere( $dbr, 'rev_user', $user );
$count = (int)$dbr->selectField(
[ 'revision' ] + $actorWhere['tables'],
'COUNT(*)',
[ $actorWhere['conds'] ],
__METHOD__,
[],
$actorWhere['joins']
);
$dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
$dbw->update(
'user',
[ 'user_editcount' => $count ],
[
'user_id' => $user->getId(),
'user_editcount IS NULL OR user_editcount < ' . $count
],
__METHOD__
);
return $count;
}
/**
* Get the user's first edit timestamp
*
* @param UserIdentity $user
* @return string|bool Timestamp of first edit, or false for
* non-existent/anonymous user accounts.
*/
public function getFirstEditTimestamp( UserIdentity $user ) {
return $this->getUserEditTimestamp( $user, self::FIRST_EDIT );
}
/**
* Get the user's latest edit timestamp
*
* @param UserIdentity $user
* @return string|bool Timestamp of latest edit, or false for
* non-existent/anonymous user accounts.
*/
public function getLatestEditTimestamp( UserIdentity $user ) {
return $this->getUserEditTimestamp( $user, self::LATEST_EDIT );
}
/**
* 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
* @return string|bool Timestamp of edit, or false for
* non-existent/anonymous user accounts.
*/
private function getUserEditTimestamp( UserIdentity $user, int $type ) {
if ( $user->getId() === 0 ) {
return false; // anonymous user
}
$dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
$actorWhere = $this->actorMigration->getWhere( $dbr, 'rev_user', $user );
$tsField = isset( $actorWhere['tables']['temp_rev_user'] )
? 'revactor_timestamp' : 'rev_timestamp';
$sortOrder = ( $type === self::FIRST_EDIT ) ? 'ASC' : 'DESC';
$time = $dbr->selectField(
[ 'revision' ] + $actorWhere['tables'],
$tsField,
[ $actorWhere['conds'] ],
__METHOD__,
[ 'ORDER BY' => "$tsField $sortOrder" ],
$actorWhere['joins']
);
if ( !$time ) {
return false; // no edits
}
return MWTimestamp::convert( TS_MW, $time );
}
/**
* @internal
*
* @param UserIdentity $user
*/
public function clearUserEditCache( UserIdentity $user ) {
if ( !$user->isRegistered() ) {
return;
}
$userId = $user->getId();
$cacheKey = 'u' . (string)$userId;
$this->userEditCountCache[ $cacheKey ] = null;
}
}