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 AbstractAuthenticator 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 AbstractAuthenticator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | abstract class AbstractAuthenticator implements |
||
35 | AuthenticatorInterface, |
||
36 | LoggerAwareInterface |
||
37 | { |
||
38 | use LoggerAwareTrait; |
||
39 | |||
40 | const AUTH_BY_PASSWORD = 'password'; |
||
41 | const AUTH_BY_SESSION = 'session'; |
||
42 | const AUTH_BY_TOKEN = 'token'; |
||
43 | |||
44 | /** |
||
45 | * The user that was last authenticated. |
||
46 | * |
||
47 | * @var AuthenticatableInterface|null |
||
48 | */ |
||
49 | protected $authenticatedUser; |
||
50 | |||
51 | /** |
||
52 | * The token that was last authenticated. |
||
53 | * |
||
54 | * @var \Charcoal\User\AuthTokenInterface|null |
||
55 | */ |
||
56 | protected $authenticatedToken; |
||
57 | |||
58 | /** |
||
59 | * The authentication method of the user that was last authenticated. |
||
60 | * |
||
61 | * @var string|null |
||
62 | */ |
||
63 | protected $authenticatedMethod; |
||
64 | |||
65 | /** |
||
66 | * Indicates if the logout method has been called. |
||
67 | * |
||
68 | * @var boolean |
||
69 | */ |
||
70 | protected $isLoggedOut = false; |
||
71 | |||
72 | /** |
||
73 | * The user object type. |
||
74 | * |
||
75 | * @var string |
||
76 | */ |
||
77 | private $userType; |
||
78 | |||
79 | /** |
||
80 | * Store the user model factory instance for the current class. |
||
81 | * |
||
82 | * @var FactoryInterface |
||
83 | */ |
||
84 | private $userFactory; |
||
85 | |||
86 | /** |
||
87 | * The auth-token object type. |
||
88 | * |
||
89 | * @var string |
||
90 | */ |
||
91 | private $tokenType; |
||
92 | |||
93 | /** |
||
94 | * Store the auth-token model factory instance for the current class. |
||
95 | * |
||
96 | * @var FactoryInterface |
||
97 | */ |
||
98 | private $tokenFactory; |
||
99 | |||
100 | /** |
||
101 | * @param array $data Authenticator dependencies. |
||
102 | */ |
||
103 | public function __construct(array $data) |
||
111 | |||
112 | /** |
||
113 | * Retrieve the user object type. |
||
114 | * |
||
115 | * @return string |
||
116 | */ |
||
117 | public function userType() |
||
121 | |||
122 | /** |
||
123 | * Retrieve the user model factory. |
||
124 | * |
||
125 | * @throws RuntimeException If the model factory was not previously set. |
||
126 | * @return FactoryInterface |
||
127 | */ |
||
128 | public function userFactory() |
||
132 | |||
133 | /** |
||
134 | * Create a new user model. |
||
135 | * |
||
136 | * @return \Charcoal\User\Access\AuthenticatableInterface |
||
137 | */ |
||
138 | public function createUser() |
||
142 | |||
143 | /** |
||
144 | * Retrieve the auth-token object type. |
||
145 | * |
||
146 | * @return string |
||
147 | */ |
||
148 | public function tokenType() |
||
152 | |||
153 | /** |
||
154 | * Retrieve the auth-token model factory. |
||
155 | * |
||
156 | * @throws RuntimeException If the token factory was not previously set. |
||
157 | * @return FactoryInterface |
||
158 | */ |
||
159 | public function tokenFactory() |
||
163 | |||
164 | /** |
||
165 | * Create a new auth-token model. |
||
166 | * |
||
167 | * @return \Charcoal\User\AuthTokenInterface |
||
168 | */ |
||
169 | public function createToken() |
||
173 | |||
174 | /** |
||
175 | * Set the user object type (model). |
||
176 | * |
||
177 | * @param string $type The user object type. |
||
178 | * @throws InvalidArgumentException If the user object type parameter is not a string. |
||
179 | * @return void |
||
180 | */ |
||
181 | protected function setUserType($type) |
||
191 | |||
192 | /** |
||
193 | * Set a user model factory. |
||
194 | * |
||
195 | * @param FactoryInterface $factory The factory used to create new user instances. |
||
196 | * @return void |
||
197 | */ |
||
198 | protected function setUserFactory(FactoryInterface $factory) |
||
202 | |||
203 | /** |
||
204 | * Set the authorization token type (model). |
||
205 | * |
||
206 | * @param string $type The auth-token object type. |
||
207 | * @throws InvalidArgumentException If the token object type parameter is not a string. |
||
208 | * @return void |
||
209 | */ |
||
210 | protected function setTokenType($type) |
||
220 | |||
221 | /** |
||
222 | * Set a model factory for token-based authentication. |
||
223 | * |
||
224 | * @param FactoryInterface $factory The factory used to create new auth-token instances. |
||
225 | * @return void |
||
226 | */ |
||
227 | protected function setTokenFactory(FactoryInterface $factory) |
||
231 | |||
232 | /** |
||
233 | * Retrieve the currently authenticated user. |
||
234 | * |
||
235 | * The method will attempt to authenticate a user. |
||
236 | * |
||
237 | * @return AuthenticatableInterface|null |
||
238 | */ |
||
239 | public function user() |
||
251 | |||
252 | /** |
||
253 | * Retrieve the ID for the currently authenticated user. |
||
254 | * |
||
255 | * The method will attempt to authenticate a user. |
||
256 | * |
||
257 | * @return mixed |
||
258 | */ |
||
259 | public function userId() |
||
272 | |||
273 | /** |
||
274 | * Retrieve the currently cached user. |
||
275 | * |
||
276 | * @return AuthenticatableInterface|null |
||
277 | */ |
||
278 | public function getUser() |
||
282 | |||
283 | /** |
||
284 | * Retrieve the ID for the currently cached user. |
||
285 | * |
||
286 | * @return mixed |
||
287 | */ |
||
288 | public function getUserId() |
||
297 | |||
298 | /** |
||
299 | * Set the authenticated user. |
||
300 | * |
||
301 | * Log a user into the application without sessions or cookies. |
||
302 | * |
||
303 | * @param AuthenticatableInterface $user The authenticated user. |
||
304 | * @return void |
||
305 | */ |
||
306 | public function setUser(AuthenticatableInterface $user) |
||
311 | |||
312 | /** |
||
313 | * Set the authenticated user from the given user ID. |
||
314 | * |
||
315 | * Log a user into the application without sessions or cookies. |
||
316 | * |
||
317 | * @param mixed $userId The authenticated user ID. |
||
318 | * @return void |
||
319 | */ |
||
320 | public function setUserById($userId) |
||
330 | |||
331 | /** |
||
332 | * Determine if the current user is authenticated. |
||
333 | * |
||
334 | * @return boolean |
||
335 | */ |
||
336 | public function check() |
||
340 | |||
341 | /** |
||
342 | * Determines if the logout method has been called. |
||
343 | * |
||
344 | * @return boolean TRUE if the logout method has been called, FALSE otherwise. |
||
345 | */ |
||
346 | protected function isLoggedOut() |
||
350 | |||
351 | /** |
||
352 | * Retrieve the authentication method of the current user. |
||
353 | * |
||
354 | * If the current user is authenticated, one of the |
||
355 | * `self::AUTH_BY_*` constants is returned. |
||
356 | * |
||
357 | * @return string|null |
||
358 | */ |
||
359 | public function getAuthenticationMethod() |
||
363 | |||
364 | /** |
||
365 | * Retrieve the authentication token of the current user. |
||
366 | * |
||
367 | * If the current user was authenticated by token, |
||
368 | * the auth token instance is returned. |
||
369 | * |
||
370 | * @return AuthTokenInterface|null |
||
371 | */ |
||
372 | public function getAuthenticationToken() |
||
376 | |||
377 | /** |
||
378 | * Log a user into the application. |
||
379 | * |
||
380 | * @param AuthenticatableInterface $user The authenticated user to log in. |
||
381 | * @param boolean $remember Whether to "remember" the user or not. |
||
382 | * @return void |
||
383 | */ |
||
384 | public function login(AuthenticatableInterface $user, $remember = false) |
||
398 | |||
399 | /** |
||
400 | * Log the user out of the application. |
||
401 | * |
||
402 | * @return void |
||
403 | */ |
||
404 | public function logout() |
||
413 | |||
414 | /** |
||
415 | * Attempt to authenticate a user by session or token. |
||
416 | * |
||
417 | * The user is authenticated via _session ID_ or _auth token_. |
||
418 | * |
||
419 | * @return AuthenticatableInterface|null Returns the authenticated user object |
||
420 | * or NULL if not authenticated. |
||
421 | */ |
||
422 | public function authenticate() |
||
444 | |||
445 | /** |
||
446 | * Attempt to authenticate a user using the given credentials. |
||
447 | * |
||
448 | * @param string $identifier The login ID, part of necessary credentials. |
||
449 | * @param string $password The password, part of necessary credentials. |
||
450 | * @throws InvalidArgumentException If the credentials are invalid or missing. |
||
451 | * @return AuthenticatableInterface|null Returns the authenticated user object |
||
452 | * or NULL if not authenticated. |
||
453 | */ |
||
454 | public function authenticateByPassword($identifier, $password) |
||
507 | |||
508 | /** |
||
509 | * Attempt to authenticate a user using their session ID. |
||
510 | * |
||
511 | * @return AuthenticatableInterface|null Returns the authenticated user object |
||
512 | * or NULL if not authenticated. |
||
513 | */ |
||
514 | protected function authenticateBySession() |
||
550 | |||
551 | /** |
||
552 | * Attempt to authenticate a user using their auth token. |
||
553 | * |
||
554 | * @return AuthenticatableInterface|null Returns the authenticated user object |
||
555 | * or NULL if not authenticated. |
||
556 | */ |
||
557 | protected function authenticateByToken() |
||
604 | |||
605 | /** |
||
606 | * Delete the user data from the session. |
||
607 | * |
||
608 | * @param AuthenticatableInterface|null $user The authenticated user to forget. |
||
609 | * @return void |
||
610 | */ |
||
611 | protected function deleteUserSession(AuthenticatableInterface $user = null) |
||
622 | |||
623 | /** |
||
624 | * Delete the user data from the cookie. |
||
625 | * |
||
626 | * @param AuthenticatableInterface|null $user The authenticated user to forget. |
||
627 | * @return void |
||
628 | */ |
||
629 | protected function deleteUserTokens(AuthenticatableInterface $user = null) |
||
650 | |||
651 | /** |
||
652 | * Delete the user data from the cookie. |
||
653 | * |
||
654 | * @throws InvalidArgumentException If trying to save a user to cookies without an ID. |
||
655 | * @return void |
||
656 | */ |
||
657 | protected function deleteCurrentToken() |
||
673 | |||
674 | /** |
||
675 | * Update the session with the given user. |
||
676 | * |
||
677 | * @param AuthenticatableInterface $user The authenticated user to remember. |
||
678 | * @throws InvalidArgumentException If trying to save a user to session without an ID. |
||
679 | * @return void |
||
680 | */ |
||
681 | protected function updateUserSession(AuthenticatableInterface $user) |
||
692 | |||
693 | /** |
||
694 | * Store the auth token for the given user in a cookie. |
||
695 | * |
||
696 | * @param AuthenticatableInterface $user The authenticated user to remember. |
||
697 | * @throws InvalidArgumentException If trying to save a user to cookies without an ID. |
||
698 | * @return void |
||
699 | */ |
||
700 | protected function updateCurrentToken(AuthenticatableInterface $user) |
||
721 | |||
722 | /** |
||
723 | * Validate the user login credentials are acceptable. |
||
724 | * |
||
725 | * @param string $identifier The user identifier to check. |
||
726 | * @param string $password The user password to check. |
||
727 | * @return boolean Returns TRUE if the credentials are acceptable, or FALSE otherwise. |
||
728 | */ |
||
729 | public function validateLogin($identifier, $password) |
||
733 | |||
734 | /** |
||
735 | * Validate the user identifier is acceptable. |
||
736 | * |
||
737 | * @param string $identifier The login ID. |
||
738 | * @return boolean Returns TRUE if the identifier is acceptable, or FALSE otherwise. |
||
739 | */ |
||
740 | public function validateAuthIdentifier($identifier) |
||
744 | |||
745 | /** |
||
746 | * Validate the user password is acceptable. |
||
747 | * |
||
748 | * @param string $password The password. |
||
749 | * @return boolean Returns TRUE if the password is acceptable, or FALSE otherwise. |
||
750 | */ |
||
751 | public function validateAuthPassword($password) |
||
755 | |||
756 | /** |
||
757 | * Validate the user authentication state is okay. |
||
758 | * |
||
759 | * For example, inactive users can not authenticate. |
||
760 | * |
||
761 | * @param AuthenticatableInterface $user The user to validate. |
||
762 | * @return boolean |
||
763 | */ |
||
764 | public function validateAuthentication(AuthenticatableInterface $user) |
||
768 | |||
769 | /** |
||
770 | * Updates the user's password hash. |
||
771 | * |
||
772 | * Assumes that the existing hash needs to be rehashed. |
||
773 | * |
||
774 | * @param AuthenticatableInterface $user The user to update. |
||
775 | * @param string $password The plain-text password to hash. |
||
776 | * @param boolean $update Whether to persist changes to storage. |
||
777 | * @throws InvalidArgumentException If the password is invalid. |
||
778 | * @return boolean Returns TRUE if the password was changed, or FALSE otherwise. |
||
779 | */ |
||
780 | View Code Duplication | public function rehashUserPassword(AuthenticatableInterface $user, $password, $update = true) |
|
828 | |||
829 | /** |
||
830 | * Updates the user's password hash. |
||
831 | * |
||
832 | * @param AuthenticatableInterface $user The user to update. |
||
833 | * @param string $password The plain-text password to hash. |
||
834 | * @param boolean $update Whether to persist changes to storage. |
||
835 | * @throws InvalidArgumentException If the password is invalid. |
||
836 | * @return boolean Returns TRUE if the password was changed, or FALSE otherwise. |
||
837 | */ |
||
838 | View Code Duplication | public function changeUserPassword(AuthenticatableInterface $user, $password, $update = true) |
|
886 | |||
887 | /** |
||
888 | * Clear the authenticator's internal cache. |
||
889 | * |
||
890 | * @return void |
||
891 | */ |
||
892 | protected function clearAuthenticator() |
||
899 | } |
||
900 |
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.