2024-09-29 16:34:23 +01:00
[![Fetch PHP ](./assets/Banner.jpg )](https://github.com/Thavarshan/fetch-php)
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
# About Fetch PHP
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
[![Latest Version on Packagist ](https://img.shields.io/packagist/v/jerome/fetch-php.svg )](https://packagist.org/packages/jerome/fetch-php)
[![Tests ](https://github.com/Thavarshan/fetch-php/actions/workflows/run-tests.yml/badge.svg?label=tests&branch=main )](https://github.com/Thavarshan/fetch-php/actions/workflows/run-tests.yml)
2024-10-19 03:40:30 +01:00
[![Check & fix styling ](https://github.com/Thavarshan/fetch-php/actions/workflows/laravel-pint.yml/badge.svg )](https://github.com/Thavarshan/fetch-php/actions/workflows/laravel-pint.yml)
2024-09-29 16:34:23 +01:00
[![Total Downloads ](https://img.shields.io/packagist/dt/jerome/fetch-php.svg )](https://packagist.org/packages/jerome/fetch-php)
2024-09-29 21:57:57 +01:00
**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.
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.
Make sure to check out [Matrix ](https://github.com/Thavarshan/matrix ) for more information on how FetchPHP is powered by PHP Fibers.
2024-09-13 19:31:23 +01:00
2024-09-30 22:18:04 +01:00
Full documentation can be found [here ](https://fetch-php.thavarshan.com/ )
2024-09-30 20:46:03 +01:00
2024-09-13 19:31:23 +01:00
---
2024-09-29 16:34:23 +01:00
### **Why Choose FetchPHP Over Guzzle?**
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
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:
- **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.
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
- **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** .
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
- **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.
2024-09-23 10:48:19 +01:00
2024-09-29 21:57:57 +01:00
- **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.
2024-09-23 10:48:19 +01:00
2024-09-29 21:57:57 +01:00
### **How FetchPHP's Async Task Management Differs from Guzzle**
2024-09-23 10:48:19 +01:00
2024-09-29 21:57:57 +01:00
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:
2024-09-23 10:48:19 +01:00
2024-09-29 21:57:57 +01:00
- **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.
2024-09-23 10:48:19 +01:00
2024-09-29 21:57:57 +01:00
- **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.
2024-09-23 10:48:19 +01:00
2024-09-29 21:57:57 +01:00
- **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.
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
| Feature | FetchPHP | Guzzle |
|------------------------|-----------------------------------------|----------------------------------------|
2024-10-19 03:44:45 +01:00
| Async Task Management | True async with PHP Fibers (PHP 8.1+) | Promises-based concurrency |
| JavaScript-like API | `async/await` syntax | Traditional PHP-based Promises |
| Task Lifecycle Control | Start, pause, resume, cancel, retry | No built-in lifecycle management |
| Error Handling | Customizable error handlers | Standard Promise error handling |
| Concurrent Requests | Supports Fibers for parallel tasks | Limited to Promises and threading |
2024-09-29 21:57:57 +01:00
---
2024-09-13 19:31:23 +01:00
2024-09-30 20:46:03 +01:00
> **Note**: The `fetch()` function allows for flexible HTTP request handling. When a URL is provided, it immediately sends the request. When no URL is provided, it returns a `ClientHandler` instance to enable further chaining for advanced request configuration.
2024-09-29 16:34:23 +01:00
#### **Example: Managing Asynchronous Tasks with FetchPHP**
2024-09-13 19:31:23 +01:00
```php
< ?php
2024-09-29 21:57:57 +01:00
use Fetch\Interfaces\Response as ResponseInterface;
$data = null;
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
// Asynchronously send a POST request using async/await syntax
async(fn () => fetch('https://example.com', [
2024-09-29 16:34:23 +01:00
'method' => 'POST',
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode(['key' => 'value']),
2024-09-29 21:57:57 +01:00
]))
->then(fn (ResponseInterface $response) => $data = $response->json()) // Success handler
->catch(fn (\Throwable $e) => echo "Error: " . $e->getMessage()); // Error handler
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
// The async operation is managed with start, pause, resume, and cancel controls
2024-09-30 23:02:30 +01:00
$task = async(fn () => fetch('https://example.com', [
'method' => 'POST',
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode(['key' => 'value']),
]));
// Manually control the task lifecycle as `then` and `catch` will automatically start the task
$task->start();
// Pause the task if needed
$task->pause();
// Resume the task
$task->resume();
// Cancel the task if required
$task->cancel();
// Retry the task if it fails
if ($task->getStatus() === TaskStatus::FAILED) {
$task->retry();
}
// Get the result only if the task is completed successfully
$result = $task->getResult();
2024-09-13 19:31:23 +01:00
```
2024-09-29 21:57:57 +01:00
---
2024-09-29 16:34:23 +01:00
#### **Lifecycle Control Example with FetchPHP**
2024-09-13 19:31:23 +01:00
```php
< ?php
2024-09-29 16:34:23 +01:00
use Matrix\Task;
2024-09-29 21:57:57 +01:00
use Matrix\Enum\TaskStatus;
2024-09-29 16:34:23 +01:00
// Define a long-running task
$task = new Task(function () {
return "Task completed!";
});
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
// Start the task
$task->start();
// Pause and resume the task dynamically
$task->pause();
$task->resume();
// Cancel the task if needed
$task->cancel();
// Retry the task if it fails
if ($task->getStatus() === TaskStatus::FAILED) {
$task->retry();
}
2024-09-29 21:57:57 +01:00
$result = $task->getResult();
2024-09-13 19:31:23 +01:00
```
---
2024-09-29 16:34:23 +01:00
### **Why FetchPHP is Better for Asynchronous PHP**
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
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.
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
---
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
## **Installation**
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
To install FetchPHP, run the following command:
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
```bash
composer require jerome/fetch-php
2024-09-13 19:31:23 +01:00
```
2024-09-29 16:34:23 +01:00
FetchPHP requires PHP 8.1 or above due to its use of Fibers for async tasks.
2024-09-29 14:59:51 +01:00
2024-09-29 16:34:23 +01:00
---
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
## **Core Features**
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
- **JavaScript-like fetch API** for HTTP requests (synchronous and asynchronous).
- **Fluent API** for building requests with a readable and chainable syntax.
- **Asynchronous support** with PHP Fibers via the Matrix package.
- **Built on Guzzle** for robust and reliable HTTP client functionality.
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
---
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
## **Usage Examples**
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
### **JavaScript-like Fetch API (Synchronous)**
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
```php
< ?php
$response = fetch('https://example.com', [
'method' => 'POST',
'headers' => [
'Content-Type' => 'application/json',
],
'body' => json_encode(['key' => 'value']),
]);
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
$data = $response->json();
```
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
### **JavaScript-like Fetch API (Asynchronous)**
2024-09-13 19:31:23 +01:00
```php
< ?php
2024-09-29 21:57:57 +01:00
use Fetch\Interfaces\Response as ResponseInterface;
$data = null;
// Asynchronously send a POST request using async/await syntax
async(fn () => fetch('https://example.com', [
'method' => 'POST',
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode(['key' => 'value']),
]))
->then(fn (ResponseInterface $response) => $data = $response->json()) // Success handler
->catch(fn (\Throwable $e) => echo "Error: " . $e->getMessage()); // Error handler
2024-09-13 19:31:23 +01:00
```
2024-09-29 14:59:51 +01:00
---
2024-09-27 23:56:28 +01:00
2024-09-29 16:34:23 +01:00
### **Using the Fluent API**
2024-09-13 19:31:23 +01:00
2024-09-30 20:46:03 +01:00
FetchPHP’ s fluent API provides the following methods for building requests:
- `withToken()` : Attach a Bearer token to the request.
- `withAuth()` : Attach basic authentication credentials.
- `withHeaders()` : Add headers to the request.
- `withBody()` : Add a request body (e.g., JSON, form data).
- `withProxy()` : Specify a proxy server for the request.
- `withCookies()` : Attach cookies to the request.
- `withQueryParameters()` : Add query parameters to the request URL.
- `timeout()` : Set the timeout for the request.
- `retry()` : Set the number of retries and delay for failed requests.
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
#### **Synchronous Example**
2024-09-13 19:31:23 +01:00
```php
< ?php
2024-09-29 14:59:51 +01:00
$response = fetch()
2024-09-29 16:34:23 +01:00
->baseUri('https://example.com')
2024-09-30 18:27:50 +01:00
->withHeaders(['Content-Type' => 'application/json'])
2024-10-03 12:57:15 +01:00
->withBody(['key' => 'value'])
2024-09-29 16:34:23 +01:00
->withToken('fake-bearer-auth-token')
->post('/posts');
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
$data = $response->json();
```
#### **Asynchronous Example**
```php
< ?php
2024-09-29 21:57:57 +01:00
use Fetch\Interfaces\Response as ResponseInterface;
$data = null;
// Asynchronously send a POST request using the fluent API
async(fn () => fetch()
2024-09-29 16:34:23 +01:00
->baseUri('https://example.com')
2024-09-30 18:27:50 +01:00
->withHeaders(['Content-Type' => 'application/json'])
2024-10-03 12:57:15 +01:00
->withBody(['key' => 'value'])
2024-09-29 16:34:23 +01:00
->withToken('fake-bearer-auth-token')
->post('/posts'))
2024-09-29 21:57:57 +01:00
->then(fn (ResponseInterface $response) => $data = $response->json()) // Success handler
->catch(fn (\Throwable $e) => echo "Error: " . $e->getMessage()); // Error handler
2024-09-13 19:31:23 +01:00
```
---
2024-09-29 16:34:23 +01:00
### **Using the ClientHandler Class**
2024-09-29 14:59:51 +01:00
2024-09-29 16:34:23 +01:00
The `ClientHandler` class is responsible for managing HTTP requests, including synchronous and asynchronous handling. You can use it directly for more advanced use cases:
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
#### **Basic Example with ClientHandler**
2024-09-13 19:31:23 +01:00
```php
< ?php
2024-09-29 16:34:23 +01:00
use Fetch\Http\ClientHandler;
2024-09-29 14:59:51 +01:00
2024-09-29 16:34:23 +01:00
$response = ClientHandler::handle('GET', 'https://example.com', [
'headers' => ['Accept' => 'application/json']
2024-09-13 19:31:23 +01:00
]);
2024-09-29 16:34:23 +01:00
$data = $response->json();
2024-09-13 19:31:23 +01:00
```
2024-09-29 16:34:23 +01:00
#### **Asynchronous Example with ClientHandler**
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
```php
< ?php
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
use Fetch\Http\ClientHandler;
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
$data = null;
// Asynchronously manage a request using the ClientHandler
async(fn () => ClientHandler::handle('POST', 'https://example.com', [
2024-09-29 16:34:23 +01:00
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode(['key' => 'value']),
2024-09-29 21:57:57 +01:00
]))
->then(fn ($response) => $data = $response->json())
->catch(fn ($e) => echo "Error: " . $e->getMessage());
2024-09-29 16:34:23 +01:00
```
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
---
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
## **Request Options**
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
FetchPHP accepts an array of options to configure requests:
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
- **`method` **: HTTP method (e.g., `'GET'` , `'POST'` , etc.). Default is `'GET'` .
- **`headers` **: Array of HTTP headers.
- **`body` **: Request body for methods like POST, PUT, PATCH.
- **`json` **: JSON data to send as the request body.
- **`timeout` **: Timeout for the request in seconds.
- **`auth` **: Array for HTTP Basic or Digest authentication.
- **`proxy` **: Proxy server URL for routing requests.
- **`client` **: A custom Guzzle client instance.
2024-09-13 19:31:23 +01:00
2024-09-29 14:59:51 +01:00
---
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
### **Error Handling**
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
Both synchronous and asynchronous requests handle errors gracefully. Here's how you can manage errors:
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
#### **Synchronous Error Handling**
2024-09-13 19:31:23 +01:00
```php
< ?php
2024-09-29 16:34:23 +01:00
$response = fetch('https://nonexistent-url.com');
2024-09-13 19:31:23 +01:00
if ($response->ok()) {
2024-09-29 16:34:23 +01:00
echo $response->json();
2024-09-13 19:31:23 +01:00
} else {
2024-09-29 16:34:23 +01:00
echo "Error: " . $response->statusText();
2024-09-13 19:31:23 +01:00
}
```
2024-09-29 16:34:23 +01:00
#### **Asynchronous Error Handling**
```php
< ?php
$response = async(fn () => fetch('https://nonexistent-url.com'))
->then(fn ($response) => $response->json())
2024-09-29 21:57:57 +01:00
->catch(fn ($e) => echo "Error: " . $e->getMessage());
echo $response;
```
---
### **Advanced Error Handling: Retry with Exponential Backoff**
```php
< ?php
$response = async(fn () => fetch('https://api.example.com/resource'))
->then(fn ($response) => $response->json())
->catch(function (\Throwable $e) {
// Implement retry logic with exponential backoff
static $attempt = 1;
if ($attempt < = 3) {
sleep(pow(2, $attempt)); // Exponential backoff
$attempt++;
return retryRequest(); // Custom function to retry
}
return "Error: " . $e->getMessage();
});
2024-09-29 16:34:23 +01:00
echo $response;
```
---
## **Proxy and Authentication Support**
FetchPHP supports proxies and authentication out of the box:
### **Proxy Example**
```php
< ?php
$response = fetch('https://example.com', [
'proxy' => 'tcp://localhost:8080'
]);
2024-09-29 21:57:57 +01:00
// or
$response = fetch('https://example.com')
->withProxy('tcp://localhost:8080')
->get();
2024-09-29 16:34:23 +01:00
echo $response->statusText();
```
### **Authentication Example**
```php
< ?php
$response = fetch('https://example.com/secure-endpoint', [
'auth' => ['username', 'password']
]);
2024-09-29 21:57:57 +01:00
// or
$response = fetch('https://example.com')
->baseUri('https://example.com/')
->withAuth('username', 'password')
->get('/secure-endpoint');
2024-09-29 16:34:23 +01:00
echo $response->statusText();
```
2024-09-13 19:31:23 +01:00
---
## License
2024-09-29 16:34:23 +01:00
This project is licensed under the MIT License - see the [LICENSE.md ](LICENSE.md ) file for details.
2024-09-13 19:31:23 +01:00
2024-09-29 14:59:51 +01:00
---
2024-09-13 19:31:23 +01:00
2024-09-29 14:59:51 +01:00
## Contributing
2024-09-13 19:31:23 +01:00
2024-09-29 16:34:23 +01:00
Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated** .
2024-09-13 19:31:23 +01:00
2024-09-29 21:57:57 +01:00
We’ re currently looking for help in the following areas:
- Expanding test coverage for async task management
- Improving documentation for more advanced use cases
- Adding support for additional HTTP methods and protocols
To contribute:
2024-09-13 19:31:23 +01:00
1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/amazing-feature` )
3. Commit your Changes (`git commit -m 'Add some amazing-feature'` )
4. Push to the Branch (`git push origin feature/amazing-feature` )
5. Open a Pull Request
2024-09-29 16:34:23 +01:00
---
2024-09-13 19:31:23 +01:00
## Authors
- **[Jerome Thayananthajothy]** - *Initial work* - [Thavarshan ](https://github.com/Thavarshan )
See also the list of [contributors ](https://github.com/Thavarshan/fetch-php/contributors ) who participated in this project.
## Acknowledgments
2024-09-29 16:34:23 +01:00
- Thanks to **Guzzle HTTP** for providing the underlying HTTP client that powers synchronous requests.