├── LICENSE ├── README.md ├── SECURITY.md ├── composer.json ├── infection.json.dist ├── phpbench.json └── src ├── Http.php ├── HttpSpecification.php ├── NetworkError.php └── RequestFailure.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Minibase 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Net 2 | 3 | A small, modern, [PSR-7](https://www.php-fig.org/psr/psr-7/) compatible 4 | [PSR-17](https://www.php-fig.org/psr/psr-17/) and 5 | [PSR-18](https://www.php-fig.org/psr/psr-18/) network library for PHP, inspired 6 | by [Go's `net`](https://pkg.go.dev/golang.org/x/net) package. 7 | 8 | **Features:** 9 | 10 | - No hard dependencies; 11 | - Favours a [BYOB](https://en.wikipedia.org/wiki/BYOB) approach to requests & responses; 12 | - _Zero configuration_ – pure data and interfaces; 13 | - Testing with third-party APIs made easy; 14 | 15 | ## Setup 16 | 17 | Some basic instructions on how to use this in your own project, or contribute to it as a develper. 18 | 19 | ### Requirements 20 | 21 | - PHP `>=8.0`; 22 | - _Any_ PSR-7 HTTP message implementation; 23 | 24 | ### Installation 25 | 26 | Use composer and access it via your autoloader. 27 | 28 | ```bash 29 | composer require minibase-app/net 30 | ``` 31 | 32 | **For Contributors:** 33 | 34 | Clone the repository and install the development tools to begin running tests for your features & bug fixes. 35 | 36 | ```bash 37 | git clone https://github.com/minibase-app/net.git \ 38 | && cd ./net \ 39 | && make vendor; 40 | ``` 41 | 42 | ## Usage 43 | 44 | Here is a snippet which details the most basic setup and usage of the library. 45 | 46 | As mentioned earlier in the _Requirements_ and _Features_, you can use any PSR-7 47 | implementation of your choosing. In all examples from here on out we use Laminas 48 | for demonstration, but anything is fine, either hand-rolled or from another library. 49 | 50 | ```php 51 | use Laminas\Diactoros\{ Request, Response, Uri }; 52 | use Minibase\Net\Http; 53 | use Psr\Http\Message\{ RequestInterface, ResponseInterface, UriInterface }; 54 | 55 | # Initiate a new HTTP instance with PSR-17 requesst & response factory closures 56 | $http = new Http( 57 | fn (string $method, UriInterface $uri): RequestInterface => 58 | new Request($uri, $method), 59 | fn (mixed $body, int $code, string $reason): ResponseInterface => 60 | new Response($body, $code), 61 | ); 62 | 63 | # Store and fetch a URI instance of your API base URL 64 | $api = new Uri('http://localhost/api'); 65 | 66 | # Make a request to (any) API and specify resources, queries, etc. 67 | $response = $http->get($api->withPath('users')->withQuery('?_limit=1')); 68 | 69 | # Introspect your response 70 | $successful = Http::OK === $response->getStatusCode(); 71 | ``` 72 | 73 | If this isn't a good enough explanation of how this works, don't worry – we will 74 | cover each piece in detail next. 75 | 76 | ### Creating a New HTTP Instance 77 | 78 | Because this library does not provide (yet another) PSR-7 implemenation, you 79 | **MUST** supply one yourself. This means you can make one, use your 80 | company's implementation, or pick your favourite package from the community – 81 | in any case it doesn't matter, we only care about the interface. 82 | 83 | Once you have a PSR-7 implementation it is time to tell the `Http` class how it 84 | should create requests and responses. This is probably the best part of the 85 | library; unlike traditional PHP HTTP "client" libraries, this one does not try 86 | to abstract the request/response away into array-based configuration, but 87 | instead uses them **_as_** a configuration. In other words, _your request is 88 | it's command_, literally... 89 | 90 | Let's take a look. 91 | 92 | ```php 93 | # Here we will make a simple JSON REST API implementation 94 | # Notice that $request and $response are merely PSR-17 factories! 95 | $request = fn (string $method, UriInterface $uri): RequestInterface => 96 | new Request($method, $uri, headers: ['content-type' => 'application/json']); 97 | $response = fn (array $body, int $code, string $reason): ResponseInterface => 98 | new JsonResponse($body, $code); 99 | $http = new Http($request, $response); 100 | ``` 101 | 102 | The astute of you, however, will have noticed that the `$response` closure does 103 | not quite adhere to the [PSR-17 `ResponseFactoryInterface`](https://www.php-fig.org/psr/psr-17/), 104 | as it actually receives a body before it gets the code and reason. Rest assured, 105 | this interface violation is only present here at the constructor – the 106 | `Http::createResponse` method is implemented as expected, and is done so that 107 | you can format as per your requirements. 108 | 109 | ### APIs as URIs 110 | 111 | Most HTTP "client" libraries will have some sort of method signature that asks 112 | for a URL/endpoint as a string, and provide optional parameters for a query 113 | (`GET` parameters). Often, this can lead to improper use the library by 114 | developers placing query parameters directly into the URL/endpoint parameter, 115 | using varrying methods of string concatenation and interpolation, leading to 116 | unstandardized, and messy code. 117 | 118 | In an attempt to prevent this sort of inconsistency from happening (among others 119 | you may have experienced), URLs have been done away with in favour of 120 | first-class `UriInterface` instances. 121 | 122 | ```php 123 | # Store this or a factory for it anywhere along with other API details, like 124 | # auth, headers, etc.! 125 | $github = new Uri('https://api.github.com'); 126 | ``` 127 | 128 | Seems underwhelming at first, but when used with an `Http` instance configured 129 | with some kind of JSON response, we get a fluid and well read set of API calls 130 | in our code. 131 | 132 | ### Making Requests 133 | 134 | Continuing with the examples from above, let's make a request for a GitHub user 135 | profile with ease and style. 136 | 137 | ```php 138 | $user = $http 139 | ->get($github->withPath('users/tpope')) 140 | ->getPayload(); 141 | ``` 142 | 143 | This reads exceptionally well and can be further abstracted as needed, providing 144 | an experience much closer to Redis, query builders, and fluid interfaces alike, 145 | as opposed to traditional HTTP "client" packages. 146 | 147 | ## Motivation 148 | 149 | > _Yeah, but like ... why?_ 150 | 151 | Good question, and similarly answered; _why not_? More specifically, and perhaps 152 | more seriously, HTTP clients in PHP appear to have a history of being designed 153 | very similarily, create their own array-based configuartions, throw exceptions 154 | for valid HTTP responses, and have a "top down" approach to creating a client 155 | (e.g., `new Client('http://api.acme.io')`) that can force you to need multiple 156 | instances for multiple APIs, all resulting in a cumbersome developer & 157 | maintenance experience. 158 | 159 | Another point that this library attempts to address is _The "Client"_. We've all 160 | had the experience of importing multiple `Client` classes, aliasing and 161 | extending all over the place. "Client" is an amiguous term, similarly to `data`, 162 | and `params`, essentially meaningless and not even HTTP specific (a client of 163 | _what_?). Inspired by Go's `net` package, `Http` just seems like a perfect fit. 164 | 165 | Aside from design & usage differences, Net attempts to maintain a slim, 166 | concrete, no dependency (PSRs aside), based API that won't result in dependency 167 | conflicts during a large-scale project upgrade, that can often happen with 168 | legacy projects catching up with latest versions. 169 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | Nothing to be concerned of, yet. If and when this gets more widley used, we can 4 | open a channel up for security concerns and bugs. 5 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minibase-app/net", 3 | "description": "A small and modern PSR-7 & PSR-18 implementation", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Jordan Brauer", 8 | "email": "18744334+jordanbrauer@users.noreply.github.com" 9 | } 10 | ], 11 | "require": { 12 | "php": "^8.0", 13 | "ext-curl": "*", 14 | "ext-json": "*", 15 | "psr/http-client": "^1.0", 16 | "psr/http-factory": "^1.0", 17 | "psr/http-message": "^1.0" 18 | }, 19 | "require-dev": { 20 | "ergebnis/composer-normalize": "^2.15", 21 | "friendsofphp/php-cs-fixer": "^3.0", 22 | "infection/infection": "^0.23.0", 23 | "laminas/laminas-diactoros": "^2.6", 24 | "marcocesarato/php-conventional-changelog": "^1.10", 25 | "nunomaduro/patrol": "^1.0", 26 | "nunomaduro/phpinsights": "^2.0", 27 | "pestphp/pest": "^1.10", 28 | "phpbench/phpbench": "^1.0", 29 | "phpunit/phpunit": "^9.5", 30 | "react/react": "^1.2", 31 | "symfony/var-dumper": "^5.3" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Minibase\\Net\\": "src/" 36 | } 37 | }, 38 | "minimum-stability": "stable" 39 | } 40 | -------------------------------------------------------------------------------- /infection.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "directories": [ 4 | "src" 5 | ], 6 | "excludes": [ 7 | "examples", 8 | "bin" 9 | ] 10 | }, 11 | "testFramework": "pest", 12 | "mutators": { 13 | "@default": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /phpbench.json: -------------------------------------------------------------------------------- 1 | { 2 | "runner.bootstrap": "vendor/autoload.php", 3 | "runner.revs": 1000, 4 | "runner.iterations": 10 5 | } 6 | -------------------------------------------------------------------------------- /src/Http.php: -------------------------------------------------------------------------------- 1 | 9 | * @license MIT 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace Minibase\Net; 16 | 17 | use Closure; 18 | use Psr\Http\Client\ClientInterface; 19 | use Psr\Http\Message\RequestFactoryInterface; 20 | use Psr\Http\Message\RequestInterface; 21 | use Psr\Http\Message\ResponseFactoryInterface; 22 | use Psr\Http\Message\ResponseInterface; 23 | use Psr\Http\Message\StreamInterface; 24 | use Psr\Http\Message\UriInterface; 25 | use TypeError; 26 | 27 | /** 28 | * Make HTTP network requests. 29 | * 30 | * @author Jordan Brauer <18744334+jordanbrauer@users.noreply.github.com> 31 | * @since 0.0.1 32 | */ 33 | final class Http implements HttpSpecification, ClientInterface, RequestFactoryInterface, ResponseFactoryInterface 34 | { 35 | # /** 36 | # * Network transport implementation, such as cURL, streams, etc. 37 | # */ 38 | # private $transport; 39 | 40 | private ?string $body; 41 | 42 | /** 43 | * Create a new HTTP handler. 44 | * 45 | * @param Closure $request PSR-17 request instance factory which generates PSR-7 requests 46 | * @param Closure $request PSR-17 response instance factory which generates PSR-7 responses 47 | */ 48 | public function __construct( 49 | private Closure $request, 50 | private Closure $response, 51 | ) { 52 | # $this->transport = new Curl(); 53 | $this->body = null; 54 | } 55 | 56 | /** 57 | * @inheritDoc 58 | */ 59 | public function connect(UriInterface $uri): ResponseInterface 60 | { 61 | return $this->sendRequest($this->createRequest(self::CONNECT, $uri)); 62 | } 63 | 64 | /** 65 | * @inheritDoc 66 | */ 67 | public function createRequest(string $method, $uri): RequestInterface 68 | { 69 | if (!\is_string($uri) and !$uri instanceof UriInterface) { 70 | throw new TypeError(sprintf( 71 | 'Arguments 2 passed to %s must be of the type %s, %s given', 72 | __FUNCTION__, 73 | implode('|', ['string', UriInterface::class]), 74 | gettype($uri), 75 | )); 76 | } 77 | 78 | return ($this->request)($method, $uri); 79 | } 80 | 81 | /** 82 | * @inheritDoc 83 | */ 84 | public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface 85 | { 86 | return ($this->response)($this->body, $code, $reasonPhrase); 87 | } 88 | 89 | /** 90 | * @inheritDoc 91 | */ 92 | public function delete(UriInterface $uri): ResponseInterface 93 | { 94 | return $this->sendRequest($this->createRequest(self::DELETE, $uri)); 95 | } 96 | 97 | /** 98 | * @inheritDoc 99 | */ 100 | public function get(UriInterface $uri): ResponseInterface 101 | { 102 | return $this->sendRequest($this->createRequest(self::GET, $uri)); 103 | } 104 | 105 | /** 106 | * @inheritDoc 107 | */ 108 | public function head(UriInterface $uri): ResponseInterface 109 | { 110 | return $this->sendRequest($this->createRequest(self::HEAD, $uri)); 111 | } 112 | 113 | /** 114 | * @inheritDoc 115 | */ 116 | public function options(UriInterface $uri): ResponseInterface 117 | { 118 | return $this->sendRequest($this->createRequest(self::OPTIONS, $uri)); 119 | } 120 | 121 | /** 122 | * @inheritDoc 123 | */ 124 | public function patch(UriInterface $uri, string|StreamInterface $body): ResponseInterface 125 | { 126 | return $this->write($this->createRequest(self::PATCH, $uri), $body); 127 | } 128 | 129 | /** 130 | * @inheritDoc 131 | */ 132 | public function post(UriInterface $uri, string|StreamInterface $body): ResponseInterface 133 | { 134 | return $this->write($this->createRequest(self::POST, $uri), $body); 135 | } 136 | 137 | /** 138 | * @inheritDoc 139 | */ 140 | public function put(UriInterface $uri, string|StreamInterface $body): ResponseInterface 141 | { 142 | return $this->write($this->createRequest(self::PUT, $uri), $body); 143 | } 144 | 145 | /** 146 | * @inheritDoc 147 | */ 148 | public function sendRequest(RequestInterface $request): ResponseInterface 149 | { 150 | # IDEA: abstract transports akin to Guzzle handlers (curl, streams, etc) 151 | $handle = curl_init((string) $request->getUri()); 152 | 153 | if (false === $handle) { 154 | throw new RequestFailure($request); 155 | } 156 | 157 | $method = strtoupper(trim($request->getMethod())); 158 | $headers = $request->getHeaders(); 159 | 160 | curl_setopt_array($handle, [ 161 | CURLOPT_ENCODING => '', 162 | CURLOPT_MAXREDIRS => 8, 163 | CURLOPT_TIMEOUT => 30, 164 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 165 | CURLOPT_HTTPHEADER => array_map( 166 | static fn (string $header, array $values) => sprintf('%s: %s', $header, implode(',', $values)), 167 | array_keys($headers), 168 | $headers, 169 | ), 170 | CURLOPT_RETURNTRANSFER => true, 171 | CURLOPT_HEADER => self::HEAD === $method, # TODO: parse headers manually 172 | ]); 173 | curl_setopt($handle, ... match ($method) { 174 | self::GET => [CURLOPT_HTTPGET, true], 175 | self::POST => [CURLOPT_POST, true], 176 | default => [CURLOPT_CUSTOMREQUEST, $method], 177 | }); 178 | 179 | if (in_array($method, [self::POST, self::PUT, self::PATCH], true)) { 180 | curl_setopt( 181 | $handle, 182 | CURLOPT_POSTFIELDS, 183 | $request->getBody()->getContents() 184 | ); 185 | } 186 | 187 | $transfer = curl_exec($handle); 188 | $code = curl_errno($handle); 189 | $error = array_filter([$code, (0 < $code) ? curl_strerror($code) : null]); 190 | 191 | curl_close($handle); 192 | 193 | if (false === $transfer or !empty($error)) { 194 | throw new NetworkError( 195 | $request, 196 | sprintf('(%d) %s', $error[0], $error[1]), 197 | $error[0], 198 | ); 199 | } 200 | 201 | # CURLINFO_EFFECTIVE_URL 202 | # CURLINFO_REQUEST_SIZE 203 | $code = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); 204 | $this->body = $transfer; 205 | 206 | return $this->createResponse($code, self::PHRASES[$code] ?? ''); 207 | } 208 | 209 | /** 210 | * @inheritDoc 211 | */ 212 | public function trace(UriInterface $uri): ResponseInterface 213 | { 214 | return $this->sendRequest($this->createRequest(self::TRACE, $uri)); 215 | } 216 | 217 | /** 218 | * Write a body to a request which modifies a resource on the server. 219 | */ 220 | private function write(RequestInterface $request, string|StreamInterface $body): ResponseInterface 221 | { 222 | if ($body instanceof StreamInterface) { 223 | $request->withBody($body); 224 | } else { 225 | $request->getBody()->write($body); 226 | $request->getBody()->rewind(); 227 | } 228 | 229 | return $this->sendRequest($request); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/HttpSpecification.php: -------------------------------------------------------------------------------- 1 | 9 | * @license MIT 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace Minibase\Net; 16 | 17 | use Psr\Http\Message\ResponseInterface; 18 | use Psr\Http\Message\StreamInterface; 19 | use Psr\Http\Message\UriInterface; 20 | 21 | /** 22 | * An HTTP communication specification. 23 | * 24 | * @author Jordan Brauer <18744334+jordanbrauer@users.noreply.github.com> 25 | * @since v0.0.1 26 | */ 27 | interface HttpSpecification 28 | { 29 | /** 30 | * The HyperText Transfer Protocol (HTTP) 202 Accepted response status code 31 | * indicates that the request has been accepted for processing, but the 32 | * processing has not been completed; in fact, processing may not have 33 | * started yet. The request might or might not eventually be acted upon, as 34 | * it might be disallowed when processing actually takes place. 35 | * 36 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202 37 | */ 38 | public const ACCEPTED = 202; 39 | 40 | /** 41 | * Used inside a response element to avoid repeatedly 42 | * enumerating the internal members of multiple bindings to the same 43 | * collection. 44 | * 45 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status 46 | * @see https://httpstatuses.com/208 47 | */ 48 | public const ALREADY_REPORTED = 208; 49 | 50 | /** 51 | * The HyperText Transfer Protocol (HTTP) 502 Bad Gateway server error 52 | * response code indicates that the server, while acting as a gateway or 53 | * proxy, received an invalid response from the upstream server. 54 | * 55 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502 56 | */ 57 | public const BAD_GATEWAY = 502; 58 | 59 | /** 60 | * The HyperText Transfer Protocol (HTTP) 400 Bad Request response status 61 | * code indicates that the server cannot or will not process the request due 62 | * to something that is perceived to be a client error (e.g., malformed 63 | * request syntax, invalid request message framing, or deceptive request 64 | * routing). 65 | * 66 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400 67 | */ 68 | public const BAD_REQUEST = 400; 69 | 70 | /** 71 | * A non-standard status code introduced by nginx for the case when a client 72 | * closes the connection while nginx is processing the request. 73 | * 74 | * @see https://httpstatuses.com/499 75 | */ 76 | public const CLIENT_CLOSED_REQUEST = 499; 77 | 78 | /** 79 | * The HTTP 409 Conflict response status code indicates a request conflict 80 | * with current state of the target resource. 81 | * 82 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409 83 | */ 84 | public const CONFLICT = 409; 85 | 86 | /** 87 | * The HTTP CONNECT method starts two-way communications with the requested 88 | * resource. It can be used to open a tunnel. 89 | * 90 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT 91 | */ 92 | public const CONNECT = 'CONNECT'; 93 | 94 | /** 95 | * A non-standard status code used to instruct nginx to close the connection 96 | * without sending a response to the client, most commonly used to deny 97 | * malicious or malformed requests. 98 | * 99 | * @see https://httpstatuses.com/444 100 | */ 101 | public const CONNECTION_CLOSED_WITHOUT_RESPONSE = 444; 102 | 103 | /** 104 | * The HTTP 100 Continue informational status response code indicates that 105 | * everything so far is OK and that the client should continue with the 106 | * request or ignore it if it is already finished. 107 | * 108 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100 109 | */ 110 | public const CONTINUE = 100; 111 | 112 | /** 113 | * The HTTP 201 Created success status response code indicates that the 114 | * request has succeeded and has led to the creation of a resource. The new 115 | * resource is effectively created before this response is sent back and the 116 | * new resource is returned in the body of the message, its location being 117 | * either the URL of the request, or the content of the Location header. 118 | * 119 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201 120 | */ 121 | public const CREATED = 201; 122 | 123 | /** 124 | * The HTTP DELETE request method deletes the specified resource. 125 | * 126 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE 127 | */ 128 | public const DELETE = 'DELETE'; 129 | 130 | /** 131 | * The HTTP 103 Early Hints information response status code is primarily 132 | * intended to be used with the Link header to allow the user agent to start 133 | * preloading resources while the server is still preparing a response. 134 | * 135 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103 136 | */ 137 | public const EARLY_HINTS = 103; 138 | 139 | /** 140 | * The HTTP 417 Expectation Failed client error response code indicates that 141 | * the expectation given in the request's Expect header could not be met. 142 | * 143 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/417 144 | */ 145 | public const EXPECTATION_FAILED = 417; 146 | 147 | /** 148 | * The method could not be performed on the resource because the requested 149 | * action depended on another action and that action failed. 150 | * 151 | * @see https://httpstatuses.com/424 152 | */ 153 | public const FAILED_DEPENDENCY = 424; 154 | 155 | /** 156 | * The HTTP 403 Forbidden client error status response code indicates that 157 | * the server understood the request but refuses to authorize it. 158 | * 159 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403 160 | */ 161 | public const FORBIDDEN = 403; 162 | 163 | /** 164 | * The HyperText Transfer Protocol (HTTP) 302 Found redirect status response 165 | * code indicates that the resource requested has been temporarily moved to 166 | * the URL given by the Location header. A browser redirects to this page 167 | * but search engines don't update their links to the resource (in 168 | * 'SEO-speak', it is said that the 'link-juice' is not sent to the new 169 | * URL). 170 | * 171 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302 172 | */ 173 | public const FOUND = 302; 174 | 175 | /** 176 | * The HyperText Transfer Protocol (HTTP) 504 Gateway Timeout server error 177 | * response code indicates that the server, while acting as a gateway or 178 | * proxy, did not get a response in time from the upstream server that it 179 | * needed in order to complete the request. 180 | * 181 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504 182 | */ 183 | public const GATEWAY_TIMEOUT = 504; 184 | 185 | /** 186 | * The **HTTP `GET` method** requests a representation of the specified 187 | * resource. Requests using `GET` should only be used to request data (they 188 | * shouldn't include data). 189 | * 190 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET 191 | */ 192 | public const GET = 'GET'; 193 | 194 | /** 195 | * The HyperText Transfer Protocol (HTTP) 410 Gone client error response 196 | * code indicates that access to the target resource is no longer available 197 | * at the origin server and that this condition is likely to be permanent. 198 | * 199 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410 200 | */ 201 | public const GONE = 410; 202 | 203 | /** 204 | * The HTTP HEAD method requests the headers that would be returned if the 205 | * HEAD request's URL was instead requested with the HTTP GET method. For 206 | * example, if a URL might produce a large download, a HEAD request could 207 | * read its Content-Length header to check the filesize without actually 208 | * downloading the file. 209 | * 210 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD 211 | */ 212 | public const HEAD = 'HEAD'; 213 | 214 | /** 215 | * The HyperText Transfer Protocol (HTTP) 505 HTTP Version Not Supported 216 | * response status code indicates that the HTTP version used in the request 217 | * is not supported by the server. 218 | * 219 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/505 220 | */ 221 | public const HTTP_VERSION_NOT_SUPPORTED = 505; 222 | 223 | /** 224 | * The HTTP 418 I'm a teapot client error response code indicates that the 225 | * server refuses to brew coffee because it is, permanently, a teapot. A 226 | * combined coffee/tea pot that is temporarily out of coffee should instead 227 | * return 503. 228 | * 229 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 230 | */ 231 | public const IM_A_TEAPOT = 418; 232 | 233 | /** 234 | * The server has fulfilled a GET request for the resource, and the response 235 | * is a representation of the result of one or more instance-manipulations 236 | * applied to the current instance. 237 | * 238 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status 239 | * @see https://httpstatuses.com/226 240 | */ 241 | public const IM_USED = 226; 242 | 243 | /** 244 | * The HyperText Transfer Protocol (HTTP) 507 Insufficient Storage response 245 | * status code may be given in the context of the Web Distributed Authoring 246 | * and Versioning (WebDAV) protocol (see RFC 4918). 247 | * 248 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/507 249 | * @see https://datatracker.ietf.org/doc/html/rfc4918 250 | */ 251 | public const INSUFFICIENT_STORAGE = 507; 252 | 253 | // TODO: 4XX codes 254 | 255 | /** 256 | * The HyperText Transfer Protocol (HTTP) 500 Internal Server Error server 257 | * error response code indicates that the server encountered an unexpected 258 | * condition that prevented it from fulfilling the request. 259 | * 260 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500 261 | */ 262 | public const INTERNAL_SERVER_ERROR = 500; 263 | 264 | /** 265 | * The HyperText Transfer Protocol (HTTP) 411 Length Required client error 266 | * response code indicates that the server refuses to accept the request 267 | * without a defined Content-Length header. 268 | * 269 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/411 270 | */ 271 | public const LENGTH_REQUIRED = 411; 272 | 273 | /** 274 | * The source or destination resource of a method is locked. 275 | * 276 | * @see https://httpstatuses.com/423 277 | */ 278 | public const LOCKED = 423; 279 | 280 | /** 281 | * The HyperText Transfer Protocol (HTTP) 508 Loop Detected response status 282 | * code may be given in the context of the Web Distributed Authoring and 283 | * Versioning (WebDAV) protocol. 284 | * 285 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508 286 | */ 287 | public const LOOP_DETECTED = 508; 288 | 289 | /** 290 | * The HyperText Transfer Protocol (HTTP) 405 Method Not Allowed response 291 | * status code indicates that the request method is known by the server but 292 | * is not supported by the target resource. 293 | * 294 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 295 | */ 296 | public const METHOD_NOT_ALLOWED = 405; 297 | 298 | /** 299 | * The request was directed at a server that is not able to produce a 300 | * response. This can be sent by a server that is not configured to produce 301 | * responses for the combination of scheme and authority that are included 302 | * in the request URI. 303 | * 304 | * @see https://httpstatuses.com/421 305 | */ 306 | public const MISDIRECTED_REQUEST = 421; 307 | 308 | /** 309 | * The HyperText Transfer Protocol (HTTP) 301 Moved Permanently redirect 310 | * status response code indicates that the resource requested has been 311 | * definitively moved to the URL given by the Location headers. A browser 312 | * redirects to this page and search engines update their links to the 313 | * resource (in 'SEO-speak', it is said that the 'link-juice' is sent to the 314 | * new URL). 315 | * 316 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301 317 | */ 318 | public const MOVED_PERMANENTLY = 301; 319 | 320 | /** 321 | * Conveys information about multiple resources, for situations where 322 | * multiple status codes might be appropriate (WebDav). 323 | * 324 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status 325 | * @see https://httpstatuses.com/207 326 | */ 327 | public const MULTI_STATUS = 207; 328 | 329 | /** 330 | * The HTTP 300 Multiple Choices redirect status response code indicates 331 | * that the request has more than one possible responses. The user-agent or 332 | * the user should choose one of them. As there is no standardized way of 333 | * choosing one of the responses, this response code is very rarely used. 334 | * 335 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/300 336 | */ 337 | public const MULTIPLE_CHOICES = 300; 338 | 339 | /** 340 | * The HTTP 511 Network Authentication Required response status code 341 | * indicates that the client needs to authenticate to gain network access. 342 | * 343 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/511 344 | */ 345 | public const NETWORK_AUTHENTICATION_REQUIRED = 511; 346 | 347 | /** 348 | * The HTTP 204 No Content success status response code indicates that a 349 | * request has succeeded, but that the client doesn't need to navigate away 350 | * from its current page. 351 | * 352 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204 353 | */ 354 | public const NO_CONTENT = 204; 355 | 356 | /** 357 | * The HTTP 203 Non-Authoritative Information response status indicates that 358 | * the request was successful but the enclosed payload has been modified by 359 | * a transforming proxy from that of the origin server's 200 (OK) response . 360 | * 361 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/203 362 | */ 363 | public const NON_AUTHORITATIVE_INFO = 203; 364 | 365 | /** 366 | * The HyperText Transfer Protocol (HTTP) 406 Not Acceptable client error 367 | * response code indicates that the server cannot produce a response 368 | * matching the list of acceptable values defined in the request's proactive 369 | * content negotiation headers, and that the server is unwilling to supply a 370 | * default representation. 371 | * 372 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406 373 | * @link https://www.youtube.com/watch?v=Q-WHRJPlL5g 374 | */ 375 | public const NOT_ACCEPTABLE = 406; 376 | 377 | /** 378 | * The HyperText Transfer Protocol (HTTP) 510 Not Extended response status 379 | * code is sent in the context of the HTTP Extension Framework, defined in 380 | * RFC 2774. 381 | * 382 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/510 383 | * @see https://datatracker.ietf.org/doc/html/rfc2774 384 | */ 385 | public const NOT_EXTENDED = 510; 386 | 387 | /** 388 | * The HTTP 404 Not Found client error response code indicates that the 389 | * server can't find the requested resource. Links that lead to a 404 page 390 | * are often called broken or dead links and can be subject to link rot. 391 | * 392 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404 393 | * @see https://en.wikipedia.org/wiki/Link_rot 394 | */ 395 | public const NOT_FOUND = 404; 396 | 397 | /** 398 | * The HyperText Transfer Protocol (HTTP) 501 Not Implemented server error 399 | * response code means that the server does not support the functionality 400 | * required to fulfill the request. 401 | * 402 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501 403 | */ 404 | public const NOT_IMPLEMENTED = 501; 405 | 406 | /** 407 | * The HTTP 304 Not Modified client redirection response code indicates that 408 | * there is no need to retransmit the requested resources. It is an implicit 409 | * redirection to a cached resource. This happens when the request method is 410 | * safe, like a GET or a HEAD request, or when the request is conditional 411 | * and uses a If-None-Match or a If-Modified-Since header. 412 | * 413 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304 414 | */ 415 | public const NOT_MODIFIED = 304; 416 | 417 | /** 418 | * The HTTP 200 OK success status response code indicates that the request 419 | * has succeeded. A 200 response is cacheable by default. 420 | * 421 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200 422 | */ 423 | public const OK = 200; 424 | 425 | /** 426 | * The HTTP OPTIONS method requests permitted communication options for a 427 | * given URL or server. A client can specify a URL with this method, or an 428 | * asterisk (*) to refer to the entire server. 429 | * 430 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS 431 | */ 432 | public const OPTIONS = 'OPTIONS'; 433 | 434 | /** 435 | * The HTTP 206 Partial Content success status response code indicates that 436 | * the request has succeeded and has the body contains the requested ranges 437 | * of data, as described in the Range header of the request. 438 | * 439 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206 440 | */ 441 | public const PARTIAL_CONTENT = 206; 442 | 443 | /** 444 | * The **HTTP `PATCH` request method** applies partial modifications to a 445 | * resource. 446 | * 447 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH 448 | */ 449 | public const PATCH = 'PATCH'; 450 | 451 | /** 452 | * The HTTP 413 Payload Too Large response status code indicates that the 453 | * request entity is larger than limits defined by server; the server might 454 | * close the connection or return a Retry-After header field. 455 | * 456 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413 457 | */ 458 | public const PAYLOAD_TOO_LARGE = 413; 459 | 460 | /** 461 | * The HTTP 402 Payment Required is a nonstandard client error status 462 | * response code that is reserved for future use. Sometimes, this code 463 | * indicates that the request can not be processed until the client makes a 464 | * payment. 465 | * 466 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402 467 | */ 468 | public const PAYMENT_REQUIRED = 402; 469 | 470 | /** 471 | * This means that the resource is now permanently located at another URI, 472 | * specified by the `Location:` HTTP Response header. This has the same 473 | * semantics as the `301 Moved Permanently` HTTP response code, with the 474 | * exception that the user agent _must not_ change the HTTP method used: If 475 | * a `POST` was used in the first request, a `POST` must be used in the 476 | * second request. 477 | * 478 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages 479 | */ 480 | public const PERMANENT_REDIRECT = 308; 481 | 482 | /** 483 | * HTTP status code reason map. 484 | * 485 | * @var array 486 | */ 487 | public const PHRASES = [ 488 | self::CONTINUE => 'Continue', 489 | self::SWITCHING_PROTOCOLS => 'Switching Protocols', 490 | self::PROCESSING => 'Processing', 491 | self::EARLY_HINTS => 'Early Hints', 492 | self::OK => 'OK', 493 | self::CREATED => 'Created', 494 | self::ACCEPTED => 'Accepted', 495 | self::NON_AUTHORITATIVE_INFO => 'Non-Authoritative Information', 496 | self::NO_CONTENT => 'No Content', 497 | self::RESET_CONTENT => 'Reset Content', 498 | self::PARTIAL_CONTENT => 'Partial Content', 499 | self::MULTI_STATUS => 'Multi-Status', 500 | self::ALREADY_REPORTED => 'Already Reported', 501 | self::IM_USED => 'IM Used', 502 | self::MULTIPLE_CHOICES => 'Multiple Choices', 503 | self::MOVED_PERMANENTLY => 'Moved Permanently', 504 | self::FOUND => 'Found', 505 | self::SEE_OTHER => 'See Other', 506 | self::NOT_MODIFIED => 'Not Modified', 507 | self::USE_PROXY => 'Use Proxy', 508 | self::SWITCH_PROXY => 'Switch Proxy', 509 | self::TEMPORARY_REDIRECT => 'Temporary Redirect', 510 | self::PERMANENT_REDIRECT => 'Permanent Redirect', 511 | self::BAD_REQUEST => 'Bad Request', 512 | self::UNAUTHORIZED => 'Unauthorized', 513 | self::PAYMENT_REQUIRED => 'Payment Required', 514 | self::FORBIDDEN => 'Forbidden', 515 | self::NOT_FOUND => 'Not Found', 516 | self::METHOD_NOT_ALLOWED => 'Method Not Allowed', 517 | self::NOT_ACCEPTABLE => 'Not Acceptable', 518 | self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', 519 | self::REQUEST_TIMEOUT => 'Request Timeout', 520 | self::CONFLICT => 'Conflict', 521 | self::GONE => 'Gone', 522 | self::LENGTH_REQUIRED => 'Length Required', 523 | self::PRECONDITION_FAILED => 'Precondition Failed', 524 | self::PAYLOAD_TOO_LARGE => 'Payload Too Large', 525 | self::URI_TOO_LONG => 'URI Too Long', 526 | self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', 527 | self::REQUESTED_RANGE_NOT_SATISFIABLE => 'Range Not Satisfiable', 528 | self::EXPECTATION_FAILED => 'Expectation Failed', 529 | self::IM_A_TEAPOT => 'I\'m a teapot', 530 | self::MISDIRECTED_REQUEST => 'Misdirected Request', 531 | self::UNPROCESSABLE_ENTITY => 'Unprocessable Entity', 532 | self::LOCKED => 'Locked', 533 | self::FAILED_DEPENDENCY => 'Failed Dependency', 534 | self::TOO_EARLY => 'Too Early', 535 | self::UPGRADE_REQURED => 'Upgrade Required', 536 | self::PRECONDITION_REQUIRED => 'Precondition Required', 537 | self::TOO_MANY_REQUESTS => 'Too Many Requests', 538 | self::REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', 539 | self::CONNECTION_CLOSED_WITHOUT_RESPONSE => 'Connection Closed Without Response', 540 | self::UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', 541 | self::CLIENT_CLOSED_REQUEST => 'Client Closed Request', 542 | self::INTERNAL_SERVER_ERROR => 'Internal Server Error', 543 | self::NOT_IMPLEMENTED => 'Not Implemented', 544 | self::BAD_GATEWAY => 'Bad Gateway', 545 | self::SERVICE_UNAVAILABLE => 'Service Unavailable', 546 | self::GATEWAY_TIMEOUT => 'Gateway Timeout', 547 | self::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', 548 | self::VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates', 549 | self::INSUFFICIENT_STORAGE => 'Insufficient Storage', 550 | self::LOOP_DETECTED => 'Loop Detected', 551 | self::NOT_EXTENDED => 'Not Extended', 552 | self::NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', 553 | ]; 554 | 555 | /** 556 | * The HTTP POST method sends data to the server. The type of the body of 557 | * the request is indicated by the Content-Type header. 558 | * 559 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST 560 | */ 561 | public const POST = 'POST'; 562 | 563 | /** 564 | * The HyperText Transfer Protocol (HTTP) 412 Precondition Failed client 565 | * error response code indicates that access to the target resource has been 566 | * denied. This happens with conditional requests on methods other than GET 567 | * or HEAD when the condition defined by the If-Unmodified-Since or 568 | * If-None-Match headers is not fulfilled. 569 | * 570 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412 571 | */ 572 | public const PRECONDITION_FAILED = 412; 573 | 574 | /** 575 | * The HTTP 428 Precondition Required response status code indicates that 576 | * the server requires the request to be conditional. 577 | * 578 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/428 579 | */ 580 | public const PRECONDITION_REQUIRED = 428; 581 | 582 | /** 583 | * An interim response used to inform the client that the server has 584 | * accepted the complete request, but has not yet completed it. 585 | * 586 | * @see https://httpstatuses.com/102 587 | */ 588 | public const PROCESSING = 102; 589 | 590 | /** 591 | * The HTTP 407 Proxy Authentication Required client error status response 592 | * code indicates that the request has not been applied because it lacks 593 | * valid authentication credentials for a proxy server that is between the 594 | * browser and the server that can access the requested resource. 595 | * 596 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407 597 | */ 598 | public const PROXY_AUTHENTICATION_REQUIRED = 407; 599 | 600 | /** 601 | * The HTTP PUT request method creates a new resource or replaces a 602 | * representation of the target resource with the request payload. 603 | * 604 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT 605 | */ 606 | public const PUT = 'PUT'; 607 | 608 | /** 609 | * The HTTP 431 Request Header Fields Too Large response status code 610 | * indicates that the server refuses to process the request because the 611 | * request’s HTTP headers are too long. The request may be resubmitted after 612 | * reducing the size of the request headers. 613 | * 614 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431 615 | */ 616 | public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431; 617 | 618 | /** 619 | * The HyperText Transfer Protocol (HTTP) 408 Request Timeout response 620 | * status code means that the server would like to shut down this unused 621 | * connection. It is sent on an idle connection by some servers, even 622 | * without any previous request by the client. 623 | * 624 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408 625 | */ 626 | public const REQUEST_TIMEOUT = 408; 627 | 628 | /** 629 | * The HyperText Transfer Protocol (HTTP) 416 Range Not Satisfiable error 630 | * response code indicates that a server cannot serve the requested ranges. 631 | * The most likely reason is that the document doesn't contain such ranges, 632 | * or that the Range header value, though syntactically correct, doesn't 633 | * make sense. 634 | * 635 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416 636 | */ 637 | public const REQUESTED_RANGE_NOT_SATISFIABLE = 416; 638 | 639 | /** 640 | * The HTTP 205 Reset Content response status tells the client to reset the 641 | * document view, so for example to clear the content of a form, reset a 642 | * canvas state, or to refresh the UI. 643 | * 644 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/205 645 | */ 646 | public const RESET_CONTENT = 205; 647 | 648 | /** 649 | * The HyperText Transfer Protocol (HTTP) 303 See Other redirect status 650 | * response code indicates that the redirects don't link to the newly 651 | * uploaded resources, but to another page (such as a confirmation page or 652 | * an upload progress page). This response code is usually sent back as a 653 | * result of PUT or POST. The method used to display this redirected page is 654 | * always GET. 655 | * 656 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303 657 | */ 658 | public const SEE_OTHER = 303; 659 | 660 | /** 661 | * The HyperText Transfer Protocol (HTTP) 503 Service Unavailable server 662 | * error response code indicates that the server is not ready to handle the 663 | * request. 664 | * 665 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503 666 | */ 667 | public const SERVICE_UNAVAILABLE = 503; 668 | 669 | /** 670 | * This response code is no longer used; it is just reserved. It was used 671 | * in a previous version of the HTTP/1.1 specification. 672 | * 673 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages 674 | */ 675 | public const SWITCH_PROXY = 306; 676 | 677 | /** 678 | * The HTTP 101 Switching Protocols response code indicates the protocol the 679 | * server is switching to as requested by a client which sent the message 680 | * including the Upgrade request header. 681 | * 682 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101 683 | */ 684 | public const SWITCHING_PROTOCOLS = 101; 685 | 686 | /** 687 | * The server sends this response to direct the client to get the requested 688 | * resource at another URI with same method that was used in the prior 689 | * request. This has the same semantics as the `302 Found` HTTP response 690 | * code, with the exception that the user agent _must not_ change the HTTP 691 | * method used: If a `POST` was used in the first request, a `POST` must be 692 | * used in the second request. 693 | * 694 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages 695 | */ 696 | public const TEMPORARY_REDIRECT = 307; 697 | 698 | /** 699 | * The HyperText Transfer Protocol (HTTP) 425 Too Early response status code 700 | * indicates that the server is unwilling to risk processing a request that 701 | * might be replayed, which creates the potential for a replay attack. 702 | * 703 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/425 704 | */ 705 | public const TOO_EARLY = 425; 706 | 707 | /** 708 | * The HTTP 429 Too Many Requests response status code indicates the user 709 | * has sent too many requests in a given amount of time ("rate limiting"). 710 | * 711 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429 712 | */ 713 | public const TOO_MANY_REQUESTS = 429; 714 | 715 | /** 716 | * The HTTP TRACE method performs a message loop-back test along the path to 717 | * the target resource, providing a useful debugging mechanism. 718 | * 719 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/TRACE 720 | */ 721 | public const TRACE = 'TRACE'; 722 | 723 | /** 724 | * The HTTP 401 Unauthorized client error status response code indicates 725 | * that the request has not been applied because it lacks valid 726 | * authentication credentials for the target resource. 727 | * 728 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 729 | */ 730 | public const UNAUTHORIZED = 401; 731 | 732 | /** 733 | * The HyperText Transfer Protocol (HTTP) 451 Unavailable For Legal Reasons 734 | * client error response code indicates that the user requested a resource 735 | * that is not available due to legal reasons, such as a web page for which 736 | * a legal action has been issued. 737 | * 738 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/451 739 | */ 740 | public const UNAVAILABLE_FOR_LEGAL_REASONS = 451; 741 | 742 | /** 743 | * The HyperText Transfer Protocol (HTTP) 422 Unprocessable Entity response 744 | * status code indicates that the server understands the content type of the 745 | * request entity, and the syntax of the request entity is correct, but it 746 | * was unable to process the contained instructions. 747 | * 748 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422 749 | */ 750 | public const UNPROCESSABLE_ENTITY = 422; 751 | 752 | /** 753 | * The HTTP 415 Unsupported Media Type client error response code indicates 754 | * that the server refuses to accept the request because the payload format 755 | * is in an unsupported format. 756 | * 757 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/415 758 | */ 759 | public const UNSUPPORTED_MEDIA_TYPE = 415; 760 | 761 | /** 762 | * The HTTP 426 Upgrade Required client error response code indicates that 763 | * the server refuses to perform the request using the current protocol but 764 | * might be willing to do so after the client upgrades to a different 765 | * protocol. 766 | * 767 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/426 768 | */ 769 | public const UPGRADE_REQURED = 426; 770 | 771 | /** 772 | * The HTTP 414 URI Too Long response status code indicates that the URI 773 | * requested by the client is longer than the server is willing to 774 | * interpret. 775 | * 776 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/414 777 | */ 778 | public const URI_TOO_LONG = 414; 779 | 780 | /** 781 | * Defined in a previous version of the HTTP specification to indicate that 782 | * a requested response must be accessed by a proxy. 783 | * 784 | * @deprecated Due to security concerns regarding in-band configuration of a proxy. 785 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages 786 | */ 787 | public const USE_PROXY = 305; 788 | 789 | /** 790 | * The HyperText Transfer Protocol (HTTP) 506 Variant Also Negotiates 791 | * response status code may be given in the context of Transparent Content 792 | * Negotiation (see RFC 2295). This protocol enables a client to retrieve 793 | * the best variant of a given resource, where the server supports multiple 794 | * variants. 795 | * 796 | * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/506 797 | * @see https://datatracker.ietf.org/doc/html/rfc2295 798 | */ 799 | public const VARIANT_ALSO_NEGOTIATES = 506; 800 | 801 | /** 802 | * Issue an HTTP `CONNECT` request to the given URI. 803 | */ 804 | public function connect(UriInterface $uri): ResponseInterface; 805 | 806 | /** 807 | * Issue an HTTP `DELETE` request to the given URI. 808 | */ 809 | public function delete(UriInterface $uri): ResponseInterface; 810 | 811 | /** 812 | * Issue an HTTP `GET` request to the given URI. 813 | */ 814 | public function get(UriInterface $uri): ResponseInterface; 815 | 816 | /** 817 | * Issue an HTTP `HEAD` request to the given URI. 818 | */ 819 | public function head(UriInterface $uri): ResponseInterface; 820 | 821 | /** 822 | * Issue an HTTP `OPTIONS` request to the given URI. 823 | */ 824 | public function options(UriInterface $uri): ResponseInterface; 825 | 826 | /** 827 | * Issue an HTTP `PATCH` request to the given URI. 828 | */ 829 | public function patch(UriInterface $uri, string|StreamInterface $body): ResponseInterface; 830 | 831 | /** 832 | * Issue an HTTP `POST` request to the given URI. 833 | */ 834 | public function post(UriInterface $uri, string|StreamInterface $body): ResponseInterface; 835 | 836 | /** 837 | * Issue an HTTP `PUT` request to the given URI. 838 | */ 839 | public function put(UriInterface $uri, string|StreamInterface $body): ResponseInterface; 840 | 841 | /** 842 | * Issue an HTTP `TRACE` request to the given URI. 843 | */ 844 | public function trace(UriInterface $uri): ResponseInterface; 845 | } 846 | -------------------------------------------------------------------------------- /src/NetworkError.php: -------------------------------------------------------------------------------- 1 | 9 | * @license MIT 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace Minibase\Net; 16 | 17 | use Psr\Http\Client\NetworkExceptionInterface; 18 | use Psr\Http\Message\RequestInterface; 19 | use RuntimeException; 20 | use Throwable; 21 | 22 | /** 23 | * To be thrown when a request cannot be completed due to network issues. 24 | * 25 | * @author Jordan Brauer <18744334+jordanbrauer@users.noreply.github.com> 26 | * @since 0.0.1 27 | */ 28 | final class NetworkError extends RuntimeException implements NetworkExceptionInterface 29 | { 30 | /** 31 | * Create a new instance of the network exception. 32 | * 33 | * @param RequestInterface $request The request instance that was involved in the error 34 | * @param string $message An (optional) message to use for output and logging 35 | * @param int $code An (optional) error code to help classify the error 36 | * @param Throwable|null $previous If caught, the previously handled error that lead to this one 37 | */ 38 | public function __construct( 39 | private RequestInterface $request, 40 | string $message = 'Request was unable to complete due to network issues', 41 | int $code = 1, 42 | ?Throwable $previous = null, 43 | ) { 44 | parent::__construct($message, $code, $previous); 45 | } 46 | 47 | /** 48 | * @inheritDoc 49 | */ 50 | public function getRequest(): RequestInterface 51 | { 52 | return $this->request; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/RequestFailure.php: -------------------------------------------------------------------------------- 1 | 9 | * @license MIT 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace Minibase\Net; 16 | 17 | use Psr\Http\Client\RequestExceptionInterface; 18 | use Psr\Http\Message\RequestInterface; 19 | use RuntimeException; 20 | use Throwable; 21 | 22 | /** 23 | * To be thrown when a request fails to be sent. 24 | * 25 | * @author Jordan Brauer <18744334+jordanbrauer@users.noreply.github.com> 26 | * @since 0.0.1 27 | */ 28 | final class RequestFailure extends RuntimeException implements RequestExceptionInterface 29 | { 30 | /** 31 | * Create a new instance of the failed request exception. 32 | * 33 | * @param RequestInterface $request The request instance that caused the error 34 | * @param string $message An (optional) message to use for output and logging 35 | * @param int $code An (optional) error code to help classify the error 36 | * @param Throwable|null $previous If caught, the previously handled error that lead to this one 37 | */ 38 | public function __construct( 39 | private RequestInterface $request, 40 | string $message = 'Request was unable to be sent', 41 | int $code = 1, 42 | ?Throwable $previous = null, 43 | ) { 44 | parent::__construct($message, $code, $previous); 45 | } 46 | 47 | /** 48 | * @inheritDoc 49 | */ 50 | public function getRequest(): RequestInterface 51 | { 52 | return $this->request; 53 | } 54 | } 55 | --------------------------------------------------------------------------------