|
3 | 3 | ## Content |
4 | 4 |
|
5 | 5 | - [Setup](#setup) |
6 | | -- [Curl - simple http client (CurlExtension)](#curl) |
7 | | -- [SAPI - fake request (CliRequestExtension)](#sapi) |
| 6 | +- [HTTP Client](#http-client) |
| 7 | + - [IClient Interface](#iclient-interface) |
| 8 | + - [CurlClient](#curlclient) |
| 9 | + - [CurlBuilder](#curlbuilder) |
| 10 | + - [FakeClient](#fakeclient) |
| 11 | + - [Request & Response](#request--response) |
| 12 | +- [SAPI - fake request (SapiRequestExtension)](#sapi) |
8 | 13 | - [BasicAuth - simple basic authentication](#basic-authentication) |
9 | 14 | - [Useful classes](#useful-classes) |
10 | 15 | - [Url](#url) |
11 | 16 |
|
12 | 17 | ## Setup |
13 | 18 |
|
| 19 | +**Requirements:** PHP 8.2+ |
| 20 | + |
14 | 21 | ```bash |
15 | 22 | composer require contributte/http |
16 | 23 | ``` |
17 | 24 |
|
18 | | -## Curl |
| 25 | +## HTTP Client |
| 26 | + |
| 27 | +This package provides a simple HTTP client abstraction with cURL implementation. |
| 28 | + |
| 29 | +### IClient Interface |
| 30 | + |
| 31 | +The `IClient` interface defines a common contract for HTTP clients: |
| 32 | + |
| 33 | +```php |
| 34 | +use Contributte\Http\Client\IClient; |
| 35 | +use Contributte\Http\Client\Request; |
| 36 | +use Contributte\Http\Client\Response; |
| 37 | + |
| 38 | +interface IClient |
| 39 | +{ |
| 40 | + public function request(Request $request): Response; |
| 41 | + public function get(string $url, array $headers = []): Response; |
| 42 | + public function post(string $url, $body = null, array $headers = []): Response; |
| 43 | + public function put(string $url, $body = null, array $headers = []): Response; |
| 44 | + public function delete(string $url, array $headers = []): Response; |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +### CurlClient |
19 | 49 |
|
20 | | -There is a prepared simple cURL client in this package. |
| 50 | +The `CurlClient` is the default implementation using cURL. |
21 | 51 |
|
22 | | -You have to register it at first. |
| 52 | +Register it via the DI extension: |
23 | 53 |
|
24 | 54 | ```neon |
25 | 55 | extensions: |
26 | 56 | curl: Contributte\Http\DI\CurlExtension |
27 | 57 | ``` |
28 | 58 |
|
29 | | -Extension registers [`Contributte\Http\Curl\CurlClient`](https://github.com/contributte/http/blob/master/src/Curl/CurlClient.php) as a service. |
| 59 | +Or use it directly: |
| 60 | + |
| 61 | +```php |
| 62 | +use Contributte\Http\Curl\CurlClient; |
| 63 | + |
| 64 | +$client = new CurlClient(); |
| 65 | + |
| 66 | +// Simple GET request |
| 67 | +$response = $client->get('https://api.example.com/users'); |
| 68 | + |
| 69 | +// POST with JSON body |
| 70 | +$response = $client->post('https://api.example.com/users', json_encode(['name' => 'John'])); |
| 71 | + |
| 72 | +// Using Request object |
| 73 | +$request = new Request('https://api.example.com/users', Request::METHOD_POST); |
| 74 | +$request->setBody(json_encode(['name' => 'John'])); |
| 75 | +$request->addHeader('Authorization', 'Bearer token'); |
| 76 | +$response = $client->request($request); |
| 77 | + |
| 78 | +// Configure default headers |
| 79 | +$client->setDefaultHeaders([ |
| 80 | + 'Content-Type' => 'application/json', |
| 81 | + 'Accept' => 'application/json', |
| 82 | +]); |
| 83 | + |
| 84 | +// Add a single default header |
| 85 | +$client->addDefaultHeader('X-Api-Key', 'your-api-key'); |
| 86 | +``` |
| 87 | + |
| 88 | +### CurlBuilder |
| 89 | + |
| 90 | +The `CurlBuilder` provides a fluent interface for building HTTP requests: |
| 91 | + |
| 92 | +```php |
| 93 | +use Contributte\Http\Curl\CurlBuilder; |
| 94 | +use Contributte\Http\Curl\CurlClient; |
| 95 | + |
| 96 | +$client = new CurlClient(); |
| 97 | + |
| 98 | +// Simple GET request |
| 99 | +$request = CurlBuilder::create() |
| 100 | + ->get('https://api.example.com/users') |
| 101 | + ->build(); |
| 102 | +$response = $client->request($request); |
| 103 | + |
| 104 | +// POST with JSON body |
| 105 | +$request = CurlBuilder::create() |
| 106 | + ->post('https://api.example.com/users') |
| 107 | + ->setJsonBody(['name' => 'John', 'email' => ' [email protected]']) |
| 108 | + ->build(); |
| 109 | +$response = $client->request($request); |
| 110 | + |
| 111 | +// With authentication |
| 112 | +$request = CurlBuilder::create() |
| 113 | + ->get('https://api.example.com/protected') |
| 114 | + ->setBearerToken('your-jwt-token') |
| 115 | + ->build(); |
| 116 | + |
| 117 | +// Or basic auth |
| 118 | +$request = CurlBuilder::create() |
| 119 | + ->get('https://api.example.com/protected') |
| 120 | + ->setBasicAuth('username', 'password') |
| 121 | + ->build(); |
| 122 | + |
| 123 | +// With custom headers and options |
| 124 | +$request = CurlBuilder::create() |
| 125 | + ->post('https://api.example.com/upload') |
| 126 | + ->addHeader('X-Custom', 'value') |
| 127 | + ->setContentType('multipart/form-data') |
| 128 | + ->setTimeout(60) |
| 129 | + ->setFollowRedirects(true) |
| 130 | + ->setSslVerify(true) |
| 131 | + ->setUserAgent('MyApp/1.0') |
| 132 | + ->build(); |
| 133 | + |
| 134 | +// Form data |
| 135 | +$request = CurlBuilder::create() |
| 136 | + ->post('https://example.com/form') |
| 137 | + ->setFormBody(['username' => 'john', 'password' => 'secret']) |
| 138 | + ->build(); |
| 139 | +``` |
| 140 | + |
| 141 | +Available builder methods: |
| 142 | + |
| 143 | +| Method | Description | |
| 144 | +|--------|-------------| |
| 145 | +| `get($url)` | Set GET method and URL | |
| 146 | +| `post($url)` | Set POST method and URL | |
| 147 | +| `put($url)` | Set PUT method and URL | |
| 148 | +| `delete($url)` | Set DELETE method and URL | |
| 149 | +| `patch($url)` | Set PATCH method and URL | |
| 150 | +| `head($url)` | Set HEAD method and URL | |
| 151 | +| `options($url)` | Set OPTIONS method and URL | |
| 152 | +| `addHeader($name, $value)` | Add a header | |
| 153 | +| `setHeaders($headers)` | Set all headers | |
| 154 | +| `setContentType($type)` | Set Content-Type header | |
| 155 | +| `setAccept($type)` | Set Accept header | |
| 156 | +| `setAuthorization($value)` | Set Authorization header | |
| 157 | +| `setBearerToken($token)` | Set Bearer token authentication | |
| 158 | +| `setBasicAuth($user, $pass)` | Set Basic authentication | |
| 159 | +| `setBody($body)` | Set raw body | |
| 160 | +| `setJsonBody($data)` | Set JSON body (auto-sets Content-Type) | |
| 161 | +| `setFormBody($data)` | Set form body (auto-sets Content-Type) | |
| 162 | +| `setTimeout($seconds)` | Set request timeout | |
| 163 | +| `setFollowRedirects($follow)` | Enable/disable redirect following | |
| 164 | +| `setSslVerify($verify)` | Enable/disable SSL verification | |
| 165 | +| `setUserAgent($agent)` | Set User-Agent header | |
| 166 | +| `setOption($key, $value)` | Set a cURL option | |
| 167 | +| `build()` | Build and return the Request object | |
| 168 | + |
| 169 | +### FakeClient |
| 170 | + |
| 171 | +The `FakeClient` is a test double for mocking HTTP requests in tests: |
| 172 | + |
| 173 | +```php |
| 174 | +use Contributte\Http\Client\FakeClient; |
| 175 | +use Contributte\Http\Client\Request; |
| 176 | + |
| 177 | +$client = new FakeClient(); |
| 178 | + |
| 179 | +// Queue responses (FIFO) |
| 180 | +$client->respondWith('Hello World', 200); |
| 181 | +$client->respondWithJson(['status' => 'ok', 'data' => [1, 2, 3]]); |
| 182 | +$client->respondWithError('Connection failed', 500); |
| 183 | + |
| 184 | +// Make requests |
| 185 | +$response1 = $client->get('https://example.com'); // Returns "Hello World" |
| 186 | +$response2 = $client->get('https://example.com'); // Returns JSON response |
| 187 | +$response3 = $client->get('https://example.com'); // Returns error response |
| 188 | + |
| 189 | +// Record and inspect requests |
| 190 | +$client->get('https://api.example.com/users'); |
| 191 | +$client->post('https://api.example.com/users', '{"name":"John"}'); |
| 192 | + |
| 193 | +// Get all recorded requests |
| 194 | +$requests = $client->getRecordedRequests(); |
| 195 | + |
| 196 | +// Get last request |
| 197 | +$lastRequest = $client->getLastRequest(); |
| 198 | +echo $lastRequest->getUrl(); // https://api.example.com/users |
| 199 | +echo $lastRequest->getMethod(); // POST |
| 200 | +echo $lastRequest->getBody(); // {"name":"John"} |
| 201 | + |
| 202 | +// Assertions |
| 203 | +$client->assertRequestCount(2); |
| 204 | +$client->assertRequestMade('https://api.example.com/users'); |
| 205 | +$client->assertRequestMade('https://api.example.com/users', Request::METHOD_POST); |
| 206 | + |
| 207 | +// Reset for next test |
| 208 | +$client->reset(); |
| 209 | +``` |
| 210 | + |
| 211 | +### Request & Response |
| 212 | + |
| 213 | +The `Request` class represents an HTTP request: |
| 214 | + |
| 215 | +```php |
| 216 | +use Contributte\Http\Client\Request; |
| 217 | + |
| 218 | +$request = new Request('https://api.example.com/users', Request::METHOD_POST); |
| 219 | +$request->setHeaders(['Content-Type' => 'application/json']); |
| 220 | +$request->addHeader('Authorization', 'Bearer token'); |
| 221 | +$request->setBody(json_encode(['name' => 'John'])); |
| 222 | + |
| 223 | +// Available methods |
| 224 | +$request->getUrl(); |
| 225 | +$request->getMethod(); |
| 226 | +$request->getHeaders(); |
| 227 | +$request->getHeader('Content-Type'); |
| 228 | +$request->hasHeader('Authorization'); |
| 229 | +$request->getBody(); |
| 230 | +$request->getOptions(); |
| 231 | +``` |
| 232 | + |
| 233 | +The `Response` class represents an HTTP response: |
| 234 | + |
| 235 | +```php |
| 236 | +use Contributte\Http\Client\Response; |
| 237 | + |
| 238 | +// After making a request |
| 239 | +$response = $client->get('https://api.example.com/users'); |
| 240 | + |
| 241 | +// Body |
| 242 | +$body = $response->getBody(); |
| 243 | +$jsonData = $response->getJsonBody(); // Decoded JSON |
| 244 | +$response->hasBody(); |
| 245 | + |
| 246 | +// Status |
| 247 | +$statusCode = $response->getStatusCode(); |
| 248 | +$response->isOk(); // Status is 200 |
| 249 | +$response->isSuccess(); // Status is 2xx |
| 250 | + |
| 251 | +// Headers |
| 252 | +$response->getAllHeaders(); |
| 253 | +$response->getHeader('Content-Type'); |
| 254 | +$response->hasHeader('X-Custom'); |
| 255 | + |
| 256 | +// Content type |
| 257 | +$response->isJson(); // Check if response is JSON |
| 258 | + |
| 259 | +// Errors |
| 260 | +$error = $response->getError(); |
| 261 | +``` |
30 | 262 |
|
31 | 263 | ## SAPI |
32 | 264 |
|
|
0 commit comments