views:

40

answers:

3

Hi all!

This question: http://stackoverflow.com/questions/356248/best-way-to-return-status-flag-and-message-from-a-method-in-java is similar to mine, however I would do it in PHP, not Java (which could make a slight difference here).

The problem:

There's a method that can have either a successful outcome (this may change to be more successful ones) or a "problematic" one. The latter means the operation failed, but it is also important to know why. Imagine a method of an Authentication class:

public function login($name, $password)
{
    if (successful_authentication)
    {
        report success
    }
    else
    {
        report failure, explain why (e.g. wrong name/pass, user banned)
    }
}

It would be trivial to return true and false for success and failure, but how to report the cause of failure?

Possible solutions:

  • Return true or false and write another method (getStatus()) to get the specific problem: this feels a bit awkward to me
  • Use exceptions: since it's not exceptional for a user to be banned (an exception would be if the user would die while typing as another author on this site has pointed out) the usage of exceptions here in these cases would be plain wrong (however the method could throw a database exception if a query fails)
  • Return true on success and a string on failure with an error code indicating the problem: with PHP it's possible this way to have nice clean blocks as follows:

    $loginStatus = $auth->login('name', 'pass');
    if ($loginStatus === true)
    {
        doSomething();
    }
    else
    {
        if ($loginStatus == 'wrong_login_data')
        ...
        elseif ($loginStatus == 'banned')
        ...
        // or with an array full of error messages:
        echo $erroMessages[$loginStatus];
    }
    
  • Return a general status (state) object: very elegant solution and also future-proof (no problem if the number of statuses varies or later additional data should be returned), maybe the best one:

    $loginStatus = $auth->login('name', 'pass')
    if ($loginStatus->isSuccess)
    ...
    else
        echo $errorMessages[$loginStatus->errorCode]; // $errorMessages as by the previous example
    
  • Either of the above two, but not with plain string but class constants:

    $loginStatus = $auth->login('name', 'pass')
    if ($loginStatus->isSuccess)
    ...
    elseif ($loginStatus->errorCode == Auth::ERR_USER_BANNED)
    ...
    

    With this it would be unnecessary to explain the error codes in the documentation and would feel more "natural" as well (at least for me).

The question:

What would you use (of the above ones or any other solution)? What have been proven on the long run to be a good way?

Thank you in advance!

+1  A: 

Throw an exception. If checking the return value is required, then any failure to do so will allow unauthorized logins. Failure to check for an exception will blow the whole thing up, which seems preferable in this case.

In less critical spots if I was doing a check against an object that already existed I might just return the result code and have a separate function to retrieve the message, but I wouldn't go out of my way to make something into an object just to use that technique. Otherwise I'd go with a status object containing the result code and message.

bemace
Thanks for your reply! Note that this would only be a login method, but not a method to check if the user is already authenticated (not to do the latter would indeed cause troubles). I don't think the failure to log in would be an exception (just think about mistyping your password - it's not a very rare case) and thus it would be a usage of exceptions for normal program flow. But let's not just think about a login method, I would like to know what's the best technique with any method that can have successful and failed states.
Piedone
@piedone - edited
bemace
A: 

I would go with a variation of your "Return a general status (state) object" where the state is described by your auth object.

So $auth->login('name', 'pass') is a simple bool, and $auth->getState() is an enum or string (intended for end user perhaps) describing the state.

Captain Giraffe
A: 

This is how PEAR has been doing it for as long as I can remember. So this is a tried and tested method.

class LoginError {
    private $reason;

    public function __construct($reason)
    {
        $this->reason = $reason;
    }

    public function getReason()
    {
        return $this->reason;
    }
}

function isError($response)
{
    return ($response instanceof LoginError);
}

public function login($name, $password)
{
    if (successful_authentication)
    {
        return true;
    }
    else
    {
        return new LoginError('The password is incorrect');
    }
}


$loginStatus = login('name', 'pass');
if (!isError($loginStatus))
{
    doSomething();
}
else
{
    echo $loginStatus->getReason();
}
mellowsoon