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 AbstractUser 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 AbstractUser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
26 | abstract class AbstractUser extends Content implements |
||
27 | AuthenticatableInterface, |
||
28 | UserInterface |
||
29 | { |
||
30 | use AuthenticatableTrait; |
||
31 | |||
32 | /** |
||
33 | * The email address should be unique and mandatory. |
||
34 | * |
||
35 | * It is also used as the login name. |
||
36 | * |
||
37 | * @var string |
||
38 | */ |
||
39 | private $email; |
||
40 | |||
41 | /** |
||
42 | * The password is stored encrypted in the (database) storage. |
||
43 | * |
||
44 | * @var string|null |
||
45 | */ |
||
46 | private $password; |
||
47 | |||
48 | /** |
||
49 | * The display name serves as a human-readable identifier for the user. |
||
50 | * |
||
51 | * @var string|null |
||
52 | */ |
||
53 | private $displayName; |
||
54 | |||
55 | /** |
||
56 | * Roles define a set of tasks a user is allowed or denied from performing. |
||
57 | * |
||
58 | * @var string[] |
||
59 | */ |
||
60 | private $roles = []; |
||
61 | |||
62 | /** |
||
63 | * The timestamp of the latest (successful) login. |
||
64 | * |
||
65 | * @var DateTimeInterface|null |
||
66 | */ |
||
67 | private $lastLoginDate; |
||
68 | |||
69 | /** |
||
70 | * The IP address during the latest (successful) login. |
||
71 | * |
||
72 | * @var string|null |
||
73 | */ |
||
74 | private $lastLoginIp; |
||
75 | |||
76 | /** |
||
77 | * The timestamp of the latest password change. |
||
78 | * |
||
79 | * @var DateTimeInterface|null |
||
80 | */ |
||
81 | private $lastPasswordDate; |
||
82 | |||
83 | /** |
||
84 | * The IP address during the latest password change. |
||
85 | * |
||
86 | * @var string|null |
||
87 | */ |
||
88 | private $lastPasswordIp; |
||
89 | |||
90 | /** |
||
91 | * The token value for the "remember me" session. |
||
92 | * |
||
93 | * @var string|null |
||
94 | */ |
||
95 | private $loginToken; |
||
96 | |||
97 | /** |
||
98 | * The user preferences. |
||
99 | * |
||
100 | * @var mixed |
||
101 | */ |
||
102 | private $preferences; |
||
103 | |||
104 | /** |
||
105 | * @param string $email The user email. |
||
106 | * @throws InvalidArgumentException If the email is not a string. |
||
107 | * @return self |
||
108 | */ |
||
109 | public function setEmail($email) |
||
121 | |||
122 | /** |
||
123 | * @return string |
||
124 | */ |
||
125 | public function getEmail() |
||
129 | |||
130 | /** |
||
131 | * @param string|null $password The user password. Encrypted in storage. |
||
132 | * @throws InvalidArgumentException If the password is not a string (or null, to reset). |
||
133 | * @return self |
||
134 | */ |
||
135 | public function setPassword($password) |
||
149 | |||
150 | /** |
||
151 | * @return string|null |
||
152 | */ |
||
153 | public function getPassword() |
||
157 | |||
158 | /** |
||
159 | * @param string|null $name The user's display name. |
||
160 | * @return self |
||
161 | */ |
||
162 | public function setDisplayName($name) |
||
168 | |||
169 | /** |
||
170 | * @return string|null |
||
171 | */ |
||
172 | public function getDisplayName() |
||
176 | |||
177 | /** |
||
178 | * @param string|string[]|null $roles The ACL roles this user belongs to. |
||
179 | * @throws InvalidArgumentException If the roles argument is invalid. |
||
180 | * @return self |
||
181 | */ |
||
182 | public function setRoles($roles) |
||
203 | |||
204 | /** |
||
205 | * @return string[] |
||
206 | */ |
||
207 | public function getRoles() |
||
211 | |||
212 | /** |
||
213 | * @param string|DateTimeInterface|null $lastLoginDate The last login date. |
||
214 | * @throws InvalidArgumentException If the ts is not a valid date/time. |
||
215 | * @return self |
||
216 | */ |
||
217 | View Code Duplication | public function setLastLoginDate($lastLoginDate) |
|
245 | |||
246 | /** |
||
247 | * @return DateTimeInterface|null |
||
248 | */ |
||
249 | public function getLastLoginDate() |
||
253 | |||
254 | /** |
||
255 | * @param string|integer|null $ip The last login IP address. |
||
256 | * @throws InvalidArgumentException If the IP is not an IP string, an integer, or null. |
||
257 | * @return self |
||
258 | */ |
||
259 | View Code Duplication | public function setLastLoginIp($ip) |
|
280 | |||
281 | /** |
||
282 | * Get the last login IP in x.x.x.x format |
||
283 | * |
||
284 | * @return string|null |
||
285 | */ |
||
286 | public function getLastLoginIp() |
||
290 | |||
291 | /** |
||
292 | * @param string|DateTimeInterface|null $lastPasswordDate The last password date. |
||
293 | * @throws InvalidArgumentException If the passsword date is not a valid DateTime. |
||
294 | * @return self |
||
295 | */ |
||
296 | View Code Duplication | public function setLastPasswordDate($lastPasswordDate) |
|
324 | |||
325 | /** |
||
326 | * @return DateTimeInterface|null |
||
327 | */ |
||
328 | public function getLastPasswordDate() |
||
332 | |||
333 | /** |
||
334 | * @param integer|string|null $ip The last password IP. |
||
335 | * @throws InvalidArgumentException If the IP is not null, an integer or an IP string. |
||
336 | * @return self |
||
337 | */ |
||
338 | View Code Duplication | public function setLastPasswordIp($ip) |
|
359 | |||
360 | /** |
||
361 | * Get the last password change IP in x.x.x.x format |
||
362 | * |
||
363 | * @return string|null |
||
364 | */ |
||
365 | public function getLastPasswordIp() |
||
369 | |||
370 | /** |
||
371 | * @param string|null $token The login token. |
||
372 | * @throws InvalidArgumentException If the token is not a string. |
||
373 | * @return self |
||
374 | */ |
||
375 | public function setLoginToken($token) |
||
392 | |||
393 | /** |
||
394 | * @return string|null |
||
395 | */ |
||
396 | public function getLoginToken() |
||
400 | |||
401 | /** |
||
402 | * @param mixed $preferences Structure of user preferences. |
||
403 | * @return self |
||
404 | */ |
||
405 | public function setPreferences($preferences) |
||
411 | |||
412 | /** |
||
413 | * @return mixed |
||
414 | */ |
||
415 | public function getPreferences() |
||
419 | |||
420 | |||
421 | |||
422 | // Extends Charcoal\User\Access\AuthenticatableTrait |
||
423 | // ========================================================================= |
||
424 | |||
425 | /** |
||
426 | * Retrieve the name of the unique ID for the user. |
||
427 | * |
||
428 | * @return string |
||
429 | */ |
||
430 | public function getAuthIdKey() |
||
434 | |||
435 | /** |
||
436 | * Retrieve the name of the login username for the user. |
||
437 | * |
||
438 | * @return string |
||
439 | */ |
||
440 | public function getAuthIdentifierKey() |
||
444 | |||
445 | /** |
||
446 | * Retrieve the name of the login password for the user. |
||
447 | * |
||
448 | * @return string |
||
449 | */ |
||
450 | public function getAuthPasswordKey() |
||
454 | |||
455 | /** |
||
456 | * Retrieve the name of the login token for the user. |
||
457 | * |
||
458 | * @return string |
||
459 | */ |
||
460 | public function getAuthLoginTokenKey() |
||
464 | |||
465 | |||
466 | // Extends Charcoal\Validator\ValidatableTrait |
||
467 | // ========================================================================= |
||
468 | |||
469 | /** |
||
470 | * Validate the user model. |
||
471 | * |
||
472 | * @param ValidatorInterface $v Optional. A custom validator object to use for validation. If null, use object's. |
||
473 | * @return boolean |
||
474 | */ |
||
475 | public function validate(ValidatorInterface &$v = null) |
||
489 | |||
490 | /** |
||
491 | * Validate the username or email address. |
||
492 | * |
||
493 | * @return boolean |
||
494 | */ |
||
495 | protected function validateLoginRequired() |
||
518 | |||
519 | /** |
||
520 | * Validate the username or email address is unique. |
||
521 | * |
||
522 | * @return boolean |
||
523 | */ |
||
524 | protected function validateLoginUnique() |
||
548 | } |
||
549 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.