Refactoring The Dilemma: User Account Functionality in PHP

I am writing a user account system in PHP with an emphasis on security, but I'm stuck with reorganizing it into something cleaner and more convenient.

The problem is combining the functions of the user account together, but in separate classes. The way I'm doing it now is a bunch of classes with public static methods that all take $ username as the first parameter, and they use other static methods, passing the same $ username as the first parameter. Obviously, OOP is the best way to go, especially since each method must strtolower the username in order to query the database, and must handle the case when the provided username does not exist at all.

The problem with putting everything in the "User" class is that it will be huge, and there will be a lot of completely unrelated code in one file. For example, the Change password code does not require calling methods related to checking the user's email or changing user (unrelated) settings.

I could have two classes: User and AuthenticatedUser, where AuthenticatedUser inherits the user. The user will implement all the functions that are possible without the user entering the system, and AuthenticatedUser - these are all functions that require entering the system - for example, access to encrypted user data. The service code can use user objects, and the GUI code will receive an AuthenticatedUser object after the user logs in. But I do not want to embed all functions in User.

Here is a list of some of the operations associated with a user account to show why they will not be in the same class:

  • To come in
  • Block the user if> = X attempts in the last Y minutes (includes methods for determining whether the user is blocked, adding a checkmark to the number of attempts, etc.).
  • Email Bypass Blocking
  • Change password
  • Administrator Change Password (Strength)
  • Password reset (email loop) (including methods for starting reset, checking email token, etc.)
  • Set / get user data stored in clear text
  • Set / Get Encrypted User Data
  • Set / receive account settings (is the reset password allowed ?, block the account in case of password failures ?, bypasses the lock using the email cycle?) Etc.) Ideally, these settings should be set / installed next to the code, behavior which depends on them.
  • Get the username of the user in the appropriate case (since they indicated it when creating the account)
  • Email Authentication
  • A lot of functionality used only by a certain code. For instance. get the user's salt used for key derivation.
  • More details ...

I thought I could do something like the PasswordChanger class, which inherits the user, and implements password change functions. So it will look like this:

$proper = $user->getProperName(); ... $passChanger = $user->getPasswordChanger(); $result = $passChanger->changePassword($oldPass, $newPass); ... $userLockout = $user->getUserLockout(); $result = $userLockout->isUserLockedOut(); ... $userEV = $user->getEmailValidation(); $result = $userEV->tryValidateEmail($token); ... 

This is the best solution that I have come up with so far. This allows me to split related functions into my own file and get rid of the need to pass in a username. But it seems really strange - I have never seen such code before. This makes the superclass aware of all its subclasses, which is poorly designed. Any ideas?

Edit: An alternative to avoiding inheritance would be to have a PasswordChanger that has a User. How:

 $passChanger = new PasswordChanger($user); $passChanger->changePassword($old, $new); // uses $user->getUsername() ... $emailValidator = new EmailValdiator($user); $emailValidator->tryValidate($token); 

"PasswordChanger has a user" and "PasswordChanger is a user." The first one actually makes sense, so I like it a little better.

+4
source share
3 answers

Well, you started out well and you are asking the right questions. However, there is no single answer. Design is an art, not a science. It looks like you're trying to reverse engineer , not refactoring . It may seem easier for you if you start by refactoring your code only with no idea where your project might end (a really good resource for refactoring is Michael Persian's book . When you reorganize more and more, you should find that out of the dark A design is looming! And often this design is not the one that, in your opinion, you will finish when you start.

Oh, and btw inheritance is one of the most commonly used aspects of OO. If you are thinking about inheritance, stop and think again. If you still think you need inheritance, stop and think more about it. If you still think that you need inheritance, it is possible that you ...

+2
source

PasswordChanger and User have nothing in common, so you should avoid inheriting them.

In this case, I am doing something like:

 class User { var $pswChanger; //> -> Object of PasswordChanger } 

Essentially, all the objects you need for the User class in its attribute

Of course, you can access them with $this->pswChanger->method();

+1
source

You can try using the decorator pattern. You can extend UserDecorator by adding validateDecorator, notifyDecorator, etc.

 class User { private $name; private $password; public function __construct($name, $pw) { $this->name = $name ; $this->password = $pw; } public function getUsername() { return $this->name; } public function getPassword() { return $this->password; } } class UserDecorator { protected $user; protected $name; protected $password; public function __construct(User $user) { $this->user= $user; $this->setValues(); } public function setValues() { $this->name = $this->user->getUsername(); $this->password = $this->user->getPassword(); } public function getUsername() { return $this->name; } public function getPassword() { return $this->password; } } class PasswordChangeDecorator extends UserDecorator { private $userdecorator; public function __construct(UserDecorator $user) { $this->userdecorator = $user; } public function changePassWord($newPw) { $this->userdecorator->password = $newPw; } } $user = new User("some random user name", "oldpw"); $userdecorator = new UserDecorator($user); $pwdecorator = new PasswordChangeDecorator($userdecorator); print $userdecorator->getPassword() . "\n"; // set the new password $pwdecorator->changePassWord("newpw"); // test the outcome print $userdecorator->getPassword(); 
+1
source

Source: https://habr.com/ru/post/1397315/


All Articles