use MediaWiki\Auth\AuthenticationRequest;
use MediaWiki\User\UserIdentity;
use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Assert\Assert;
use Wikimedia\IPSet;
use Wikimedia\ScopedCallback;
use Wikimedia\Rdbms\Database;
* of the database.
*/
class User implements IDBAccessObject, UserIdentity {
+
/**
- * @const int Number of characters in user_token field.
+ * Number of characters required for the user_token field.
*/
const TOKEN_LENGTH = 32;
/**
- * @const string An invalid value for user_token
+ * An invalid string value for the user_token field.
*/
const INVALID_TOKEN = '*** INVALID ***';
/**
- * @const int Serialized record version.
+ * Version number to tag cached versions of serialized User objects. Should be increased when
+ * {@link $mCacheVars} or one of it's members changes.
*/
const VERSION = 13;
return $name;
}
- /**
- * Return a random password.
- *
- * @deprecated since 1.27, use PasswordFactory::generateRandomPasswordString()
- * @return string New random password
- */
- public static function randomPassword() {
- global $wgMinimalPasswordLength;
- return PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
- }
-
/**
* Set cached properties to default.
*
public function trackBlockWithCookie() {
$block = $this->getBlock();
- if ( $block && $this->getRequest()->getCookie( 'BlockID' ) === null ) {
- if ( $block->shouldTrackWithCookie( $this->isAnon() ) ) {
- $block->setCookie( $this->getRequest()->response() );
- }
+ if ( $block && $this->getRequest()->getCookie( 'BlockID' ) === null
+ && $block->shouldTrackWithCookie( $this->isAnon() )
+ ) {
+ $block->setCookie( $this->getRequest()->response() );
}
}
* protected against race conditions using a compare-and-set (CAS) mechanism
* based on comparing $this->mTouched with the user_touched field.
*
- * @param Database $db
+ * @param IDatabase $db
* @param array $conditions WHERE conditions for use with Database::update
* @return array WHERE conditions for use with Database::update
*/
- protected function makeUpdateConditions( Database $db, array $conditions ) {
+ protected function makeUpdateConditions( IDatabase $db, array $conditions ) {
if ( $this->mTouched ) {
// CAS check: only update if the row wasn't changed sicne it was loaded.
$conditions['user_touched'] = $db->timestamp( $this->mTouched );
if ( $success ) {
$this->mTouched = $newTouched;
- $this->clearSharedCache();
+ $this->clearSharedCache( 'changed' );
} else {
// Clears on failure too since that is desired if the cache is stale
$this->clearSharedCache( 'refresh' );
}
}
+ /** @var array|null */
+ private static $defOpt = null;
+ /** @var string|null */
+ private static $defOptLang = null;
+
+ /**
+ * Reset the process cache of default user options. This is only necessary
+ * if the wiki configuration has changed since defaults were calculated,
+ * and as such should only be performed inside the testing suite that
+ * regularly changes wiki configuration.
+ */
+ public static function resetGetDefaultOptionsForTestsOnly() {
+ Assert::invariant( defined( 'MW_PHPUNIT_TEST' ), 'Unit tests only' );
+ self::$defOpt = null;
+ self::$defOptLang = null;
+ }
+
/**
* Combine the language default options with any site-specific options
* and add the default language variants.
public static function getDefaultOptions() {
global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgDefaultSkin;
- static $defOpt = null;
- static $defOptLang = null;
-
$contLang = MediaWikiServices::getInstance()->getContentLanguage();
- if ( $defOpt !== null && $defOptLang === $contLang->getCode() ) {
+ if ( self::$defOpt !== null && self::$defOptLang === $contLang->getCode() ) {
// The content language does not change (and should not change) mid-request, but the
// unit tests change it anyway, and expect this method to return values relevant to the
// current content language.
- return $defOpt;
+ return self::$defOpt;
}
- $defOpt = $wgDefaultUserOptions;
+ self::$defOpt = $wgDefaultUserOptions;
// Default language setting
- $defOptLang = $contLang->getCode();
- $defOpt['language'] = $defOptLang;
+ self::$defOptLang = $contLang->getCode();
+ self::$defOpt['language'] = self::$defOptLang;
foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
if ( $langCode === $contLang->getCode() ) {
- $defOpt['variant'] = $langCode;
+ self::$defOpt['variant'] = $langCode;
} else {
- $defOpt["variant-$langCode"] = $langCode;
+ self::$defOpt["variant-$langCode"] = $langCode;
}
}
// since extensions may change the set of searchable namespaces depending
// on user groups/permissions.
foreach ( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
- $defOpt['searchNs' . $nsnum] = (bool)$val;
+ self::$defOpt['searchNs' . $nsnum] = (bool)$val;
}
- $defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
+ self::$defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
- Hooks::run( 'UserGetDefaultOptions', [ &$defOpt ] );
+ Hooks::run( 'UserGetDefaultOptions', [ &self::$defOpt ] );
- return $defOpt;
+ return self::$defOpt;
}
/**
// Set the user limit key
if ( $userLimit !== false ) {
+ // phan is confused because &can-bypass's value is a bool, so it assumes
+ // that $userLimit is also a bool here.
+ // @phan-suppress-next-line PhanTypeInvalidExpressionArrayDestructuring
list( $max, $period ) = $userLimit;
wfDebug( __METHOD__ . ": effective user limit: $max in {$period}s\n" );
$keys[$cache->makeKey( 'limiter', $action, 'user', $id )] = $userLimit;
$triggered = false;
foreach ( $keys as $key => $limit ) {
+ // phan is confused because &can-bypass's value is a bool, so it assumes
+ // that $userLimit is also a bool here.
+ // @phan-suppress-next-line PhanTypeInvalidExpressionArrayDestructuring
list( $max, $period ) = $limit;
$summary = "(limit $max in {$period}s)";
$count = $cache->get( $key );
* @param Title $title Title to check
* @param bool $fromReplica Whether to check the replica DB instead of the master
* @return bool
+ * @throws MWException
+ *
+ * @deprecated since 1.33,
+ * use MediaWikiServices::getInstance()->getPermissionManager()->isBlockedFrom(..)
+ *
*/
public function isBlockedFrom( $title, $fromReplica = false ) {
- $blocked = $this->isHidden();
-
- if ( !$blocked ) {
- $block = $this->getBlock( $fromReplica );
- if ( $block ) {
- // Special handling for a user's own talk page. The block is not aware
- // of the user, so this must be done here.
- if ( $title->equals( $this->getTalkPage() ) ) {
- $blocked = $block->appliesToUsertalk( $title );
- } else {
- $blocked = $block->appliesToTitle( $title );
- }
- }
- }
-
- // only for the purpose of the hook. We really don't need this here.
- $allowUsertalk = $this->mAllowUsertalk;
-
- Hooks::run( 'UserIsBlockedFrom', [ $this, $title, &$blocked, &$allowUsertalk ] );
-
- return $blocked;
+ return MediaWikiServices::getInstance()->getPermissionManager()
+ ->isBlockedFrom( $this, $title, $fromReplica );
}
/**
*
* Called implicitly from invalidateCache() and saveSettings().
*
- * @param string $mode Use 'refresh' to clear now; otherwise before DB commit
+ * @param string $mode Use 'refresh' to clear now or 'changed' to clear before DB commit
*/
- public function clearSharedCache( $mode = 'changed' ) {
+ public function clearSharedCache( $mode = 'refresh' ) {
if ( !$this->getId() ) {
return;
}
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
$key = $this->getCacheKey( $cache );
+
if ( $mode === 'refresh' ) {
- $cache->delete( $key, 1 );
+ $cache->delete( $key, 1 ); // low tombstone/"hold-off" TTL
} else {
- $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
- if ( $lb->hasOrMadeRecentMasterChanges() ) {
- $lb->getConnection( DB_MASTER )->onTransactionPreCommitOrIdle(
- function () use ( $cache, $key ) {
- $cache->delete( $key );
- },
- __METHOD__
- );
- } else {
- $cache->delete( $key );
- }
+ $lb->getConnection( DB_MASTER )->onTransactionPreCommitOrIdle(
+ function () use ( $cache, $key ) {
+ $cache->delete( $key );
+ },
+ __METHOD__
+ );
}
}
*/
public function invalidateCache() {
$this->touch();
- $this->clearSharedCache();
+ $this->clearSharedCache( 'changed' );
}
/**
$this->saveOptions();
Hooks::run( 'UserSaveSettings', [ $this ] );
- $this->clearSharedCache();
+ $this->clearSharedCache( 'changed' );
$this->getUserPage()->purgeSquid();
}
[ 'LOCK IN SHARE MODE' ]
);
$loaded = false;
- if ( $this->mId ) {
- if ( $this->loadFromDatabase( self::READ_LOCKING ) ) {
- $loaded = true;
- }
+ if ( $this->mId && $this->loadFromDatabase( self::READ_LOCKING ) ) {
+ $loaded = true;
}
if ( !$loaded ) {
throw new MWException( $fname . ": hit a key conflict attempting " .
// XXX it's not clear whether central ID providers are supposed to obey this
return $this->getName() === $user->getName();
}
+
+ /**
+ * Checks if usertalk is allowed
+ *
+ * @return bool
+ */
+ public function isAllowUsertalk() {
+ return $this->mAllowUsertalk;
+ }
+
}