Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like CurrentUser 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 CurrentUser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | class CurrentUser extends User |
||
52 | { |
||
53 | /** |
||
54 | * true if CurrentUser is logged in, otherwise false. |
||
55 | * @var bool |
||
56 | */ |
||
57 | private $loggedIn = false; |
||
58 | |||
59 | /** |
||
60 | * Specifies the timeout for the session in minutes. If the session ID was |
||
61 | * not updated for the last $this->_sessionTimeout minutes, the CurrentUser |
||
62 | * will be logged out automatically if no cookie was set. |
||
63 | * @var int |
||
64 | */ |
||
65 | private $sessionTimeout = PMF_AUTH_TIMEOUT; |
||
66 | |||
67 | /** |
||
68 | * The Session class object |
||
69 | * @var Session |
||
70 | */ |
||
71 | private $session; |
||
72 | |||
73 | /** |
||
74 | * Specifies the timeout for the session-ID in minutes. If the session ID |
||
75 | * was not updated for the last $this->_sessionIdTimeout minutes, it will |
||
76 | * be updated. If set to 0, the session ID will be updated on every click. |
||
77 | * The session ID timeout must not be greater than Session timeout. |
||
78 | * @var int |
||
79 | */ |
||
80 | private $sessionIdTimeout = 1; |
||
81 | |||
82 | /** |
||
83 | * LDAP configuration if available. |
||
84 | * @var array |
||
85 | */ |
||
86 | private $ldapConfig = []; |
||
87 | |||
88 | /** |
||
89 | * Remember me activated or deactivated. |
||
90 | * @var bool |
||
91 | */ |
||
92 | private $rememberMe = false; |
||
93 | |||
94 | /** |
||
95 | * Login successful or auth failure: |
||
96 | * 1 -> success |
||
97 | * 0 -> failure. |
||
98 | * @var int |
||
99 | */ |
||
100 | private $loginState = 1; |
||
101 | |||
102 | /** |
||
103 | * Number of failed login attempts |
||
104 | * @var int |
||
105 | */ |
||
106 | private $loginAttempts = 0; |
||
107 | |||
108 | /** |
||
109 | * Lockout time in seconds |
||
110 | * @var integer |
||
111 | */ |
||
112 | private $lockoutTime = 600; |
||
113 | |||
114 | /** |
||
115 | * Constructor. |
||
116 | * @param Configuration $config |
||
117 | */ |
||
118 | public function __construct(Configuration $config) |
||
124 | |||
125 | /** |
||
126 | * Checks the given login and password in all auth-objects. Returns true |
||
127 | * on success, otherwise false. Raises errors that can be checked using |
||
128 | * the error() method. On success, the CurrentUser instance will be |
||
129 | * labeled as logged in. The name of the successful auth container will |
||
130 | * be stored in the user table. A new auth object may be added by using |
||
131 | * addAuth() method. The given password must not be encrypted, since the |
||
132 | * auth object takes care about the encryption method. |
||
133 | * |
||
134 | * @param string $login Login name |
||
135 | * @param string $password Password |
||
136 | * |
||
137 | * @return bool |
||
138 | */ |
||
139 | public function login(string $login, string $password): bool |
||
257 | |||
258 | /** |
||
259 | * Returns true if CurrentUser is logged in, otherwise false. |
||
260 | * @return bool |
||
261 | */ |
||
262 | public function isLoggedIn(): bool |
||
266 | |||
267 | /** |
||
268 | * Returns false if the CurrentUser object stored in the |
||
269 | * session is valid and not timed out. There are two |
||
270 | * parameters for session timeouts: $this->_sessionTimeout |
||
271 | * and $this->_sessionIdTimeout. |
||
272 | * @return bool |
||
273 | */ |
||
274 | public function sessionIsTimedOut(): bool |
||
281 | |||
282 | /** |
||
283 | * Returns false if the session-ID is not timed out. |
||
284 | * |
||
285 | * @return bool |
||
286 | */ |
||
287 | public function sessionIdIsTimedOut(): bool |
||
294 | |||
295 | /** |
||
296 | * Returns the age of the current session-ID in minutes. |
||
297 | * |
||
298 | * @return float |
||
299 | */ |
||
300 | public function sessionAge(): float |
||
307 | |||
308 | /** |
||
309 | * Returns an associative array with session information stored |
||
310 | * in the user table. The array has the following keys: |
||
311 | * session_id, session_timestamp and ip. |
||
312 | * @return array |
||
313 | */ |
||
314 | public function getSessionInfo(): array |
||
337 | |||
338 | /** |
||
339 | * Updates the session-ID, does not care about time outs. |
||
340 | * Stores session information in the user table: session_id, |
||
341 | * session_timestamp and ip. |
||
342 | * Optionally it should update the 'last login' time. |
||
343 | * Returns true on success, otherwise false. |
||
344 | * |
||
345 | * @param bool $updateLastLogin Update the last login time? |
||
346 | * |
||
347 | * @return bool |
||
348 | */ |
||
349 | public function updateSessionId(bool $updateLastLogin = false): bool |
||
393 | |||
394 | /** |
||
395 | * Saves the CurrentUser into the session. This method |
||
396 | * may be called after a successful login. |
||
397 | * @return void |
||
398 | */ |
||
399 | public function saveToSession() |
||
403 | |||
404 | /** |
||
405 | * Deletes the CurrentUser from the session. The user |
||
406 | * will be logged out. Return true on success, otherwise false. |
||
407 | * @param bool $deleteCookie |
||
408 | * @return bool |
||
409 | */ |
||
410 | public function deleteFromSession(bool $deleteCookie = false): bool |
||
452 | |||
453 | /** |
||
454 | * This static method returns a valid CurrentUser object if there is one |
||
455 | * in the session that is not timed out. The session-ID is updated if |
||
456 | * necessary. The CurrentUser will be removed from the session, if it is |
||
457 | * timed out. If there is no valid CurrentUser in the session or the |
||
458 | * session is timed out, null will be returned. If the session data is |
||
459 | * correct, but there is no user found in the user table, false will be |
||
460 | * returned. On success, a valid CurrentUser object is returned. |
||
461 | * @static |
||
462 | * @param Configuration $config |
||
463 | * @return null|CurrentUser |
||
464 | */ |
||
465 | public static function getFromSession(Configuration $config) |
||
505 | |||
506 | /** |
||
507 | * This static method returns a valid CurrentUser object if there is one |
||
508 | * in the cookie that is not timed out. The session-ID is updated then. |
||
509 | * The CurrentUser will be removed from the session, if it is |
||
510 | * timed out. If there is no valid CurrentUser in the cookie or the |
||
511 | * cookie is timed out, null will be returned. If the cookie is correct, |
||
512 | * but there is no user found in the user table, false will be returned. |
||
513 | * On success, a valid CurrentUser object is returned. |
||
514 | * |
||
515 | * @static |
||
516 | * |
||
517 | * @param Configuration $config |
||
518 | * |
||
519 | * @return null|CurrentUser |
||
520 | */ |
||
521 | public static function getFromCookie(Configuration $config) |
||
546 | |||
547 | /** |
||
548 | * Sets the number of minutes when the current user stored in |
||
549 | * the session gets invalid. |
||
550 | * |
||
551 | * @param float $timeout Timeout |
||
552 | */ |
||
553 | public function setSessionTimeout($timeout) |
||
557 | |||
558 | /** |
||
559 | * Sets the number of minutes when the session-ID needs to be |
||
560 | * updated. By setting the session-ID timeout to zero, the |
||
561 | * session-ID will be updated on each click. |
||
562 | * |
||
563 | * @param float $timeout Timeout |
||
564 | */ |
||
565 | public function setSessionIdTimeout($timeout) |
||
569 | |||
570 | /** |
||
571 | * Enables the remember me decision. |
||
572 | */ |
||
573 | public function enableRememberMe() |
||
577 | |||
578 | /** |
||
579 | * Saves remember me token in the database. |
||
580 | * |
||
581 | * @param string $rememberMe |
||
582 | * |
||
583 | * @return bool |
||
584 | */ |
||
585 | protected function setRememberMe($rememberMe) |
||
601 | |||
602 | /** |
||
603 | * Sets login success/failure. |
||
604 | * |
||
605 | * @param bool $success |
||
606 | * |
||
607 | * @return bool |
||
608 | */ |
||
609 | protected function setSuccess($success) |
||
630 | |||
631 | /** |
||
632 | * Sets IP and session timestamp plus lockout time, success flag to |
||
633 | * false. |
||
634 | * |
||
635 | * @return mixed |
||
636 | */ |
||
637 | View Code Duplication | protected function setLoginAttempt() |
|
659 | |||
660 | /** |
||
661 | * Checks if the last login attempt from current user failed. |
||
662 | * |
||
663 | * @return bool |
||
664 | */ |
||
665 | protected function isFailedLastLoginAttempt() |
||
699 | |||
700 | /** |
||
701 | * Returns the CSRF token from session. |
||
702 | * |
||
703 | * @return string |
||
704 | */ |
||
705 | public function getCsrfTokenFromSession() |
||
709 | |||
710 | /** |
||
711 | * Save CSRF token to session. |
||
712 | */ |
||
713 | public function saveCrsfTokenToSession() |
||
719 | |||
720 | /** |
||
721 | * Deletes CSRF token from session. |
||
722 | */ |
||
723 | protected function deleteCsrfTokenFromSession() |
||
727 | |||
728 | /** |
||
729 | * Creates a CSRF token. |
||
730 | * |
||
731 | * @return string |
||
732 | */ |
||
733 | private function createCsrfToken() |
||
737 | } |
||
738 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.