HTTP Status Codes as PHP Exception
Tags: php, webdev, api
Published: 2022-11-05 09:57:28

HTTP status codes are not just _404 Not Found_ a _500 Server Internal Error_, there's [plenty more](https://httpwg.org/specs/rfc9110.html#status.codes). During your API development, you will sooner or later face a situation, where you will have use wider variety of HTTP statuses.

You can implement them one by one for your project, or you can use library **[HTTP Exceptions](https://github.com/pavelsterba/http-exceptions)**, which recently released major version 2.0 with all HTTP status codes.

## Installation

Just add it as dependency to your project:

```
composer require pavelsterba/http-exceptions
```

## Usage

All exceptions can be thrown without any additional information - message and code are predefined and you just have to send them to endpoint.

```php
try {
    throw new HttpException\ServerError\InternalServerErrorException();
} catch (HttpException\HttpException $e) {
    echo $e->getMessage(); // 500 Internal Server Error
    echo $e->getCode(); // 500
}
```

If you want to customize error message or attach previous exception, you can of course pass them as parameters to constructor (but you have to pass status code as well), or you can use static `get` method to customize it:

```php
use HttpException\ServerError\InternalServerErrorException;

try {
    // ...
} catch (Exception $ex) {
    throw InternalServerErrorException::get("Server down, sorry.", $ex);
}
```

## Exceptions hierarchy

Since version 2.0, you are able to have your API fully exception driven, because exceptions for Informational, Successfull and Redirection codes are implemented. Full hierarchy or exceptions is:

```
Exception
└─ HttpException\HttpException
   ├─ HttpException\InformationalException
   │  ├─ HttpException\Informational\ContinueException
   │  ├─ HttpException\Informational\SwitchingProtocolsException
   │  ├─ HttpException\Informational\ProcessingException
   │  └─ HttpException\Informational\EarlyHintsException
   ├─ HttpException\SuccessfulException
   │  ├─ HttpException\Successful\OKException
   │  ├─ HttpException\Successful\CreatedException
   │  ├─ HttpException\Successful\AcceptedException
   │  ├─ HttpException\Successful\NonAuthoritativeInformationException
   │  ├─ HttpException\Successful\NoContentException
   │  ├─ HttpException\Successful\ResetContentException
   │  ├─ HttpException\Successful\PartialContentException
   │  ├─ HttpException\Successful\MultiStatusException
   │  ├─ HttpException\Successful\AlreadyReportedException
   │  └─ HttpException\Successful\IMUsedException
   ├─ HttpException\RedirectionException
   │  ├─ HttpException\Redirection\MultipleChoicesException
   │  ├─ HttpException\Redirection\MovedPermanentlyException
   │  ├─ HttpException\Redirection\FoundException
   │  ├─ HttpException\Redirection\SeeOtherException
   │  ├─ HttpException\Redirection\NotModifiedException
   │  ├─ HttpException\Redirection\UseProxyException
   │  ├─ HttpException\Redirection\TemporaryRedirectException
   │  └─ HttpException\Redirection\PermanentRedirectException
   ├─ HttpException\ClientErrorException
   │  ├─ HttpException\ClientError\BadRequestException
   │  ├─ HttpException\ClientError\UnauthorizedException
   │  ├─ HttpException\ClientError\PaymentRequiredException
   │  ├─ HttpException\ClientError\ForbiddenException
   │  ├─ HttpException\ClientError\NotFoundException
   │  ├─ HttpException\ClientError\MethodNotAllowedException
   │  ├─ HttpException\ClientError\NotAcceptableException
   │  ├─ HttpException\ClientError\ProxyAuthenticationRequiredException
   │  ├─ HttpException\ClientError\RequestTimeoutException
   │  ├─ HttpException\ClientError\ConflictException
   │  ├─ HttpException\ClientError\GoneException
   │  ├─ HttpException\ClientError\LengthRequiredException
   │  ├─ HttpException\ClientError\PreconditionFailedException
   │  ├─ HttpException\ClientError\PayloadTooLargeException
   │  ├─ HttpException\ClientError\URITooLongException
   │  ├─ HttpException\ClientError\UnsupportedMediaTypeException
   │  ├─ HttpException\ClientError\RangeNotSatisfiableException
   │  ├─ HttpException\ClientError\ExpectationFailedException
   │  ├─ HttpException\ClientError\IMaTeapotException
   │  ├─ HttpException\ClientError\MisdirectedRequestException
   │  ├─ HttpException\ClientError\UnprocessableEntityException
   │  ├─ HttpException\ClientError\LockedException
   │  ├─ HttpException\ClientError\FailedDependencyException
   │  ├─ HttpException\ClientError\TooEarlyException
   │  ├─ HttpException\ClientError\UpgradeRequiredException
   │  ├─ HttpException\ClientError\PreconditionRequiredException
   │  ├─ HttpException\ClientError\TooManyRequestsException
   │  ├─ HttpException\ClientError\RequestHeaderFieldsTooLargeException
   │  └─ HttpException\ClientError\UnavailableForLegalReasonsException
   └─ HttpException\ServerErrorException
      ├─ HttpException\ServerError\InternalServerErrorException
      ├─ HttpException\ServerError\NotImplementedException
      ├─ HttpException\ServerError\BadGatewayException
      ├─ HttpException\ServerError\ServiceUnavailableException
      ├─ HttpException\ServerError\GatewayTimeoutException
      ├─ HttpException\ServerError\HTTPVersionNotSupportedException
      ├─ HttpException\ServerError\VariantAlsoNegotiatesException
      ├─ HttpException\ServerError\InsufficientStorageException
      ├─ HttpException\ServerError\LoopDetectedException
      ├─ HttpException\ServerError\NotExtendedException
      └─ HttpException\ServerError\NetworkAuthenticationRequiredException
```

With this in mind, you can catch them as you like:

```php
try {
    // ...
} catch (HttpException\ClientError\NotFoundException $e) {
    // Only NotFoundException catched
} catch (HttpException\ClientErrorException $e) {
    // All ClientError exceptions
} catch (HttpException\HttpException $e) {
    // All HTTP exceptions from library
} catch (Exception $e) {
    // Everything else...
}
```