MediaWiki REL1_41
UserFactory.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\User;
24
27use InvalidArgumentException;
31use stdClass;
35
42
49 public const CONSTRUCTOR_OPTIONS = [
52 ];
53
55 private $options;
56
58 private $loadBalancerFactory;
59
61 private $loadBalancer;
62
64 private $userNameUtils;
65
67 private $lastUserFromIdentity = null;
68
74 public function __construct(
75 ServiceOptions $options,
76 ILBFactory $loadBalancerFactory,
77 UserNameUtils $userNameUtils
78 ) {
79 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
80 $this->options = $options;
81 $this->loadBalancerFactory = $loadBalancerFactory;
82 $this->loadBalancer = $loadBalancerFactory->getMainLB();
83 $this->userNameUtils = $userNameUtils;
84 }
85
104 public function newFromName(
105 string $name,
106 string $validate = self::RIGOR_VALID
107 ): ?User {
108 // RIGOR_* constants are the same here and in the UserNameUtils class
109 $canonicalName = $this->userNameUtils->getCanonical( $name, $validate );
110 if ( $canonicalName === false ) {
111 return null;
112 }
113
114 $user = new User();
115 $user->mName = $canonicalName;
116 $user->mFrom = 'name';
117 $user->setItemLoaded( 'name' );
118 return $user;
119 }
120
129 public function newAnonymous( ?string $ip = null ): User {
130 if ( $ip ) {
131 if ( !$this->userNameUtils->isIP( $ip ) ) {
132 throw new InvalidArgumentException( 'Invalid IP address' );
133 }
134 $user = new User();
135 $user->setName( $ip );
136 } else {
137 $user = new User();
138 }
139 return $user;
140 }
141
150 public function newFromId( int $id ): User {
151 $user = new User();
152 $user->mId = $id;
153 $user->mFrom = 'id';
154 $user->setItemLoaded( 'id' );
155 return $user;
156 }
157
166 public function newFromActorId( int $actorId ): User {
167 $user = new User();
168 $user->mActorId = $actorId;
169 $user->mFrom = 'actor';
170 $user->setItemLoaded( 'actor' );
171 return $user;
172 }
173
182 public function newFromUserIdentity( UserIdentity $userIdentity ): User {
183 if ( $userIdentity instanceof User ) {
184 return $userIdentity;
185 }
186
187 $id = $userIdentity->getId();
188 $name = $userIdentity->getName();
189 // Cache the $userIdentity we converted last. This avoids redundant conversion
190 // in cases where we would be converting the same UserIdentity over and over,
191 // for instance because we need to access data preferences when formatting
192 // timestamps in a listing.
193 if (
194 $this->lastUserFromIdentity
195 && $this->lastUserFromIdentity->getId() === $id
196 && $this->lastUserFromIdentity->getName() === $name
197 ) {
198 return $this->lastUserFromIdentity;
199 }
200
201 $this->lastUserFromIdentity = $this->newFromAnyId(
202 $id === 0 ? null : $id,
203 $name === '' ? null : $name,
204 null
205 );
206
207 return $this->lastUserFromIdentity;
208 }
209
225 public function newFromAnyId(
226 ?int $userId,
227 ?string $userName,
228 ?int $actorId = null,
229 $dbDomain = false
230 ): User {
231 // Stop-gap solution for the problem described in T222212.
232 // Force the User ID and Actor ID to zero for users loaded from the database
233 // of another wiki, to prevent subtle data corruption and confusing failure modes.
234 if ( $dbDomain !== false ) {
235 $userId = 0;
236 $actorId = 0;
237 }
238
239 $user = new User;
240 $user->mFrom = 'defaults';
241
242 if ( $actorId !== null ) {
243 $user->mActorId = $actorId;
244 if ( $actorId !== 0 ) {
245 $user->mFrom = 'actor';
246 }
247 $user->setItemLoaded( 'actor' );
248 }
249
250 if ( $userName !== null && $userName !== '' ) {
251 $user->mName = $userName;
252 $user->mFrom = 'name';
253 $user->setItemLoaded( 'name' );
254 }
255
256 if ( $userId !== null ) {
257 $user->mId = $userId;
258 if ( $userId !== 0 ) {
259 $user->mFrom = 'id';
260 }
261 $user->setItemLoaded( 'id' );
262 }
263
264 if ( $user->mFrom === 'defaults' ) {
265 throw new InvalidArgumentException(
266 'Cannot create a user with no name, no ID, and no actor ID'
267 );
268 }
269
270 return $user;
271 }
272
285 public function newFromConfirmationCode(
286 string $confirmationCode,
287 int $flags = self::READ_NORMAL
288 ) {
289 [ $index, $options ] = DBAccessObjectUtils::getDBOptions( $flags );
290
291 $db = $this->loadBalancer->getConnectionRef( $index );
292
293 $id = $db->newSelectQueryBuilder()
294 ->select( 'user_id' )
295 ->from( 'user' )
296 ->where( [ 'user_email_token' => md5( $confirmationCode ) ] )
297 ->andWhere( $db->buildComparison( '>', [ 'user_email_token_expires' => $db->timestamp() ] ) )
298 ->options( $options )
299 ->caller( __METHOD__ )->fetchField();
300
301 if ( !$id ) {
302 return null;
303 }
304
305 return $this->newFromId( (int)$id );
306 }
307
317 public function newFromRow( $row, $data = null ) {
318 return User::newFromRow( $row, $data );
319 }
320
326 public function newFromAuthority( Authority $authority ): User {
327 if ( $authority instanceof User ) {
328 return $authority;
329 }
330 return $this->newFromUserIdentity( $authority->getUser() );
331 }
332
341 public function newTempPlaceholder() {
342 $user = new User();
343 $user->setName( $this->userNameUtils->getTempPlaceholder() );
344 return $user;
345 }
346
354 public function newUnsavedTempUser( ?string $name ) {
355 $user = new User();
356 $user->setName( $name ?? $this->userNameUtils->getTempPlaceholder() );
357 return $user;
358 }
359
365 public function invalidateCache( UserIdentity $userIdentity ) {
366 if ( !$userIdentity->isRegistered() ) {
367 return;
368 }
369
370 $wikiId = $userIdentity->getWikiId();
371 if ( $wikiId === UserIdentity::LOCAL ) {
372 $legacyUser = $this->newFromUserIdentity( $userIdentity );
373 // Update user_touched within User class to manage state of User::mTouched for CAS check
374 $legacyUser->invalidateCache();
375 } else {
376 // cross-wiki invalidation
377 $userId = $userIdentity->getId( $wikiId );
378
379 $dbw = $this->getUserTableConnection( ILoadBalancer::DB_PRIMARY, $wikiId );
380 $dbw->newUpdateQueryBuilder()
381 ->update( 'user' )
382 ->set( [ 'user_touched' => $dbw->timestamp() ] )
383 ->where( [ 'user_id' => $userId ] )
384 ->caller( __METHOD__ )->execute();
385
386 $dbw->onTransactionPreCommitOrIdle(
387 static function () use ( $wikiId, $userId ) {
388 User::purge( $wikiId, $userId );
389 },
390 __METHOD__
391 );
392 }
393 }
394
400 private function getUserTableConnection( $mode, $wikiId ) {
401 if ( is_string( $wikiId ) && $this->loadBalancerFactory->getLocalDomainID() === $wikiId ) {
402 $wikiId = UserIdentity::LOCAL;
403 }
404
405 if ( $this->options->get( MainConfigNames::SharedDB ) &&
406 in_array( 'user', $this->options->get( MainConfigNames::SharedTables ) )
407 ) {
408 // The main LB is aliased for the shared database in Setup.php
409 $lb = $this->loadBalancer;
410 } else {
411 $lb = $this->loadBalancerFactory->getMainLB( $wikiId );
412 }
413
414 return $lb->getConnection( $mode, [], $wikiId );
415 }
416}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:88
Helper class for DAO classes.
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.
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)
newUnsavedTempUser(?string $name)
Create an unsaved temporary user with a previously acquired name or a placeholder name.
newTempPlaceholder()
Create a placeholder user for an anonymous user who will be upgraded to a temporary user.
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...
UserNameUtils service.
internal since 1.36
Definition User.php:98
setItemLoaded( $item)
Set that an item has been loaded.
Definition User.php:1064
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.
isRegistered()
This must be equivalent to getId() != 0 and is provided for code readability.
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.
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.
Utility class for bot passwords.