fetch-php/README.md

438 lines
15 KiB
Markdown
Raw Permalink Normal View History

[![Fetch PHP](./assets/Banner.jpg)](https://github.com/Thavarshan/fetch-php)
2024-09-13 19:31:23 +01:00
# About Fetch PHP
2024-09-13 19:31: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)
[![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)
[![Total Downloads](https://img.shields.io/packagist/dt/jerome/fetch-php.svg)](https://packagist.org/packages/jerome/fetch-php)
**FetchPHP** is a modern HTTP client library for PHP, built on top of the Guzzle HTTP client, designed to mimic the behavior of JavaScripts `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
---
### **Why Choose FetchPHP Over Guzzle?**
2024-09-13 19:31:23 +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**. Heres why FetchPHP stands out:
- **True Async Task Management with Fibers**: While Guzzle uses Promises for async operations, FetchPHP leverages PHPs 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
- **JavaScript-Like `async`/`await` Syntax**: FetchPHP introduces a familiar `async()` syntax for developers who use JavaScripts async/await functionality. This makes writing asynchronous PHP code more **readable** and **intuitive**.
2024-09-13 19:31:23 +01:00
- **Fluent API**: FetchPHP provides a **fluent, chainable API** similar to Laravels HTTP client, making constructing and managing HTTP requests easier. Its more flexible and readable than Guzzles Promise-based API, which can feel rigid for managing complex tasks.
- **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. Guzzles Promises manage errors in a less flexible way, usually through chained `.then()` and `.catch()` methods.
### **How FetchPHP's Async Task Management Differs from Guzzle**
Heres a breakdown of FetchPHPs underlying async task management powered by [Matrix](https://github.com/Thavarshan/matrix) compared to Guzzles Promise-based approach:
- **Fiber-Based Concurrency**: FetchPHP uses PHP Fibers to run tasks asynchronously. Fibers allow tasks to be paused, resumed, and canceled mid-execution, which isnt possible with Guzzles Promises. This provides FetchPHP a true **multi-tasking** advantage.
- **Task Lifecycle Management**: FetchPHP allows you to start, pause, resume, cancel, and retry tasks using the `Task` class. Guzzle doesnt 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.
- **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 Guzzles Promises.
2024-09-13 19:31:23 +01:00
| Feature | FetchPHP | Guzzle |
|------------------------|-----------------------------------------|----------------------------------------|
| 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-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.
#### **Example: Managing Asynchronous Tasks with FetchPHP**
2024-09-13 19:31:23 +01:00
```php
<?php
use Fetch\Interfaces\Response as ResponseInterface;
$data = null;
2024-09-13 19:31:23 +01:00
// 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
// The async operation is managed with start, pause, resume, and cancel controls
$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
```
---
#### **Lifecycle Control Example with FetchPHP**
2024-09-13 19:31:23 +01:00
```php
<?php
use Matrix\Task;
use Matrix\Enum\TaskStatus;
// Define a long-running task
$task = new Task(function () {
return "Task completed!";
});
2024-09-13 19:31: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();
}
$result = $task->getResult();
2024-09-13 19:31:23 +01:00
```
---
### **Why FetchPHP is Better for Asynchronous PHP**
2024-09-13 19:31:23 +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-13 19:31:23 +01:00
## **Installation**
2024-09-13 19:31:23 +01:00
To install FetchPHP, run the following command:
2024-09-13 19:31:23 +01:00
```bash
composer require jerome/fetch-php
2024-09-13 19:31:23 +01:00
```
FetchPHP requires PHP 8.1 or above due to its use of Fibers for async tasks.
---
2024-09-13 19:31:23 +01:00
## **Core Features**
2024-09-13 19:31: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-13 19:31:23 +01:00
## **Usage Examples**
2024-09-13 19:31:23 +01:00
### **JavaScript-like Fetch API (Synchronous)**
2024-09-13 19:31: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
$data = $response->json();
```
2024-09-13 19:31:23 +01:00
### **JavaScript-like Fetch API (Asynchronous)**
2024-09-13 19:31:23 +01:00
```php
<?php
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
```
---
### **Using the Fluent API**
2024-09-13 19:31:23 +01:00
2024-09-30 20:46:03 +01:00
FetchPHPs 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
#### **Synchronous Example**
2024-09-13 19:31:23 +01:00
```php
<?php
$response = fetch()
->baseUri('https://example.com')
->withHeaders(['Content-Type' => 'application/json'])
->withBody(['key' => 'value'])
->withToken('fake-bearer-auth-token')
->post('/posts');
2024-09-13 19:31:23 +01:00
$data = $response->json();
```
#### **Asynchronous Example**
```php
<?php
use Fetch\Interfaces\Response as ResponseInterface;
$data = null;
// Asynchronously send a POST request using the fluent API
async(fn () => fetch()
->baseUri('https://example.com')
->withHeaders(['Content-Type' => 'application/json'])
->withBody(['key' => 'value'])
->withToken('fake-bearer-auth-token')
->post('/posts'))
->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
```
---
### **Using the ClientHandler Class**
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
#### **Basic Example with ClientHandler**
2024-09-13 19:31:23 +01:00
```php
<?php
use Fetch\Http\ClientHandler;
$response = ClientHandler::handle('GET', 'https://example.com', [
'headers' => ['Accept' => 'application/json']
2024-09-13 19:31:23 +01:00
]);
$data = $response->json();
2024-09-13 19:31:23 +01:00
```
#### **Asynchronous Example with ClientHandler**
2024-09-13 19:31:23 +01:00
```php
<?php
2024-09-13 19:31:23 +01:00
use Fetch\Http\ClientHandler;
2024-09-13 19:31:23 +01:00
$data = null;
// Asynchronously manage a request using the ClientHandler
async(fn () => ClientHandler::handle('POST', 'https://example.com', [
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode(['key' => 'value']),
]))
->then(fn ($response) => $data = $response->json())
->catch(fn ($e) => echo "Error: " . $e->getMessage());
```
2024-09-13 19:31:23 +01:00
---
2024-09-13 19:31:23 +01:00
## **Request Options**
2024-09-13 19:31:23 +01:00
FetchPHP accepts an array of options to configure requests:
2024-09-13 19:31: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-13 19:31:23 +01:00
### **Error Handling**
2024-09-13 19:31: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
#### **Synchronous Error Handling**
2024-09-13 19:31:23 +01:00
```php
<?php
$response = fetch('https://nonexistent-url.com');
2024-09-13 19:31:23 +01:00
if ($response->ok()) {
echo $response->json();
2024-09-13 19:31:23 +01:00
} else {
echo "Error: " . $response->statusText();
2024-09-13 19:31:23 +01:00
}
```
#### **Asynchronous Error Handling**
```php
<?php
$response = async(fn () => fetch('https://nonexistent-url.com'))
->then(fn ($response) => $response->json())
->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();
});
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'
]);
// or
$response = fetch('https://example.com')
->withProxy('tcp://localhost:8080')
->get();
echo $response->statusText();
```
### **Authentication Example**
```php
<?php
$response = fetch('https://example.com/secure-endpoint', [
'auth' => ['username', 'password']
]);
// or
$response = fetch('https://example.com')
->baseUri('https://example.com/')
->withAuth('username', 'password')
->get('/secure-endpoint');
echo $response->statusText();
```
2024-09-13 19:31:23 +01:00
---
## License
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-13 19:31:23 +01:00
## Contributing
2024-09-13 19:31: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
Were 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-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
- Thanks to **Guzzle HTTP** for providing the underlying HTTP client that powers synchronous requests.