55[ ![ codecov] ( https://codecov.io/gh/farzai/transport-php/branch/main/graph/badge.svg )] ( https://codecov.io/gh/farzai/transport-php )
66[ ![ Total Downloads] ( https://img.shields.io/packagist/dt/farzai/transport.svg?style=flat-square )] ( https://packagist.org/packages/farzai/transport )
77
8- A HTTP client for Farzai Package .
8+ A modern, PSR-compliant HTTP client for PHP with middleware architecture, advanced retry strategies, and fluent API for building requests .
99
10+ ## Features
11+
12+ - ✅ ** PSR-7** HTTP Message Interface
13+ - ✅ ** PSR-18** HTTP Client Interface
14+ - ✅ ** PSR-3** Logger Interface support
15+ - ✅ ** Middleware Architecture** - Extensible plugin system for custom behavior
16+ - ✅ ** Advanced Retry Strategies** - Exponential backoff with jitter, custom retry conditions
17+ - ✅ ** Fluent Request Builder** - Chainable API for building requests
18+ - ✅ ** Immutable Configuration** - Thread-safe, predictable behavior
19+ - ✅ ** Type-Safe** - Full PHP 8.1+ type hints and strict types
20+ - ✅ ** JSON Helpers** - Parse JSON with proper error handling
21+ - ✅ ** Custom Exceptions** - Detailed error context and retry information
22+
23+ ## Requirements
24+
25+ - PHP 8.1 or higher
1026
1127## Installation
1228
@@ -16,12 +32,311 @@ You can install the package via composer:
1632composer require farzai/transport
1733```
1834
35+ ## Usage
36+
37+ ### Basic Usage (Fluent API)
38+
39+ ``` php
40+ use Farzai\Transport\TransportBuilder;
41+
42+ // Create a transport client with configuration
43+ $transport = TransportBuilder::make()
44+ ->withBaseUri('https://api.example.com')
45+ ->withHeaders([
46+ 'Authorization' => 'Bearer token123',
47+ 'Accept' => 'application/json',
48+ ])
49+ ->withTimeout(30)
50+ ->build();
51+
52+ // Make requests using fluent API
53+ $response = $transport->get('/users/123')->send();
54+
55+ // Access response data
56+ echo $response->statusCode(); // 200
57+ echo $response->body(); // Raw response body
58+ $data = $response->json(); // Parsed JSON as array
59+ ```
60+
61+ ### Fluent Request Building
62+
63+ ``` php
64+ // GET request with query parameters
65+ $response = $transport
66+ ->get('/users')
67+ ->withQuery(['page' => 1, 'limit' => 10])
68+ ->withHeader('X-Custom-Header', 'value')
69+ ->send();
70+
71+ // POST with JSON body
72+ $response = $transport
73+ ->post('/users')
74+ ->withJson([
75+ 'name' => 'John Doe',
76+ 'email' => 'john@example.com'
77+ ])
78+ ->send();
79+
80+ // POST with form data
81+ $response = $transport
82+ ->post('/login')
83+ ->withForm([
84+ 'username' => 'john',
85+ 'password' => 'secret'
86+ ])
87+ ->send();
88+
89+ // With authentication
90+ $response = $transport
91+ ->get('/protected')
92+ ->withBearerToken('your-token')
93+ ->send();
94+
95+ // Or basic auth
96+ $response = $transport
97+ ->get('/protected')
98+ ->withBasicAuth('username', 'password')
99+ ->send();
100+ ```
101+
102+ ### Working with JSON Responses
103+
104+ ``` php
105+ // Parse JSON with automatic error handling
106+ $data = $response->json(); // ['id' => 123, 'name' => 'John Doe']
107+
108+ // Get specific field using dot notation
109+ $name = $response->json('name'); // 'John Doe'
110+ $city = $response->json('user.address.city'); // 'New York'
111+
112+ // Get as array
113+ $array = $response->toArray();
114+
115+ // Safe JSON parsing (returns null on error instead of throwing)
116+ $data = $response->jsonOrNull();
117+
118+ // Check if response is successful
119+ if ($response->isSuccessful()) {
120+ // Handle success (2xx status codes)
121+ }
122+ ```
123+
124+ ### Advanced Retry Logic
125+
126+ ``` php
127+ use Farzai\Transport\Retry\ExponentialBackoffStrategy;
128+ use Farzai\Transport\Retry\RetryCondition;
129+ use Farzai\Transport\Exceptions\NetworkException;
130+
131+ // Configure retry with exponential backoff
132+ $transport = TransportBuilder::make()
133+ ->withRetries(
134+ maxRetries: 3,
135+ strategy: new ExponentialBackoffStrategy(
136+ baseDelayMs: 1000, // Start with 1 second
137+ multiplier: 2.0, // Double each retry
138+ maxDelayMs: 30000, // Cap at 30 seconds
139+ useJitter: true // Add randomization
140+ ),
141+ condition: RetryCondition::default()
142+ ->onExceptions([NetworkException::class])
143+ )
144+ ->build();
145+
146+ // Retries automatically with exponential backoff + jitter
147+ $response = $transport->get('/unreliable-endpoint')->send();
148+ ```
149+
150+ ### Custom Middleware
151+
152+ ``` php
153+ use Farzai\Transport\Middleware\MiddlewareInterface;
154+ use Psr\Http\Message\RequestInterface;
155+ use Psr\Http\Message\ResponseInterface;
156+
157+ // Create custom middleware
158+ class AuthMiddleware implements MiddlewareInterface
159+ {
160+ public function handle(RequestInterface $request, callable $next): ResponseInterface
161+ {
162+ // Add auth token to all requests
163+ $request = $request->withHeader('Authorization', 'Bearer ' . $this->getToken());
164+
165+ return $next($request);
166+ }
167+
168+ private function getToken(): string
169+ {
170+ // Your token logic
171+ return 'your-token';
172+ }
173+ }
174+
175+ // Add to transport
176+ $transport = TransportBuilder::make()
177+ ->withMiddleware(new AuthMiddleware())
178+ ->build();
179+ ```
180+
181+ ### Configuration Options
182+
183+ ``` php
184+ $transport = TransportBuilder::make()
185+ ->withBaseUri('https://api.example.com')
186+ ->withHeaders(['Accept' => 'application/json'])
187+ ->withTimeout(30) // Seconds
188+ ->withRetries(3) // Max retry attempts
189+ ->withMiddleware($customMiddleware)
190+ ->withoutDefaultMiddlewares() // Disable logging, timeout, retry middlewares
191+ ->setClient($customPsrClient) // Use custom PSR-18 client
192+ ->setLogger($customLogger) // Use custom PSR-3 logger
193+ ->build();
194+ ```
195+
196+ ### Error Handling
197+
198+ ``` php
199+ use Farzai\Transport\Exceptions\RetryExhaustedException;
200+ use Farzai\Transport\Exceptions\JsonParseException;
201+ use Farzai\Transport\Exceptions\NetworkException;
202+
203+ // Throw exception on non-2xx responses
204+ try {
205+ $response->throw();
206+ } catch (\GuzzleHttp\Exception\BadResponseException $e) {
207+ echo $e->getMessage();
208+ }
209+
210+ // Custom error handling callback
211+ $response->throw(function ($response, $exception) {
212+ if ($response->statusCode() === 404) {
213+ throw new \Exception('Resource not found!');
214+ }
215+ throw $exception;
216+ });
217+
218+ // Handle retry exhaustion
219+ try {
220+ $response = $transport->get('/flaky-endpoint')->send();
221+ } catch (RetryExhaustedException $e) {
222+ echo "Failed after {$e->getAttempts()} attempts\n";
223+ echo "Delays used: " . implode(', ', $e->getDelaysUsed()) . "ms\n";
224+
225+ foreach ($e->getRetryExceptions() as $attempt => $exception) {
226+ echo "Attempt $attempt: {$exception->getMessage()}\n";
227+ }
228+ }
229+
230+ // Handle JSON parse errors
231+ try {
232+ $data = $response->json();
233+ } catch (JsonParseException $e) {
234+ echo "Invalid JSON: {$e->getMessage()}\n";
235+ echo "JSON string: {$e->jsonString}\n";
236+ echo "Error code: {$e->jsonErrorCode}\n";
237+ }
238+ ```
239+
240+ ### Using Custom PSR-18 Client and Logger
241+
242+ ``` php
243+ use Farzai\Transport\TransportBuilder;
244+ use Monolog\Logger;
245+ use Monolog\Handler\StreamHandler;
246+
247+ // Create custom logger
248+ $logger = new Logger('http-client');
249+ $logger->pushHandler(new StreamHandler('path/to/your.log'));
250+
251+ // Use any PSR-18 compliant client
252+ $client = new \Your\Custom\Psr18Client();
253+
254+ $transport = TransportBuilder::make()
255+ ->setClient($client)
256+ ->setLogger($logger)
257+ ->build();
258+ ```
259+
260+ ### Testing with Response Builder
261+
262+ ``` php
263+ use Farzai\Transport\ResponseBuilder;
264+
265+ $response = ResponseBuilder::create()
266+ ->statusCode(200)
267+ ->withHeader('Content-Type', 'application/json')
268+ ->withBody('{"success": true}')
269+ ->build();
270+
271+ // Or use the fluent builder methods
272+ $response = ResponseBuilder::create()
273+ ->statusCode(404)
274+ ->withHeaders([
275+ 'Content-Type' => 'application/json',
276+ 'X-Request-ID' => '12345'
277+ ])
278+ ->withBody('{"error": "Not found"}')
279+ ->withVersion('1.1')
280+ ->withReason('Not Found')
281+ ->build();
282+ ```
283+
284+ ## Architecture
285+
286+ ### Middleware System
287+
288+ The library uses a middleware pipeline architecture that allows you to easily extend functionality:
289+
290+ ```
291+ Request → Middleware Stack → HTTP Client → Response
292+ ↓
293+ [LoggingMiddleware]
294+ [TimeoutMiddleware]
295+ [RetryMiddleware]
296+ [CustomMiddleware...]
297+ ```
298+
299+ Default middlewares (can be disabled with ` withoutDefaultMiddlewares() ` ):
300+ - ** LoggingMiddleware** : Logs requests and responses
301+ - ** TimeoutMiddleware** : Enforces request timeouts
302+ - ** RetryMiddleware** : Handles retry logic with configurable strategies
303+
304+ ### Immutable Configuration
305+
306+ All configuration is immutable and set during the build phase:
307+
308+ ``` php
309+ // ✅ Correct - configuration during build
310+ $transport = TransportBuilder::make()
311+ ->withTimeout(30)
312+ ->withRetries(3)
313+ ->build();
314+
315+ // ❌ No longer available - no mutable setters
316+ // $transport->setTimeout(30); // Method doesn't exist
317+ ```
318+
319+ This makes the Transport instance:
320+ - ** Thread-safe** - Can be safely shared across threads
321+ - ** Predictable** - Configuration can't change unexpectedly
322+ - ** Easier to test** - No hidden state changes
323+
19324## Testing
20325
21326``` bash
22327composer test
23328```
24329
330+ ## Code Quality
331+
332+ ``` bash
333+ # Run tests with coverage
334+ composer test-coverage
335+
336+ # Fix code style
337+ composer format
338+ ```
339+
25340## Changelog
26341
27342Please see [ CHANGELOG] ( CHANGELOG.md ) for more information on what has changed recently.
0 commit comments