MediaWiki  master
UserFactory.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\User;
24 
26 use IDBAccessObject;
27 use InvalidArgumentException;
31 use stdClass;
32 use User;
36 
43 
50  public const CONSTRUCTOR_OPTIONS = [
53  ];
54 
56  private $options;
57 
59  private $loadBalancerFactory;
60 
62  private $loadBalancer;
63 
65  private $userNameUtils;
66 
68  private $lastUserFromIdentity = null;
69 
75  public function __construct(
76  ServiceOptions $options,
77  ILBFactory $loadBalancerFactory,
78  UserNameUtils $userNameUtils
79  ) {
80  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
81  $this->options = $options;
82  $this->loadBalancerFactory = $loadBalancerFactory;
83  $this->loadBalancer = $loadBalancerFactory->getMainLB();
84  $this->userNameUtils = $userNameUtils;
85  }
86 
105  public function newFromName(
106  string $name,
107  string $validate = self::RIGOR_VALID
108  ): ?User {
109  // RIGOR_* constants are the same here and in the UserNameUtils class
110  $canonicalName = $this->userNameUtils->getCanonical( $name, $validate );
111  if ( $canonicalName === false ) {
112  return null;
113  }
114 
115  $user = new User();
116  $user->mName = $canonicalName;
117  $user->mFrom = 'name';
118  $user->setItemLoaded( 'name' );
119  return $user;
120  }
121 
130  public function newAnonymous( ?string $ip = null ): User {
131  if ( $ip ) {
132  if ( !$this->userNameUtils->isIP( $ip ) ) {
133  throw new InvalidArgumentException( 'Invalid IP address' );
134  }
135  $user = new User();
136  $user->setName( $ip );
137  } else {
138  $user = new User();
139  }
140  return $user;
141  }
142 
151  public function newFromId( int $id ): User {
152  $user = new User();
153  $user->mId = $id;
154  $user->mFrom = 'id';
155  $user->setItemLoaded( 'id' );
156  return $user;
157  }
158 
167  public function newFromActorId( int $actorId ): User {
168  $user = new User();
169  $user->mActorId = $actorId;
170  $user->mFrom = 'actor';
171  $user->setItemLoaded( 'actor' );
172  return $user;
173  }
174 
183  public function newFromUserIdentity( UserIdentity $userIdentity ): User {
184  if ( $userIdentity instanceof User ) {
185  return $userIdentity;
186  }
187 
188  $id = $userIdentity->getId();
189  $name = $userIdentity->getName();
190  // Cache the $userIdentity we converted last. This avoids redundant conversion
191  // in cases where we would be converting the same UserIdentity over and over,
192  // for instance because we need to access data preferences when formatting
193  // timestamps in a listing.
194  if (
195  $this->lastUserFromIdentity
196  && $this->lastUserFromIdentity->getId() === $id
197  && $this->lastUserFromIdentity->getName() === $name
198  ) {
199  return $this->lastUserFromIdentity;
200  }
201 
202  $this->lastUserFromIdentity = $this->newFromAnyId(
203  $id === 0 ? null : $id,
204  $name === '' ? null : $name,
205  null
206  );
207 
208  return $this->lastUserFromIdentity;
209  }
210 
226  public function newFromAnyId(
227  ?int $userId,
228  ?string $userName,
229  ?int $actorId = null,
230  $dbDomain = false
231  ): User {
232  // Stop-gap solution for the problem described in T222212.
233  // Force the User ID and Actor ID to zero for users loaded from the database
234  // of another wiki, to prevent subtle data corruption and confusing failure modes.
235  if ( $dbDomain !== false ) {
236  $userId = 0;
237  $actorId = 0;
238  }
239 
240  $user = new User;
241  $user->mFrom = 'defaults';
242 
243  if ( $actorId !== null ) {
244  $user->mActorId = $actorId;
245  if ( $actorId !== 0 ) {
246  $user->mFrom = 'actor';
247  }
248  $user->setItemLoaded( 'actor' );
249  }
250 
251  if ( $userName !== null && $userName !== '' ) {
252  $user->mName = $userName;
253  $user->mFrom = 'name';
254  $user->setItemLoaded( 'name' );
255  }
256 
257  if ( $userId !== null ) {
258  $user->mId = $userId;
259  if ( $userId !== 0 ) {
260  $user->mFrom = 'id';
261  }
262  $user->setItemLoaded( 'id' );
263  }
264 
265  if ( $user->mFrom === 'defaults' ) {
266  throw new InvalidArgumentException(
267  'Cannot create a user with no name, no ID, and no actor ID'
268  );
269  }
270 
271  return $user;
272  }
273 
286  public function newFromConfirmationCode(
287  string $confirmationCode,
288  int $flags = self::READ_NORMAL
289  ) {
290  [ $index, $options ] = DBAccessObjectUtils::getDBOptions( $flags );
291 
292  $db = $this->loadBalancer->getConnectionRef( $index );
293 
294  $id = $db->selectField(
295  'user',
296  'user_id',
297  [
298  'user_email_token' => md5( $confirmationCode ),
299  'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
300  ],
301  __METHOD__,
302  $options
303  );
304 
305  if ( !$id ) {
306  return null;
307  }
308 
309  return $this->newFromId( (int)$id );
310  }
311 
321  public function newFromRow( $row, $data = null ) {
322  return User::newFromRow( $row, $data );
323  }
324 
330  public function newFromAuthority( Authority $authority ): User {
331  if ( $authority instanceof User ) {
332  return $authority;
333  }
334  return $this->newFromUserIdentity( $authority->getUser() );
335  }
336 
345  public function newTempPlaceholder() {
346  $user = new User();
347  $user->setName( $this->userNameUtils->getTempPlaceholder() );
348  return $user;
349  }
350 
358  public function newUnsavedTempUser( string $name ) {
359  $user = new User();
360  $user->setName( $name );
361  return $user;
362  }
363 
369  public function invalidateCache( UserIdentity $userIdentity ) {
370  if ( !$userIdentity->isRegistered() ) {
371  return;
372  }
373 
374  $wikiId = $userIdentity->getWikiId();
375  if ( $wikiId === UserIdentity::LOCAL ) {
376  $legacyUser = $this->newFromUserIdentity( $userIdentity );
377  // Update user_touched within User class to manage state of User::mTouched for CAS check
378  $legacyUser->invalidateCache();
379  } else {
380  // cross-wiki invalidation
381  $userId = $userIdentity->getId( $wikiId );
382 
383  $dbw = $this->getUserTableConnection( ILoadBalancer::DB_PRIMARY, $wikiId );
384  $dbw->newUpdateQueryBuilder()
385  ->update( 'user' )
386  ->set( [ 'user_touched' => $dbw->timestamp() ] )
387  ->where( [ 'user_id' => $userId ] )
388  ->caller( __METHOD__ )->execute();
389 
390  $dbw->onTransactionPreCommitOrIdle(
391  static function () use ( $wikiId, $userId ) {
392  User::purge( $wikiId, $userId );
393  },
394  __METHOD__
395  );
396  }
397  }
398 
404  private function getUserTableConnection( $mode, $wikiId ) {
405  if ( is_string( $wikiId ) && $this->loadBalancerFactory->getLocalDomainID() === $wikiId ) {
406  $wikiId = UserIdentity::LOCAL;
407  }
408 
409  if ( $this->options->get( MainConfigNames::SharedDB ) &&
410  in_array( 'user', $this->options->get( MainConfigNames::SharedTables ) )
411  ) {
412  // The main LB is aliased for the shared database in Setup.php
413  $lb = $this->loadBalancer;
414  } else {
415  $lb = $this->loadBalancerFactory->getMainLB( $wikiId );
416  }
417 
418  return $lb->getConnection( $mode, [], $wikiId );
419  }
420 }
if(!defined('MW_SETUP_CALLBACK'))
Definition: WebStart.php:88
Helper class for DAO classes.
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
A class for passing options to services.
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
A class containing constants representing the names of configuration variables.
const SharedDB
Name constant for the SharedDB setting, for use with Config::get()
const SharedTables
Name constant for the SharedTables setting, for use with Config::get()
Creates User objects.
Definition: UserFactory.php:42
newFromId(int $id)
Factory method for creation from a given user ID, replacing User::newFromId.
newFromAuthority(Authority $authority)
newFromName(string $name, string $validate=self::RIGOR_VALID)
Factory method for creating users by name, replacing static User::newFromName.
newAnonymous(?string $ip=null)
Returns a new anonymous User based on ip.
invalidateCache(UserIdentity $userIdentity)
Purge user related caches, "touch" the user table to invalidate further caches.
newFromRow( $row, $data=null)
newFromAnyId(?int $userId, ?string $userName, ?int $actorId=null, $dbDomain=false)
Factory method for creation from an ID, name, and/or actor ID, replacing User::newFromAnyId.
newFromUserIdentity(UserIdentity $userIdentity)
Factory method for creation fom a given UserIdentity, replacing User::newFromIdentity.
__construct(ServiceOptions $options, ILBFactory $loadBalancerFactory, UserNameUtils $userNameUtils)
Definition: UserFactory.php:75
newTempPlaceholder()
Create a placeholder user for an anonymous user who will be upgraded to a temporary user.
newUnsavedTempUser(string $name)
Create an unsaved temporary user with a previously acquired name.
newFromActorId(int $actorId)
Factory method for creation from a given actor ID, replacing User::newFromActorId.
newFromConfirmationCode(string $confirmationCode, int $flags=self::READ_NORMAL)
Factory method to fetch the user for a given email confirmation code, replacing User::newFromConfirma...
const CONSTRUCTOR_OPTIONS
RIGOR_* constants are inherited from UserRigorOptions READ_* constants are inherited from IDBAccessOb...
Definition: UserFactory.php:50
UserNameUtils service.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:71
static purge( $dbDomain, $userId)
Definition: User.php:490
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition: User.php:747
setItemLoaded( $item)
Set that an item has been loaded.
Definition: User.php:1091
Interface for database access objects.
getWikiId()
Get the ID of the wiki this page belongs to.
This interface represents the authority associated the current execution context, such as a web reque...
Definition: Authority.php:37
Interface for objects representing user identity.
getId( $wikiId=self::LOCAL)
Shared interface for rigor levels when dealing with User methods.
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:36
Manager of ILoadBalancer objects and, indirectly, IDatabase connections.
Definition: ILBFactory.php:46
getMainLB( $domain=false)
Get the tracked load balancer instance for the main cluster that handles the given domain.
This class is a delegate to ILBFactory for a given database cluster.
const DB_PRIMARY
Definition: defines.php:28