1

I've been searching for a way to use a string instead of an integer for the code of a custom exception, but much to my surprise this seems impossible to do!

I'd like to be able to throw such an exception:

throw new CustomException( "user_not_found", "User not found" );

so then I can test it as follows with PHPUnit:

$this->expectExceptionCode( "user_not_found" ); 
$User = new User(100); // 100 is a valid id for a non-existing user 
$User->delete_user(); // This method throws the above CustomException

What I've been doing so far is passing a custom context array to the exception:

throw new CustomException( "User not found", [ "debug" => "user_not_found" ] );

and then my test looks like this:

try {
    $User = new User(100);
    $User->delete_user();
} catch( \Throwable $e ) {
    $this->assertEquals( "user_not_found", $e->getContext()["debug"] );
}

But I would really prefer the first solution because it looks cleaner to me.

1
  • 1
    When you extend Exception, you can add whatever custom fields you want, including in the constructor. If you want to use the numeric code, you can do that with class constants, and you can use subclasses to hide these from callers, so UserNotFound automatically calls the parent with 1001 or whatever. Or, ignore the codes and inspect on the specific exception by type
    – Chris Haas
    Commented Dec 4, 2021 at 12:29

1 Answer 1

0

First, create base exception, like:

class MyBaseException extends \Exception {
    public function __constructor(string $key, string $message) {
        parent::__construct($message, ['debug' => $key]);
    }
}

Then, implement other exceptions, like:

class UserNotFoundException extends MyBaseException {
    public function __constructor() {
        parent::__constructor('user_not_found', 'User not found');
    }
}

Finally, write tests for class (not key and/or code), like:

$this->expectException(UserNotFoundException::class);

// Your throwing code 

$User = new User(100);
$User->delete_user();

See also: How to assert that an exception was thrown?

5
  • Yes, this is a solution but in this way I would have to create a custom exception for every error I want to test, while I would really prefer to have only one custom exception to reuse with different code/message for each error.
    – grazdev
    Commented Dec 4, 2021 at 13:34
  • @grazianodev for that renameMyBaseException to CustomException and throw. So that at least throwing is the way you want, but PHPUnit does not support catching the way you want.
    – Top-Master
    Commented Dec 4, 2021 at 13:50
  • @grazianodev of course for you it seems bad to have a class for each message; that's because you throw just once, but other developers will hate you, because even if anyone is trying to throw exact same Exception, they are forced to copy/paste. And whenever wanting to slightly change message, they are forced to do search-and-replace in entire folder.
    – Top-Master
    Commented Dec 4, 2021 at 14:02
  • Haha well actually I'm a self-taught programmer working on a personal project, with no intention to work with other developers. Anyway, I have exceptions that I throw only once, wouldn't it be overkill to create a very specific custom exception to throw only once?
    – grazdev
    Commented Dec 4, 2021 at 14:24
  • 1
    @grazianodev "following correct structure and/or design-pattern" is never overkill (but if concerned about speed, PHP has opcache-extension, try that out if possible).
    – Top-Master
    Commented Dec 4, 2021 at 14:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.