重构困境:PHP中的用户帐户功能

时间:2012-02-19 19:30:49

标签: php oop class refactoring account

我正在用PHP编写用户帐户系统,重点是安全性,但我仍然坚持将其重构为更清洁,更实用的东西。

问题是尝试将用户帐户功能组合在一起,但是在单独的类中。我现在这样做的方式是,有一些带有公共静态方法的类都将$ username作为第一个参数,并且它们使用其他静态方法,同样传递与第一个参数相同的$ username。显然,OOP是一种更好的方法,特别是因为每个方法都必须使用用户名来进行数据库查询,并且必须处理提供的用户名根本不存在的情况。

将所有内容放在“用户”类中的问题是它会很大,并且在同一个文件中会有很多完全不相关的代码。例如,更改密码代码不需要调用与验证用户电子邮件或更改用户(不相关)设置相关的方法。

我可以有两个类:User和AuthenticatedUser,AuthenticatedUser继承User。用户可以在没有用户登录的情况下实现所有可能的功能,AuthenticatedUser将是需要登录的所有功能 - 例如访问用户的加密数据。维护代码可以使用User的对象,GUI代码将在用户登录后获得AuthenticatedUser对象。但我不想将所有功能都塞进用户。

以下是与用户帐户相关的一些操作的列表,只是为了说明它们不适合一个类的原因:

  • 登录
  • 锁定用户如果> = X尝试过去Y分钟(包括确定用户是否被锁定的方法,为尝试计数添加勾号等)。
  • 使用电子邮件循环绕过锁定
  • 更改密码
  • 管理员更改密码(强制)
  • 密码重置(电子邮件循环)(包括启动重置,验证电子邮件令牌的方法等)
  • 设置/获取以纯文本格式存储的用户数据
  • 设置/获取加密的用户数据
  • 设置/获取帐户设置(允许密码重置?,在密码失败时锁定帐户?,在允许电子邮件循环的情况下绕过锁定等等)理想情况下,这些设置应设置/接近行为依赖于的代码它们。
  • 在正确的情况下获取用户的用户名(正如他们在创建帐户时指定的那样)
  • 电子邮件验证
  • 许多功能仅由特定代码使用。例如。获取用户“salt”用于密钥派生。
  • 更多......

我在想我可以做一些像继承User的PasswordChanger类,并实现密码更改功能。所以它看起来像:

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

这是我迄今为止提出的最佳解决方案。它允许我将相关的功能分解到它自己的文件中,并且不必传递用户名。但它似乎很奇怪 - 我以前从未见过这样的代码。它迫使超类知道它的所有子类,这是糟糕的设计。有什么想法吗?

编辑:另一种避免继承的方法是拥有一个拥有用户的PasswordChanger。像:

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

“PasswordChanger 用户”与“PasswordChanger 用户”。前者实际上是有道理的,所以我喜欢这种方式更好。

3 个答案:

答案 0 :(得分:2)

嗯,你已经有了一个好的开始,而且你正在问正确的问题。但是没有一个答案。设计是一门艺术,而不是一门科学。听起来好像你正在尝试重新设计而不是重构。如果你开始重构你的代码只是对你的设计可能最终结果的一个简单的想法,你会发现它更容易(重构的真正好资源是book by Michael Feathers。当你重构更多时而你应该发现一个设计从黑暗中浮现出来!而且这个设计通常不是你认为在你开始时最终会想到的。

哦,btw继承是OO最常用的方面之一。如果您正在考虑继承,请停下来再思考。如果你仍然认为你需要继承,那就停下来思考一下。如果您仍然认为您需要继承,那么您可能会......

答案 1 :(得分:1)

PasswordChangerUser没有任何共同点,所以你应该避免继承它们。

在这种情况下我做的是:

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

}

基本上将用户类所需的所有对象放在其属性

您当然可以使用$this->pswChanger->method();

之类的内容访问它们

答案 2 :(得分:1)

您可以尝试使用装饰器模式。您可以通过添加validateDecorator,notifyDecorator等来扩展UserDecorator。

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();