Skip to content

Commit c09dd49

Browse files
claudef3l1x
authored andcommitted
Refactor HTTP client: rename Curl to Client, add CurlBuilder and FakeClient
- Create new Client namespace with generic HTTP client abstraction: - IClient interface for HTTP client implementations - Request class for HTTP request representation - Response class for HTTP response handling - FakeClient for testing with response queue and request recording - Add CurlBuilder for fluent HTTP request construction - Update CurlClient to implement IClient interface - Remove backward compatibility (ICurlClient, deprecated Response alias) - Update documentation with new API examples Resolves #5
1 parent 947b1d8 commit c09dd49

File tree

12 files changed

+1584
-56
lines changed

12 files changed

+1584
-56
lines changed

.docs/README.md

Lines changed: 238 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,262 @@
33
## Content
44

55
- [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)
813
- [BasicAuth - simple basic authentication](#basic-authentication)
914
- [Useful classes](#useful-classes)
1015
- [Url](#url)
1116

1217
## Setup
1318

19+
**Requirements:** PHP 8.2+
20+
1421
```bash
1522
composer require contributte/http
1623
```
1724

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
1949

20-
There is a prepared simple cURL client in this package.
50+
The `CurlClient` is the default implementation using cURL.
2151

22-
You have to register it at first.
52+
Register it via the DI extension:
2353

2454
```neon
2555
extensions:
2656
curl: Contributte\Http\DI\CurlExtension
2757
```
2858

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+
```
30262

31263
## SAPI
32264

0 commit comments

Comments
 (0)