我应该如何在 php 身份验证类中抛出异常


How should I throw exceptions in a php auth class?

在以下身份验证类示例中抛出异常时,建议针对要处理的每种情况抛出不同的异常,例如:

addUser(...) {
    // normal database code here...
    switch(TRUE) {
        case ($username_exists):
            throw new UserExists('Cannot create new account. Account username ' . $un . ' already exists.');
        case ($email_exists):
            throw new EmailExists('Cannot create new account. Account email ' . $email . ' already exists.');
    }
}
   //to be called externally by...
try {
    $auth->adduser(...);
} catch (UserExists) {
    $output = 'That username is already taken.';
} catch (EmailExists) {
    $output = 'That email is already being used.';
} catch (AuthException $e) {
    $output = $e->getMessage();
}
echo $output;
}

还是建议抛出具有唯一异常代码的异常的一般"类型"? 例如...

addUser(...) {
    // normal database code here...
    switch(TRUE) {
        case ($username_exists):
            throw new AuthException('Cannot create new account. Account username ' . $un . ' already exists.', 10);
        case ($email_exists):
            throw new AuthException('Cannot create new account. Account email ' . $email . ' already exists.', 20);
    }
}
   //to be called externally by...
try {
    $auth->adduser(...);
} catch (AuthException $e) {
    switch($e->getCode()) {
        case 10:
            $output = 'That username is already taken.';
            break;
        case 20:
            $output = 'That email is already being used.';
            break;
        default:
            $output = $e->getMessage();
    }
echo $output;
}

我问是因为我是例外的新手,两种解决方案似乎同样可行。也许完全有其他解决方案?

有趣的旁注:直到收到一些答案后,我才意识到我在问一个"你的偏好是什么"的问题。我期待一种推荐的方法。

我不会说一种方式是对的,一种方式是错误的——如果这个问题击中了一大群人的正确神经,问"这是推荐的还是这种方式的"很容易激起一大锅。

特别是关于您的问题,两者都是引发不同类型异常的有效且可接受的方法 - 我经常看到这两种异常。

然而,在几乎所有语言的大规模应用程序中,我经常看到底部方法 - 这也是我自己的个人风格/偏好。我认为抛出单个"类型"的异常,AuthException,并指定异常代码是非常清晰和简洁的。

如果您希望它更具描述性(从编程的角度来看),您可以使用伪enum * 设置来提供用户友好的描述:

class AuthExceptionCode {
    const USER_EXISTS = 0;
    const EMAIL_EXISTS= 1;
    // ....
}

要使用代码引发异常,请执行以下操作:

throw new AuthException('error message', AuthExceptionCode::USER_EXISTS);

如果你有一个实际的自定义Exception类,即你extend Exception,你可以把代码方向放在类本身:

class AuthException extends Exception {
    const MISC = -1;
    const USER_EXISTS = 0;
    const EMAIL_EXISTS= 1;
    public function __construct($message, $code = self::MISC) {
        switch ($code) {
            case self::USER_EXISTS:
                // example on how to access the codes from within the class
                // ...

* Enumerations不是 PHP 原生的,因此使用 const 是最简单的方法(不使用第 3 方类/插件。

一般来说,我认为最佳实践是例外应包含单个例外条件。 以声压级例外为例。 你会扔InvalidUserArgumentExceptionInvalidEmailArgumentException吗? 不。 您将抛出 SPL InvalidArgumentException,然后根据详细信息更改异常消息(例如"无效用户"或"无效电子邮件")。 也就是说,恕我直言,您应该使用单个AuthException并改变消息(就像您在第二个示例中所做的那样),只是不使用代码和开关,只需直接输出异常消息:

try {
    $auth->adduser(...);
} catch (AuthException $e) {
    $output = $e->getMessage();
}
echo $output;

我更喜欢顶级方式,即对可能发生的每个异常都有一个单独的 catch 语句,但这取决于。.如果你发现自己有很长的异常类名,很难理解它们的含义,那么我会把它们分组一下。