1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
|
<?php
/**
* Factory for creating User objects without static coupling.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\User;
use DBAccessObjectUtils;
use IDBAccessObject;
use InvalidArgumentException;
use MediaWiki\Permissions\Authority;
use stdClass;
use User;
use Wikimedia\Rdbms\ILoadBalancer;
/**
* Creates User objects.
*
* @since 1.35
*/
class UserFactory implements IDBAccessObject, UserRigorOptions {
/**
* RIGOR_* constants are inherited from UserRigorOptions
* READ_* constants are inherited from IDBAccessObject
*/
/** @var ILoadBalancer */
private $loadBalancer;
/** @var UserNameUtils */
private $userNameUtils;
/** @var User|null */
private $lastUserFromIdentity = null;
/**
* @param ILoadBalancer $loadBalancer
* @param UserNameUtils $userNameUtils
*/
public function __construct(
ILoadBalancer $loadBalancer,
UserNameUtils $userNameUtils
) {
$this->loadBalancer = $loadBalancer;
$this->userNameUtils = $userNameUtils;
}
/**
* Factory method for creating users by name, replacing static User::newFromName
*
* This is slightly less efficient than newFromId(), so use newFromId() if
* you have both an ID and a name handy.
*
* @note unlike User::newFromName, this returns null instead of false for invalid usernames
*
* @since 1.35
* @since 1.36 returns null instead of false for invalid user names
*
* @param string $name Username, validated by Title::newFromText
* @param string $validate Validation strategy, one of the RIGOR_* constants. For no
* validation, use RIGOR_NONE.
* @return ?User User object, or null if the username is invalid (e.g. if it contains
* illegal characters or is an IP address). If the username is not present in the database,
* the result will be a user object with a name, a user id of 0, and default settings.
*/
public function newFromName(
string $name,
string $validate = self::RIGOR_VALID
): ?User {
// RIGOR_* constants are the same here and in the UserNameUtils class
$canonicalName = $this->userNameUtils->getCanonical( $name, $validate );
if ( $canonicalName === false ) {
return null;
}
$user = new User();
$user->mName = $canonicalName;
$user->mFrom = 'name';
$user->setItemLoaded( 'name' );
return $user;
}
/**
* Returns a new anonymous User based on ip.
*
* @since 1.35
*
* @param string|null $ip IP address
* @return User
*/
public function newAnonymous( ?string $ip = null ): User {
if ( $ip ) {
if ( !$this->userNameUtils->isIP( $ip ) ) {
throw new InvalidArgumentException( 'Invalid IP address' );
}
$user = new User();
$user->setName( $ip );
} else {
$user = new User();
}
return $user;
}
/**
* Factory method for creation from a given user ID, replacing User::newFromId
*
* @since 1.35
*
* @param int $id Valid user ID
* @return User
*/
public function newFromId( int $id ): User {
$user = new User();
$user->mId = $id;
$user->mFrom = 'id';
$user->setItemLoaded( 'id' );
return $user;
}
/**
* Factory method for creation from a given actor ID, replacing User::newFromActorId
*
* @since 1.35
*
* @param int $actorId
* @return User
*/
public function newFromActorId( int $actorId ): User {
$user = new User();
$user->mActorId = $actorId;
$user->mFrom = 'actor';
$user->setItemLoaded( 'actor' );
return $user;
}
/**
* Factory method for creation fom a given UserIdentity, replacing User::newFromIdentity
*
* @since 1.35
*
* @param UserIdentity $userIdentity
* @return User
*/
public function newFromUserIdentity( UserIdentity $userIdentity ): User {
if ( $userIdentity instanceof User ) {
return $userIdentity;
}
$id = $userIdentity->getId();
$name = $userIdentity->getName();
// Cache the $userIdentity we converted last. This avoids redundant conversion
// in cases where we would be converting the same UserIdentity over and over,
// for instance because we need to access data preferences when formatting
// timestamps in a listing.
if (
$this->lastUserFromIdentity
&& $this->lastUserFromIdentity->getId() === $id
&& $this->lastUserFromIdentity->getName() === $name
) {
return $this->lastUserFromIdentity;
}
$this->lastUserFromIdentity = $this->newFromAnyId(
$id === 0 ? null : $id,
$name === '' ? null : $name,
null
);
return $this->lastUserFromIdentity;
}
/**
* Factory method for creation from an ID, name, and/or actor ID, replacing User::newFromAnyId
*
* @note This does not check that the ID, name, and actor ID all correspond to
* the same user.
*
* @since 1.35
*
* @param ?int $userId
* @param ?string $userName
* @param ?int $actorId
* @param bool|string $dbDomain
* @return User
* @throws InvalidArgumentException if none of userId, userName, and actorId are specified
*/
public function newFromAnyId(
?int $userId,
?string $userName,
?int $actorId = null,
$dbDomain = false
): User {
// Stop-gap solution for the problem described in T222212.
// Force the User ID and Actor ID to zero for users loaded from the database
// of another wiki, to prevent subtle data corruption and confusing failure modes.
if ( $dbDomain !== false ) {
$userId = 0;
$actorId = 0;
}
$user = new User;
$user->mFrom = 'defaults';
if ( $actorId !== null ) {
$user->mActorId = $actorId;
if ( $actorId !== 0 ) {
$user->mFrom = 'actor';
}
$user->setItemLoaded( 'actor' );
}
if ( $userName !== null && $userName !== '' ) {
$user->mName = $userName;
$user->mFrom = 'name';
$user->setItemLoaded( 'name' );
}
if ( $userId !== null ) {
$user->mId = $userId;
if ( $userId !== 0 ) {
$user->mFrom = 'id';
}
$user->setItemLoaded( 'id' );
}
if ( $user->mFrom === 'defaults' ) {
throw new InvalidArgumentException(
'Cannot create a user with no name, no ID, and no actor ID'
);
}
return $user;
}
/**
* Factory method to fetch the user for a given email confirmation code, replacing User::newFromConfirmationCode
*
* This code is generated when an account is created or its e-mail address has changed.
* If the code is invalid or has expired, returns null.
*
* @since 1.35
*
* @param string $confirmationCode
* @param int $flags
* @return User|null
*/
public function newFromConfirmationCode(
string $confirmationCode,
int $flags = self::READ_NORMAL
) {
[ $index, $options ] = DBAccessObjectUtils::getDBOptions( $flags );
$db = $this->loadBalancer->getConnectionRef( $index );
$id = $db->selectField(
'user',
'user_id',
[
'user_email_token' => md5( $confirmationCode ),
'user_email_token_expires > ' . $db->addQuotes( $db->timestamp() ),
],
__METHOD__,
$options
);
if ( !$id ) {
return null;
}
return $this->newFromId( (int)$id );
}
/**
* @see User::newFromRow
*
* @since 1.36
*
* @param stdClass $row A row from the user table
* @param array|null $data Further data to load into the object
* @return User
*/
public function newFromRow( $row, $data = null ) {
return User::newFromRow( $row, $data );
}
/**
* @internal for transition from User to Authority as performer concept.
* @param Authority $authority
* @return User
*/
public function newFromAuthority( Authority $authority ): User {
if ( $authority instanceof User ) {
return $authority;
}
return $this->newFromUserIdentity( $authority->getUser() );
}
/**
* Create a placeholder user for an anonymous user who will be upgraded to
* a temporary user. This will throw an exception if temp user autocreation
* is disabled.
*
* @since 1.39
* @return User
*/
public function newTempPlaceholder() {
$user = new User();
$user->setName( $this->userNameUtils->getTempPlaceholder() );
return $user;
}
/**
* Create an unsaved temporary user with a previously acquired name.
*
* @since 1.39
* @param string $name
* @return User
*/
public function newUnsavedTempUser( string $name ) {
$user = new User();
$user->setName( $name );
return $user;
}
}
|