Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method return status: bool, string, const... (PHP)

This question: 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!

like image 308
Piedone Avatar asked Oct 23 '10 17:10

Piedone


3 Answers

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();
}
like image 112
mellowsoon Avatar answered Nov 20 '22 18:11

mellowsoon


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.

like image 27
Brad Mace Avatar answered Nov 20 '22 18:11

Brad Mace


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.

like image 1
Captain Giraffe Avatar answered Nov 20 '22 20:11

Captain Giraffe