$data = $cache->getWithSetCallback(
$this->getCacheKey( $cache ),
$cache::TTL_HOUR,
- function ( $oldValue, &$ttl, array &$setOpts ) {
- $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+ function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
+ $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
wfDebug( "User: cache miss for user {$this->mId}\n" );
$this->loadFromDatabase( self::READ_NORMAL );
$data[$name] = $this->$name;
}
+ $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl );
+
return $data;
},
public static function newFromConfirmationCode( $code, $flags = 0 ) {
$db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$id = $db->selectField(
'user',
$conds[] = 'ug_user > ' . (int)$after;
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$ids = $dbr->selectFieldValues(
'user_groups',
'ug_user',
if ( is_null( $this->mGroups ) ) {
$db = ( $this->queryFlagsUsed & self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$res = $db->select( 'user_groups',
[ 'ug_group' ],
[ 'ug_user' => $this->mId ],
/**
* Get blocking information
- * @param bool $bFromSlave Whether to check the slave database first.
- * To improve performance, non-critical checks are done against slaves.
+ * @param bool $bFromSlave Whether to check the replica DB first.
+ * To improve performance, non-critical checks are done against replica DBs.
* Check when actually saving should be done against master.
*/
private function getBlockedStatus( $bFromSlave = true ) {
/**
* Check if user is blocked
*
- * @param bool $bFromSlave Whether to check the slave database instead of
+ * @param bool $bFromSlave Whether to check the replica DB instead of
* the master. Hacked from false due to horrible probs on site.
* @return bool True if blocked, false otherwise
*/
/**
* Get the block affecting the user, or null if the user is not blocked
*
- * @param bool $bFromSlave Whether to check the slave database instead of the master
+ * @param bool $bFromSlave Whether to check the replica DB instead of the master
* @return Block|null
*/
public function getBlock( $bFromSlave = true ) {
* Check if user is blocked from editing a particular article
*
* @param Title $title Title to check
- * @param bool $bFromSlave Whether to check the slave database instead of the master
+ * @param bool $bFromSlave Whether to check the replica DB instead of the master
* @return bool
*/
public function isBlockedFrom( $title, $bFromSlave = false ) {
return [];
}
$utp = $this->getTalkPage();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// Get the "last viewed rev" timestamp from the oldest message notification
$timestamp = $dbr->selectField( 'user_newtalk',
'MIN(user_last_timestamp)',
* @return bool True if the user has new messages
*/
protected function checkNewtalk( $field, $id ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$ok = $dbr->selectField( 'user_newtalk', $field, [ $field => $id ], __METHOD__ );
public function getRights() {
if ( is_null( $this->mRights ) ) {
$this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
+ Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
// Deny any rights denied by the user's session, unless this
// endpoint has no sessions.
}
}
- Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
// Force reindexation of rights when a hook has unset one of them
$this->mRights = array_values( array_unique( $this->mRights ) );
+
+ // If block disables login, we should also remove any
+ // extra rights blocked users might have, in case the
+ // blocked user has a pre-existing session (T129738).
+ // This is checked here for cases where people only call
+ // $user->isAllowed(). It is also checked in Title::checkUserBlock()
+ // to give a better error message in the common case.
+ $config = RequestContext::getMain()->getConfig();
+ if (
+ $this->isLoggedIn() &&
+ $config->get( 'BlockDisablesLogin' ) &&
+ $this->isBlocked()
+ ) {
+ $anon = new User;
+ $this->mRights = array_intersect( $this->mRights, $anon->getRights() );
+ }
}
return $this->mRights;
}
if ( is_null( $this->mFormerGroups ) ) {
$db = ( $this->queryFlagsUsed & self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$res = $db->select( 'user_former_groups',
[ 'ufg_group' ],
[ 'ufg_user' => $this->mId ],
if ( $this->mEditCount === null ) {
/* Populate the count, if it has not been populated yet */
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
// check if the user_editcount field has been initialized
$count = $dbr->selectField(
'user', 'user_editcount',
// Only update the timestamp if the page is being watched.
// The query to find out if it is watched is cached both in memcached and per-invocation,
- // and when it does have to be executed, it can be on a slave
+ // and when it does have to be executed, it can be on a replica DB
// If this is the user's newtalk page, we always update the timestamp
$force = '';
if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
// Get a new user_touched that is higher than the old one.
// This will be used for a CAS check as a last-resort safety
- // check against race conditions and slave lag.
+ // check against race conditions and replica DB lag.
$newTouched = $this->newTouchedTimestamp();
$dbw = wfGetDB( DB_MASTER );
// Maybe the problem was a missed cache update; clear it to be safe
$this->clearSharedCache( 'refresh' );
// User was changed in the meantime or loaded with stale data
- $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'slave';
+ $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
throw new MWException(
"CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
" the version of the user to be saved is older than the current version."
$db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
? [ 'LOCK IN SHARE MODE' ]
$noPass = PasswordFactory::newInvalidPassword()->toString();
$dbw = wfGetDB( DB_MASTER );
- $inWrite = $dbw->writesOrCallbacksPending();
$seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
$dbw->insert( 'user',
[
[ 'IGNORE' ]
);
if ( !$dbw->affectedRows() ) {
- // The queries below cannot happen in the same REPEATABLE-READ snapshot.
- // Handle this by COMMIT, if possible, or by LOCK IN SHARE MODE otherwise.
- if ( $inWrite ) {
- // Can't commit due to pending writes that may need atomicity.
- // This may cause some lock contention unlike the case below.
- $options = [ 'LOCK IN SHARE MODE' ];
- $flags = self::READ_LOCKING;
- } else {
- // Often, this case happens early in views before any writes when
- // using CentralAuth. It's should be OK to commit and break the snapshot.
- $dbw->commit( __METHOD__, 'flush' );
- $options = [];
- $flags = self::READ_LATEST;
- }
- $this->mId = $dbw->selectField( 'user', 'user_id',
- [ 'user_name' => $this->mName ], __METHOD__, $options );
+ // Use locking reads to bypass any REPEATABLE-READ snapshot.
+ $this->mId = $dbw->selectField(
+ 'user',
+ 'user_id',
+ [ 'user_name' => $this->mName ],
+ __METHOD__,
+ [ 'LOCK IN SHARE MODE' ]
+ );
$loaded = false;
if ( $this->mId ) {
- if ( $this->loadFromDatabase( $flags ) ) {
+ if ( $this->loadFromDatabase( self::READ_LOCKING ) ) {
$loaded = true;
}
}
* login credentials aren't being hijacked with a foreign form
* submission.
*
+ * The $salt for 'edit' and 'csrf' tokens is the default (empty string).
+ *
* @since 1.19
* @param string|array $salt Array of Strings Optional function-specific data for hashing
* @param WebRequest|null $request WebRequest object to use or null to use $wgRequest
if ( $this->getId() == 0 ) {
return false; // anons
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$time = $dbr->selectField( 'revision', 'rev_timestamp',
[ 'rev_user' => $this->getId() ],
__METHOD__,
// Lazy initialization check...
if ( $dbw->affectedRows() == 0 ) {
// Now here's a goddamn hack...
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
if ( $dbr !== $dbw ) {
- // If we actually have a slave server, the count is
+ // If we actually have a replica DB server, the count is
// at least one behind because the current transaction
// has not been committed and replicated.
$this->mEditCount = $this->initEditCount( 1 );
} else {
- // But if DB_SLAVE is selecting the master, then the
+ // But if DB_REPLICA is selecting the master, then the
// count we just read includes the revision that was
// just added in the working transaction.
$this->mEditCount = $this->initEditCount();
} else {
if ( $this->mEditCount === null ) {
$this->getEditCount();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
} else {
$this->mEditCount++;
* @return int Number of edits
*/
protected function initEditCount( $add = 0 ) {
- // Pull from a slave to be less cruel to servers
+ // Pull from a replica DB to be less cruel to servers
// Accuracy isn't the point anyway here
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$count = (int)$dbr->selectField(
'revision',
'COUNT(rev_user)',
// Load from database
$dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
? wfGetDB( DB_MASTER )
- : wfGetDB( DB_SLAVE );
+ : wfGetDB( DB_REPLICA );
$res = $dbr->select(
'user_properties',
[ 'up_property', 'up_value' ], [ 'up_user' => $userId ], __METHOD__ );
// Find prior rows that need to be removed or updated. These rows will
- // all be deleted (the later so that INSERT IGNORE applies the new values).
+ // all be deleted (the latter so that INSERT IGNORE applies the new values).
$keysDelete = [];
foreach ( $res as $row ) {
if ( !isset( $saveOptions[$row->up_property] )
* Get a new instance of this user that was loaded from the master via a locking read
*
* Use this instead of the main context User when updating that user. This avoids races
- * where that user was loaded from a slave or even the master but without proper locks.
+ * where that user was loaded from a replica DB or even the master but without proper locks.
*
* @return User|null Returns null if the user was not found in the DB
* @since 1.27