Skip to content

Commit 0119e7c

Browse files
committed
Refactor client handling to use custom async task management and add fluent API
1 parent f48eebe commit 0119e7c

File tree

5 files changed

+260
-85
lines changed

5 files changed

+260
-85
lines changed

README.md

Lines changed: 112 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,79 @@
77
[![Check & fix styling](https://github.com/Thavarshan/fetch-php/actions/workflows/php-cs-fixer.yml/badge.svg?label=code%20style&branch=main)](https://github.com/Thavarshan/fetch-php/actions/workflows/php-cs-fixer.yml)
88
[![Total Downloads](https://img.shields.io/packagist/dt/jerome/fetch-php.svg)](https://packagist.org/packages/jerome/fetch-php)
99

10-
**FetchPHP** is a powerful PHP HTTP client library built on top of the Guzzle HTTP client, designed to mimic the behavior of JavaScript’s `fetch` API. It leverages **Matrix** for true asynchronous capabilities using PHP Fibers, allowing developers to use a **JavaScript-like async/await** syntax. FetchPHP also offers a **fluent API** inspired by Laravel's HTTP client for more flexible and readable request building.
10+
**FetchPHP** is a modern HTTP client library for PHP, built on top of the Guzzle HTTP client, designed to mimic the behavior of JavaScript’s `fetch` API. Leveraging **Matrix** for true asynchronous capabilities with PHP Fibers, FetchPHP allows developers to use a **JavaScript-like async/await** syntax. FetchPHP also offers a **fluent API** inspired by Laravel's HTTP client, making request building both flexible and readable.
11+
12+
Whether you're building small APIs or large-scale systems with high concurrency needs, FetchPHP provides a powerful and efficient solution for managing HTTP requests in PHP.
13+
14+
Make sure to check out [Matrix](https://github.com/Thavarshan/matrix) for more information on how FetchPHP is powered by PHP Fibers.
1115

1216
---
1317

1418
### **Why Choose FetchPHP Over Guzzle?**
1519

16-
Guzzle is a robust and widely-used HTTP client for PHP, and it does support asynchronous requests using Promises. However, **FetchPHP** goes a step further by offering **true asynchronous task management** via PHP Fibers, powered by **Matrix**. Here’s what sets FetchPHP apart:
20+
Guzzle is a well-established and widely-used HTTP client for PHP. It supports asynchronous requests using Promises. However, **FetchPHP** takes things further by offering **true asynchronous task management** through PHP Fibers, powered by **Matrix**. Here’s why FetchPHP stands out:
21+
22+
- **True Async Task Management with Fibers**: While Guzzle uses Promises for async operations, FetchPHP leverages PHP’s native Fibers (introduced in PHP 8.1) to provide **true non-blocking concurrency**. This gives developers **fine-grained control** over task execution, lifecycle management (e.g., pausing, resuming, retrying), and error handling.
1723

18-
- **True Async Task Management with Fibers**: While Guzzle uses Promises for async operations, FetchPHP harnesses PHP’s native Fibers (introduced in PHP 8.1) to provide **true non-blocking concurrency**. This allows more **fine-grained control** over task execution, lifecycle management (e.g., pausing, resuming, retrying), and error handling.
24+
- **JavaScript-Like `async`/`await` Syntax**: FetchPHP introduces a familiar `async()` syntax for developers who use JavaScript’s async/await functionality. This makes writing asynchronous PHP code more **readable** and **intuitive**.
1925

20-
- **JavaScript-Like `async`/`await` Syntax**: FetchPHP introduces a syntax that will feel familiar to developers who use JavaScript’s async/await functionality. With `async()`, developers can write asynchronous PHP code in a more readable and intuitive way.
26+
- **Fluent API**: FetchPHP provides a **fluent, chainable API** similar to Laravel’s HTTP client, making constructing and managing HTTP requests easier. It’s more flexible and readable than Guzzle’s Promise-based API, which can feel rigid for managing complex tasks.
2127

22-
- **Fluent API**: FetchPHP provides a **fluent, chainable API** that makes constructing and managing HTTP requests easier and more flexible, similar to Laravel’s HTTP client. This contrasts with Guzzle’s Promise-based API, which can feel more rigid and less intuitive for managing complex tasks.
28+
- **Error Handling and Task Lifecycle Control**: FetchPHP, powered by Matrix, allows for granular error management. Tasks can be paused, resumed, canceled, or retried dynamically, and errors can be handled through customizable handlers. Guzzle’s Promises manage errors in a less flexible way, usually through chained `.then()` and `.catch()` methods.
2329

24-
- **Error Handling and Task Lifecycle Control**: FetchPHP, using Matrix, allows developers to handle errors at a more granular level. Tasks can be paused, resumed, canceled, or retried dynamically, and errors can be managed through customizable handlers. Guzzle’s Promises handle errors in a less flexible way, usually through chained `.then()` and `.catch()` methods.
30+
### **How FetchPHP's Async Task Management Differs from Guzzle**
2531

26-
### **How FetchPHP's Async Task Management is Different from Guzzle**
32+
Here’s a breakdown of FetchPHP’s underlying async task management powered by [Matrix](https://github.com/Thavarshan/matrix) compared to Guzzle’s Promise-based approach:
2733

28-
Here’s a breakdown of FetchPHP’s **AsyncHelper** and **Task** classes, which manage true asynchronous behavior, compared to Guzzle’s Promise-based approach:
34+
- **Fiber-Based Concurrency**: FetchPHP uses PHP Fibers to run tasks asynchronously. Fibers allow tasks to be paused, resumed, and canceled mid-execution, which isn’t possible with Guzzle’s Promises. This provides FetchPHP a true **multi-tasking** advantage.
2935

30-
- **Fiber-Based Concurrency**: FetchPHP uses PHP Fibers to run tasks asynchronously. Fibers allow tasks to be paused, resumed, and canceled mid-execution, which isn’t possible with Guzzle’s Promises. This gives FetchPHP a true **multi-tasking** advantage.
36+
- **Task Lifecycle Management**: FetchPHP allows you to start, pause, resume, cancel, and retry tasks using the `Task` class. Guzzle doesn’t offer built-in lifecycle management at this level. FetchPHP lets you track the status of a task (e.g., `PENDING`, `RUNNING`, `PAUSED`, `COMPLETED`, `FAILED`, `CANCELED`), giving more control over long-running or asynchronous processes.
3137

32-
- **Task Lifecycle Management**: FetchPHP allows you to start, pause, resume, cancel, and retry tasks directly using the `Task` class. Guzzle does not offer built-in lifecycle management for Promises at this level. In FetchPHP, you can track the status of a task (e.g., `PENDING`, `RUNNING`, `PAUSED`, `COMPLETED`, `FAILED`, `CANCELED`), providing more control over long-running or asynchronous processes.
38+
- **Custom Error Handling**: FetchPHP offers a customizable `ErrorHandler` to manage retries, logging, and error resolution. This allows dynamic error handling and retrying tasks when needed, going beyond Guzzle’s Promises.
3339

34-
- **Custom Error Handling**: FetchPHP provides a customizable `ErrorHandler` that developers can use to manage retries, logging, and error resolution. This allows you to handle failures dynamically and retry tasks when needed, offering a level of **error recovery** beyond Guzzle’s Promises.
40+
| Feature | FetchPHP | Guzzle |
41+
|------------------------|-----------------------------------------|----------------------------------------|
42+
| Async Task Management | True async with PHP Fibers (PHP 8.1+) | Promises-based concurrency |
43+
| JavaScript-like API | `async/await` syntax | Traditional PHP-based Promises |
44+
| Task Lifecycle Control | Start, pause, resume, cancel, retry | No built-in lifecycle management |
45+
| Error Handling | Customizable error handlers | Standard Promise error handling |
46+
| Concurrent Requests | Supports Fibers for parallel tasks | Limited to Promises and threading |
47+
48+
---
3549

3650
#### **Example: Managing Asynchronous Tasks with FetchPHP**
3751

3852
```php
3953
<?php
4054

41-
use Fetch\Http\ClientHandler;
42-
use Matrix\AsyncHelper;
55+
use Fetch\Interfaces\Response as ResponseInterface;
56+
57+
$data = null;
4358

44-
$response = async(fn () => fetch('https://example.com', [
59+
// Asynchronously send a POST request using async/await syntax
60+
async(fn () => fetch('https://example.com', [
4561
'method' => 'POST',
4662
'headers' => ['Content-Type' => 'application/json'],
4763
'body' => json_encode(['key' => 'value']),
48-
]))->then(fn ($response) => $response->json())
49-
->catch(fn ($e) => $e->getMessage());
64+
]))
65+
->then(fn (ResponseInterface $response) => $data = $response->json()) // Success handler
66+
->catch(fn (\Throwable $e) => echo "Error: " . $e->getMessage()); // Error handler
5067

51-
// FetchPHP manages task lifecycle (start, pause, resume, cancel)
68+
// The async operation is managed with start, pause, resume, and cancel controls
5269
```
5370

71+
---
72+
5473
#### **Lifecycle Control Example with FetchPHP**
5574

5675
```php
5776
<?php
5877

5978
use Matrix\Task;
79+
use Matrix\Enum\TaskStatus;
6080

6181
// Define a long-running task
6282
$task = new Task(function () {
63-
// Task operation here
6483
return "Task completed!";
6584
});
6685

@@ -78,13 +97,15 @@ $task->cancel();
7897
if ($task->getStatus() === TaskStatus::FAILED) {
7998
$task->retry();
8099
}
100+
101+
$result = $task->getResult();
81102
```
82103

83104
---
84105

85106
### **Why FetchPHP is Better for Asynchronous PHP**
86107

87-
While Guzzle is a fantastic tool for making HTTP requests, FetchPHP brings **modern PHP capabilities** with **PHP 8 Fibers**, making it more powerful for developers who need **true asynchronous task management** with a **JavaScript-like syntax**. FetchPHP is designed to make your code more flexible, readable, and efficient when managing complex HTTP operations, especially when concurrency and non-blocking I/O are crucial.
108+
While Guzzle is a fantastic tool for making HTTP requests, FetchPHP brings **modern PHP capabilities** with **PHP 8 Fibers**, making it ideal for developers who need **true asynchronous task management** with a **JavaScript-like syntax**. FetchPHP is designed to make your code more flexible, readable, and efficient when managing complex HTTP operations, especially when concurrency and non-blocking I/O are crucial.
88109

89110
---
90111

@@ -132,15 +153,18 @@ $data = $response->json();
132153
```php
133154
<?php
134155

135-
$response = async(fn () => fetch('https://example.com', [
136-
'method' => 'POST',
137-
'headers' => [
138-
'Content-Type' => 'application/json',
139-
],
140-
'body' => json_encode(['key' => 'value']),
141-
]))
142-
->then(fn (ResponseInterface $response) => $response->json())
143-
->catch(fn (Throwable $e) => $e->getMessage());
156+
use Fetch\Interfaces\Response as ResponseInterface;
157+
158+
$data = null;
159+
160+
// Asynchronously send a POST request using async/await syntax
161+
async(fn () => fetch('https://example.com', [
162+
'method' => 'POST',
163+
'headers' => ['Content-Type' => 'application/json'],
164+
'body' => json_encode(['key' => 'value']),
165+
]))
166+
->then(fn (ResponseInterface $response) => $data = $response->json()) // Success handler
167+
->catch(fn (\Throwable $e) => echo "Error: " . $e->getMessage()); // Error handler
144168
```
145169

146170
---
@@ -169,14 +193,19 @@ $data = $response->json();
169193
```php
170194
<?php
171195

172-
$response = async(fn () => fetch()
196+
use Fetch\Interfaces\Response as ResponseInterface;
197+
198+
$data = null;
199+
200+
// Asynchronously send a POST request using the fluent API
201+
async(fn () => fetch()
173202
->baseUri('https://example.com')
174203
->withHeaders('Content-Type', 'application/json')
175204
->withBody(json_encode(['key' => 'value']))
176205
->withToken('fake-bearer-auth-token')
177206
->post('/posts'))
178-
->then(fn (ResponseInterface $response) => $response->json())
179-
->catch(fn (Throwable $e) => $e->getMessage());
207+
->then(fn (ResponseInterface $response) => $data = $response->json()) // Success handler
208+
->catch(fn (\Throwable $e) => echo "Error: " . $e->getMessage()); // Error handler
180209
```
181210

182211
---
@@ -205,13 +234,16 @@ $data = $response->json();
205234
<?php
206235

207236
use Fetch\Http\ClientHandler;
208-
use Matrix\AsyncHelper;
209237

210-
$response = async(fn () => ClientHandler::handle('POST', 'https://example.com', [
238+
$data = null;
239+
240+
// Asynchronously manage a request using the ClientHandler
241+
async(fn () => ClientHandler::handle('POST', 'https://example.com', [
211242
'headers' => ['Content-Type' => 'application/json'],
212243
'body' => json_encode(['key' => 'value']),
213-
]))->then(fn ($response) => $response->json())
214-
->catch(fn ($e) => $e->getMessage());
244+
]))
245+
->then(fn ($response) => $data = $response->json())
246+
->catch(fn ($e) => echo "Error: " . $e->getMessage());
215247
```
216248

217249
---
@@ -256,7 +288,30 @@ if ($response->ok()) {
256288

257289
$response = async(fn () => fetch('https://nonexistent-url.com'))
258290
->then(fn ($response) => $response->json())
259-
->catch(fn ($e) => $e->getMessage());
291+
->catch(fn ($e) => echo "Error: " . $e->getMessage());
292+
293+
echo $response;
294+
```
295+
296+
---
297+
298+
### **Advanced Error Handling: Retry with Exponential Backoff**
299+
300+
```php
301+
<?php
302+
303+
$response = async(fn () => fetch('https://api.example.com/resource'))
304+
->then(fn ($response) => $response->json())
305+
->catch(function (\Throwable $e) {
306+
// Implement retry logic with exponential backoff
307+
static $attempt = 1;
308+
if ($attempt <= 3) {
309+
sleep(pow(2, $attempt)); // Exponential backoff
310+
$attempt++;
311+
return retryRequest(); // Custom function to retry
312+
}
313+
return "Error: " . $e->getMessage();
314+
});
260315

261316
echo $response;
262317
```
@@ -276,6 +331,12 @@ $response = fetch('https://example.com', [
276331
'proxy' => 'tcp://localhost:8080'
277332
]);
278333

334+
// or
335+
336+
$response = fetch('https://example.com')
337+
->withProxy('tcp://localhost:8080')
338+
->get();
339+
279340
echo $response->statusText();
280341
```
281342

@@ -288,6 +349,13 @@ $response = fetch('https://example.com/secure-endpoint', [
288349
'auth' => ['username', 'password']
289350
]);
290351

352+
// or
353+
354+
$response = fetch('https://example.com')
355+
->baseUri('https://example.com/')
356+
->withAuth('username', 'password')
357+
->get('/secure-endpoint');
358+
291359
echo $response->statusText();
292360
```
293361

@@ -303,6 +371,14 @@ This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md
303371

304372
Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
305373

374+
We’re currently looking for help in the following areas:
375+
376+
- Expanding test coverage for async task management
377+
- Improving documentation for more advanced use cases
378+
- Adding support for additional HTTP methods and protocols
379+
380+
To contribute:
381+
306382
1. Fork the Project
307383
2. Create your Feature Branch (`git checkout -b feature/amazing-feature`)
308384
3. Commit your Changes (`git commit -m 'Add some amazing-feature'`)

0 commit comments

Comments
 (0)