| Total Complexity | 41 | 
| Total Lines | 430 | 
| Duplicated Lines | 0 % | 
| Changes | 1 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like User often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use User, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 23 | class User extends DataObject | ||
| 24 | { | ||
| 25 | const STATUS_ACTIVE = 'Active'; | ||
| 26 | const STATUS_DEACTIVATED = 'Deactivated'; | ||
| 27 | const STATUS_NEW = 'New'; | ||
| 28 | |||
| 29 | private static CommunityUser $community; | ||
| 30 | |||
| 31 | private $username; | ||
| 32 | private $email; | ||
| 33 | private $status = self::STATUS_NEW; | ||
| 34 | private $onwikiname; | ||
| 35 | private $lastactive = "0000-00-00 00:00:00"; | ||
| 36 | private $forcelogout = 0; | ||
| 37 | private $forceidentified = null; | ||
| 38 | private $confirmationdiff = 0; | ||
| 39 | /** @var User Cache variable of the current user - it's never going to change in the middle of a request. */ | ||
| 40 | private static $currentUser; | ||
| 41 | #region Object load methods | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Gets the currently logged in user | ||
| 45 | * | ||
| 46 | * @param PdoDatabase $database | ||
| 47 | * | ||
| 48 | * @return User|CommunityUser | ||
| 49 | */ | ||
| 50 | public static function getCurrent(PdoDatabase $database) | ||
| 51 |     { | ||
| 52 |         if (self::$currentUser === null) { | ||
| 53 | $sessionId = WebRequest::getSessionUserId(); | ||
| 54 | |||
| 55 |             if ($sessionId !== null) { | ||
| 56 | /** @var User $user */ | ||
| 57 | $user = self::getById($sessionId, $database); | ||
| 58 | |||
| 59 |                 if ($user === false) { | ||
|  | |||
| 60 | self::$currentUser = new CommunityUser(); | ||
| 61 | } | ||
| 62 |                 else { | ||
| 63 | self::$currentUser = $user; | ||
| 64 | } | ||
| 65 | } | ||
| 66 |             else { | ||
| 67 | $anonymousCoward = new CommunityUser(); | ||
| 68 | |||
| 69 | self::$currentUser = $anonymousCoward; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | return self::$currentUser; | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Gets a user by their user ID | ||
| 78 | * | ||
| 79 | * Pass -1 to get the community user. | ||
| 80 | * | ||
| 81 | * @param int|null $id | ||
| 82 | * @param PdoDatabase $database | ||
| 83 | * | ||
| 84 | * @return User|false | ||
| 85 | */ | ||
| 86 | public static function getById($id, PdoDatabase $database) | ||
| 96 | } | ||
| 97 | |||
| 98 | public static function getCommunity(): CommunityUser | ||
| 99 |     { | ||
| 100 |         if (!isset(self::$community)) { | ||
| 101 | self::$community = new CommunityUser(); | ||
| 102 | } | ||
| 103 | |||
| 104 | return self::$community; | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Gets a user by their username | ||
| 109 | * | ||
| 110 | * @param string $username | ||
| 111 | * @param PdoDatabase $database | ||
| 112 | * | ||
| 113 | * @return CommunityUser|User|false | ||
| 114 | */ | ||
| 115 | public static function getByUsername($username, PdoDatabase $database) | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Gets a user by their on-wiki username. | ||
| 137 | * | ||
| 138 | * @param string $username | ||
| 139 | * @param PdoDatabase $database | ||
| 140 | * | ||
| 141 | * @return User|false | ||
| 142 | */ | ||
| 143 | public static function getByOnWikiUsername($username, PdoDatabase $database) | ||
| 158 | } | ||
| 159 | |||
| 160 | #endregion | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Saves the current object | ||
| 164 | * | ||
| 165 | * @throws Exception | ||
| 166 | */ | ||
| 167 | public function save() | ||
| 168 |     { | ||
| 169 |         if ($this->isNew()) { | ||
| 170 | // insert | ||
| 171 | $statement = $this->dbObject->prepare(<<<SQL | ||
| 172 | INSERT INTO `user` ( | ||
| 173 | username, email, status, onwikiname, | ||
| 174 | lastactive, forcelogout, forceidentified, | ||
| 175 | confirmationdiff | ||
| 176 | ) VALUES ( | ||
| 177 | :username, :email, :status, :onwikiname, | ||
| 178 | :lastactive, :forcelogout, null, | ||
| 179 | :confirmationdiff | ||
| 180 | ); | ||
| 181 | SQL | ||
| 182 | ); | ||
| 183 |             $statement->bindValue(":username", $this->username); | ||
| 184 |             $statement->bindValue(":email", $this->email); | ||
| 185 |             $statement->bindValue(":status", $this->status); | ||
| 186 |             $statement->bindValue(":onwikiname", $this->onwikiname); | ||
| 187 |             $statement->bindValue(":lastactive", $this->lastactive); | ||
| 188 |             $statement->bindValue(":forcelogout", $this->forcelogout); | ||
| 189 |             $statement->bindValue(":confirmationdiff", $this->confirmationdiff); | ||
| 190 | |||
| 191 |             if ($statement->execute()) { | ||
| 192 | $this->id = (int)$this->dbObject->lastInsertId(); | ||
| 193 | } | ||
| 194 |             else { | ||
| 195 | throw new Exception($statement->errorInfo()); | ||
| 196 | } | ||
| 197 | } | ||
| 198 |         else { | ||
| 199 | // update | ||
| 200 | $statement = $this->dbObject->prepare(<<<SQL | ||
| 201 | UPDATE `user` SET | ||
| 202 | username = :username, email = :email, | ||
| 203 | status = :status, | ||
| 204 | onwikiname = :onwikiname, | ||
| 205 | lastactive = :lastactive, | ||
| 206 | forcelogout = :forcelogout, | ||
| 207 | confirmationdiff = :confirmationdiff, | ||
| 208 | updateversion = updateversion + 1 | ||
| 209 | WHERE id = :id AND updateversion = :updateversion; | ||
| 210 | SQL | ||
| 211 | ); | ||
| 212 | |||
| 213 |             $statement->bindValue(':id', $this->id); | ||
| 214 |             $statement->bindValue(':updateversion', $this->updateversion); | ||
| 215 | |||
| 216 |             $statement->bindValue(':username', $this->username); | ||
| 217 |             $statement->bindValue(':email', $this->email); | ||
| 218 |             $statement->bindValue(':status', $this->status); | ||
| 219 |             $statement->bindValue(':onwikiname', $this->onwikiname); | ||
| 220 |             $statement->bindValue(':lastactive', $this->lastactive); | ||
| 221 |             $statement->bindValue(':forcelogout', $this->forcelogout); | ||
| 222 |             $statement->bindValue(':confirmationdiff', $this->confirmationdiff); | ||
| 223 | |||
| 224 |             if (!$statement->execute()) { | ||
| 225 | throw new Exception($statement->errorInfo()); | ||
| 226 | } | ||
| 227 | |||
| 228 |             if ($statement->rowCount() !== 1) { | ||
| 229 | throw new OptimisticLockFailedException(); | ||
| 230 | } | ||
| 231 | |||
| 232 | $this->updateversion++; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | #region properties | ||
| 237 | |||
| 238 | /** | ||
| 239 | * Gets the tool username | ||
| 240 | * @return string | ||
| 241 | */ | ||
| 242 | public function getUsername() | ||
| 243 |     { | ||
| 244 | return $this->username; | ||
| 245 | } | ||
| 246 | |||
| 247 | /** | ||
| 248 | * Sets the tool username | ||
| 249 | * | ||
| 250 | * @param string $username | ||
| 251 | */ | ||
| 252 | public function setUsername($username) | ||
| 253 |     { | ||
| 254 | $this->username = $username; | ||
| 255 | |||
| 256 | // If this isn't a brand new user, then it's a rename, force the logout | ||
| 257 |         if (!$this->isNew()) { | ||
| 258 | $this->forcelogout = 1; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | /** | ||
| 263 | * Gets the user's email address | ||
| 264 | * @return string | ||
| 265 | */ | ||
| 266 | public function getEmail() | ||
| 269 | } | ||
| 270 | |||
| 271 | /** | ||
| 272 | * Sets the user's email address | ||
| 273 | * | ||
| 274 | * @param string $email | ||
| 275 | */ | ||
| 276 | public function setEmail($email) | ||
| 277 |     { | ||
| 278 | $this->email = $email; | ||
| 279 | } | ||
| 280 | |||
| 281 | /** | ||
| 282 | * Gets the status (Active, New, Deactivated, etc) of the user. | ||
| 283 | * @return string | ||
| 284 | */ | ||
| 285 | public function getStatus() | ||
| 286 |     { | ||
| 287 | return $this->status; | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * @param string $status | ||
| 292 | */ | ||
| 293 | public function setStatus($status) | ||
| 294 |     { | ||
| 295 | $this->status = $status; | ||
| 296 | } | ||
| 297 | |||
| 298 | /** | ||
| 299 | * Gets the user's on-wiki name | ||
| 300 | * @return string | ||
| 301 | */ | ||
| 302 | public function getOnWikiName() | ||
| 303 |     { | ||
| 304 | return $this->onwikiname; | ||
| 305 | } | ||
| 306 | |||
| 307 | /** | ||
| 308 | * Sets the user's on-wiki name | ||
| 309 | * | ||
| 310 | * This can have interesting side-effects with OAuth. | ||
| 311 | * | ||
| 312 | * @param string $onWikiName | ||
| 313 | */ | ||
| 314 | public function setOnWikiName($onWikiName) | ||
| 315 |     { | ||
| 316 | $this->onwikiname = $onWikiName; | ||
| 317 | } | ||
| 318 | |||
| 319 | /** | ||
| 320 | * Gets the last activity date for the user | ||
| 321 | * | ||
| 322 | * @return string | ||
| 323 | * @todo This should probably return an instance of DateTime | ||
| 324 | */ | ||
| 325 | public function getLastActive() | ||
| 326 |     { | ||
| 327 | return $this->lastactive; | ||
| 328 | } | ||
| 329 | |||
| 330 | /** | ||
| 331 | * Gets the user's forced logout status | ||
| 332 | * | ||
| 333 | * @return bool | ||
| 334 | */ | ||
| 335 | public function getForceLogout() | ||
| 336 |     { | ||
| 337 | return $this->forcelogout == 1; | ||
| 338 | } | ||
| 339 | |||
| 340 | /** | ||
| 341 | * Sets the user's forced logout status | ||
| 342 | * | ||
| 343 | * @param bool $forceLogout | ||
| 344 | */ | ||
| 345 | public function setForceLogout($forceLogout) | ||
| 346 |     { | ||
| 347 | $this->forcelogout = $forceLogout ? 1 : 0; | ||
| 348 | } | ||
| 349 | |||
| 350 | /** | ||
| 351 | * Gets the user's confirmation diff. Unused if OAuth is in use. | ||
| 352 | * @return int the diff ID | ||
| 353 | */ | ||
| 354 | public function getConfirmationDiff() | ||
| 357 | } | ||
| 358 | |||
| 359 | /** | ||
| 360 | * Sets the user's confirmation diff. | ||
| 361 | * | ||
| 362 | * @param int $confirmationDiff | ||
| 363 | */ | ||
| 364 | public function setConfirmationDiff($confirmationDiff) | ||
| 365 |     { | ||
| 366 | $this->confirmationdiff = $confirmationDiff; | ||
| 367 | } | ||
| 368 | |||
| 369 | #endregion | ||
| 370 | |||
| 371 | #region user access checks | ||
| 372 | |||
| 373 | public function isActive() | ||
| 376 | } | ||
| 377 | |||
| 378 | /** | ||
| 379 | * DO NOT USE FOR TESTING IDENTIFICATION STATUS. | ||
| 380 | * | ||
| 381 | * This only returns any overrides in the database for identification status, | ||
| 382 | * and is thus not suitable on its own to determine if a user is identified. | ||
| 383 | * | ||
| 384 | * Most (all?) users should have a null value here; this is only here as an | ||
| 385 | * emergency override in case things go horribly, horribly wrong. For | ||
| 386 | * example, when WMF completely change the layout of the ID noticeboard. | ||
| 387 | */ | ||
| 388 | public function getForceIdentified(): ?bool | ||
| 395 | } | ||
| 396 | |||
| 397 | /** | ||
| 398 | * Tests if the user is new | ||
| 399 | * @return bool | ||
| 400 | * @category Security-Critical | ||
| 401 | */ | ||
| 402 | public function isNewUser() | ||
| 403 |     { | ||
| 404 | return $this->status == self::STATUS_NEW; | ||
| 405 | } | ||
| 406 | |||
| 407 | /** | ||
| 408 | * Tests if the user has been deactivated and is unable to access the tool | ||
| 409 | * @return bool | ||
| 410 | * @category Security-Critical | ||
| 411 | */ | ||
| 412 | public function isDeactivated(): bool | ||
| 413 |     { | ||
| 414 | return $this->status == self::STATUS_DEACTIVATED; | ||
| 415 | } | ||
| 416 | |||
| 417 | /** | ||
| 418 | * Tests if the user is the community user | ||
| 419 | * | ||
| 420 | * @todo decide if this means logged out. I think it usually does. | ||
| 421 | * @return bool | ||
| 422 | * @category Security-Critical | ||
| 423 | */ | ||
| 424 | public function isCommunityUser() | ||
| 425 |     { | ||
| 426 | return false; | ||
| 427 | } | ||
| 428 | |||
| 429 | #endregion | ||
| 430 | |||
| 431 | /** | ||
| 432 | * Gets the approval date of the user | ||
| 433 | * @return DateTime|false | ||
| 434 | */ | ||
| 435 | public function getApprovalDate() | ||
| 453 | } | ||
| 454 | } | ||
| 455 |