I have backend project in which there's own "parent-to-all exception" exists, something like this (also I have InvalidArgumentException derived from this exception):
class CaliException extends \Exception {
private $caliCode;
public function __construct(string $message = "", string $caliCode = self::UNDEFINED_CODE,
int $code = 0, Throwable $previous = null) {
parent::__construct($message, $code, $previous);
$this->caliCode = $caliCode;
}
function getCaliCode(): string {
return $this->caliCode;
}
}
It's clear from code above, that purpose of custom class is ability to keep string codes. Why do I need string codes? Because I have HTTP API which works in the following way:
- Get JSON request
- Do something
- Produce JSON output with 'state' field which contains either code of a thrown exception or the "200" response. These state codes are quite useful for client which can distinguish different problems and react to them accordingly.
So there's a problem. System above produces code like this:
class UsernameFormatException extends InvalidArgumentException {
public const USERNAME_FORMAT_UNDEFINED_CODE = "DM:USERCA:0001";
public function __construct(string $message = "", string $calistoCode = self::USERNAME_FORMAT_UNDEFINED_CODE,
int $code = 0, \Throwable $previous = null) {
parent::__construct($message, $calistoCode, $code, $previous);
}
}
class PasswordFormatException extends InvalidArgumentException {
public const PASSWORD_FORMAT_UNDEFINED_CODE = "DM:USERCA:0002";
public function __construct(string $message = "", string $calistoCode = self::PASSWORD_FORMAT_UNDEFINED_CODE,
int $code = 0, \Throwable $previous = null) {
parent::__construct($message, $calistoCode, $code, $previous);
}
}
class InvalidLastActivityTimestampException extends InvalidArgumentException {
public const INVALID_TIMESTAMP = "DM:USERCA:0003";
public function __construct(string $message = "", string $calistoCode = self::INVALID_TIMESTAMP,
int $code = 0, \Throwable $previous = null) {
parent::__construct($message, $calistoCode, $code, $previous);
}
}
class InvalidCreationTimestampException extends InvalidArgumentException {
public const INVALID_TIMESTAMP = "DM:USERCA:0004";
public function __construct(string $message = "", string $calistoCode = self::INVALID_TIMESTAMP,
int $code = 0, \Throwable $previous = null) {
parent::__construct($message, $calistoCode, $code, $previous);
}
}
As seen, for each invalid-argument-case I create new CaliException
-derived exception. I consider it as not cool. Is it good? How can I improve the code?
Bonus question: I read that I should throw \InvalidArgumentException
when it's a programmer's mistake, so an exception must be not caught. But in my code there's no \InvalidArgumentException
only my own version of this. Is it acceptable in the context of best practices and so on? And how to distinguish when it's a programmer's mistake and when it's mistake of user (invalid user input)? (After all, any invalid values passed to a function are invalid inputs relatively to this function)