Wrap libxml_disable_entity_loader() calls in version constraint
[mediawiki.git] / includes / user / UserFactory.php
blob389ed642a7d220990d46ad1252796a9dd0fd1a8a
1 <?php
2 /**
3 * Factory for creating User objects without static coupling.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
20 * @file
23 namespace MediaWiki\User;
25 use DBAccessObjectUtils;
26 use IDBAccessObject;
27 use InvalidArgumentException;
28 use MediaWiki\Permissions\Authority;
29 use stdClass;
30 use User;
31 use Wikimedia\Rdbms\ILoadBalancer;
33 /**
34 * Creates User objects.
36 * For now, there is nothing much interesting in this class. It was meant for preventing static User
37 * methods causing problems in unit tests.
39 * @since 1.35
41 class UserFactory implements IDBAccessObject, UserRigorOptions {
43 /**
44 * RIGOR_* constants are inherited from UserRigorOptions
45 * READ_* constants are inherited from IDBAccessObject
48 /** @var ILoadBalancer */
49 private $loadBalancer;
51 /** @var UserNameUtils */
52 private $userNameUtils;
54 /**
55 * @param ILoadBalancer $loadBalancer
56 * @param UserNameUtils $userNameUtils
58 public function __construct(
59 ILoadBalancer $loadBalancer,
60 UserNameUtils $userNameUtils
61 ) {
62 $this->loadBalancer = $loadBalancer;
63 $this->userNameUtils = $userNameUtils;
66 /**
67 * Factory method for creating users by name, replacing static User::newFromName
69 * This is slightly less efficient than newFromId(), so use newFromId() if
70 * you have both an ID and a name handy.
72 * @note unlike User::newFromName, this returns null instead of false for invalid usernames
74 * @since 1.35
75 * @since 1.36 returns null instead of false for invalid user names
77 * @param string $name Username, validated by Title::newFromText
78 * @param string $validate Validation strategy, one of the RIGOR_* constants. For no
79 * validation, use RIGOR_NONE.
80 * @return ?User User object, or null if the username is invalid (e.g. if it contains
81 * illegal characters or is an IP address). If the username is not present in the database,
82 * the result will be a user object with a name, a user id of 0, and default settings.
84 public function newFromName(
85 string $name,
86 string $validate = self::RIGOR_VALID
87 ) : ?User {
88 // RIGOR_* constants are the same here and in the UserNameUtils class
89 $canonicalName = $this->userNameUtils->getCanonical( $name, $validate );
90 if ( $canonicalName === false ) {
91 return null;
94 $user = new User();
95 $user->mName = $canonicalName;
96 $user->mFrom = 'name';
97 $user->setItemLoaded( 'name' );
98 return $user;
102 * Returns a new anonymous User based on ip.
104 * @since 1.35
106 * @param string|null $ip IP address
107 * @return User
109 public function newAnonymous( $ip = null ) : User {
110 if ( $ip ) {
111 $validIp = $this->userNameUtils->isIP( $ip );
112 if ( $validIp ) {
113 $user = $this->newFromName( $ip, self::RIGOR_NONE );
114 } else {
115 throw new InvalidArgumentException( 'Invalid IP address' );
117 } else {
118 $user = new User();
120 return $user;
124 * Factory method for creation from a given user ID, replacing User::newFromId
126 * @since 1.35
128 * @param int $id Valid user ID
129 * @return User The corresponding User object
131 public function newFromId( int $id ) : User {
132 $user = new User();
133 $user->mId = $id;
134 $user->mFrom = 'id';
135 $user->setItemLoaded( 'id' );
136 return $user;
140 * Factory method for creation from a given actor ID, replacing User::newFromActorId
142 * @since 1.35
144 * @param int $actorId
145 * @return User
147 public function newFromActorId( int $actorId ) : User {
148 $user = new User();
149 $user->mActorId = $actorId;
150 $user->mFrom = 'actor';
151 $user->setItemLoaded( 'actor' );
152 return $user;
156 * Factory method for creation fom a given UserIdentity, replacing User::newFromIdentity
158 * @since 1.35
160 * @param UserIdentity $userIdentity
161 * @return User
163 public function newFromUserIdentity( UserIdentity $userIdentity ) : User {
164 if ( $userIdentity instanceof User ) {
165 return $userIdentity;
168 return $this->newFromAnyId(
169 $userIdentity->getId() === 0 ? null : $userIdentity->getId(),
170 $userIdentity->getName() === '' ? null : $userIdentity->getName(),
171 $userIdentity->getActorId() === 0 ? null : $userIdentity->getActorId()
176 * Factory method for creation from an ID, name, and/or actor ID, replacing User::newFromAnyId
178 * @note This does not check that the ID, name, and actor ID all correspond to
179 * the same user.
181 * @since 1.35
183 * @param ?int $userId
184 * @param ?string $userName
185 * @param ?int $actorId
186 * @param bool|string $dbDomain
187 * @return User
188 * @throws InvalidArgumentException if none of userId, userName, and actorId are specified
190 public function newFromAnyId(
191 ?int $userId,
192 ?string $userName,
193 ?int $actorId,
194 $dbDomain = false
195 ) : User {
196 // Stop-gap solution for the problem described in T222212.
197 // Force the User ID and Actor ID to zero for users loaded from the database
198 // of another wiki, to prevent subtle data corruption and confusing failure modes.
199 if ( $dbDomain !== false ) {
200 $userId = 0;
201 $actorId = 0;
204 $user = new User;
205 $user->mFrom = 'defaults';
207 if ( $actorId !== null ) {
208 $user->mActorId = $actorId;
209 if ( $actorId !== 0 ) {
210 $user->mFrom = 'actor';
212 $user->setItemLoaded( 'actor' );
215 if ( $userName !== null && $userName !== '' ) {
216 $user->mName = $userName;
217 $user->mFrom = 'name';
218 $user->setItemLoaded( 'name' );
221 if ( $userId !== null ) {
222 $user->mId = $userId;
223 if ( $userId !== 0 ) {
224 $user->mFrom = 'id';
226 $user->setItemLoaded( 'id' );
229 if ( $user->mFrom === 'defaults' ) {
230 throw new InvalidArgumentException(
231 'Cannot create a user with no name, no ID, and no actor ID'
235 return $user;
239 * Factory method to fetch the user for a given email confirmation code, replacing User::newFromConfirmationCode
241 * This code is generated when an account is created or its e-mail address has changed.
242 * If the code is invalid or has expired, returns null.
244 * @since 1.35
246 * @param string $confirmationCode
247 * @param int $flags
248 * @return User|null
250 public function newFromConfirmationCode(
251 string $confirmationCode,
252 int $flags = self::READ_NORMAL
254 list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
256 $db = $this->loadBalancer->getConnectionRef( $index );
258 $id = $db->selectField(
259 'user',
260 'user_id',
262 'user_email_token' => md5( $confirmationCode ),
263 'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
265 __METHOD__,
266 $options
269 if ( !$id ) {
270 return null;
273 return $this->newFromId( (int)$id );
277 * @see User::newFromRow
279 * @since 1.36
281 * @param stdClass $row A row from the user table
282 * @param array|null $data Further data to load into the object
283 * @return User
285 public function newFromRow( $row, $data = null ) {
286 return User::newFromRow( $row, $data );
290 * @internal for transition from User to Authority as performer concept.
291 * @param Authority $authority
292 * @return User
294 public function newFromAuthority( Authority $authority ): User {
295 if ( $authority instanceof User ) {
296 return $authority;
298 return $this->newFromUserIdentity( $authority->getActor() );