PHPackages                             mensbeam/http-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. mensbeam/http-client

ActiveLibrary[HTTP &amp; Networking](/categories/http)

mensbeam/http-client
====================

2.1.1(1y ago)024MITPHPPHP &gt;=8.0

Since Feb 7Pushed 1y ago2 watchersCompare

[ Source](https://github.com/mensbeam/HTTP-Client)[ Packagist](https://packagist.org/packages/mensbeam/http-client)[ RSS](/packages/mensbeam-http-client/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (5)Versions (7)Used By (0)

HTTP-Client
===========

[](#http-client)

Overview
--------

[](#overview)

*HTTP-Client* is a [PSR-18](https://www.php-fig.org/psr/psr-18/)-compatible wrapper around Guzzle's `\GuzzleHttp\Client`, providing enhanced default settings with customizable retry logic. It ensures that cURL is used for all requests and offers built-in error-handling mechanisms to improve debugging and resilience.

Guzzle's built-in retry mechanism requires a lot of boilerplate code and is difficult to use in real-world scenarios. It also has a major limitation: you can't modify a request before retrying. This becomes a problem when a token expires, and you need to update an Authentication header or a form parameter with a new token. One of Guzzle’s key benefits is its ability to automatically create request bodies based on request options, like JSON encoding or assembling form parameters from an array. However, its built-in retry middleware does not support this feature. In version 1.5, this library introduced a custom retry middleware to overcome these issues. But in practice, it was still impractical — especially when modifying headers. The only way to do so was by creating a new [PSR-7](https://www.php-fig.org/psr/psr-7/) request object, which added complexity. Starting with version 2.0, *HTTP-Client* simplifies this process. The retry callback now receives the method, URI, and request options directly. This makes modifying the request before retrying much easier.

Features
--------

[](#features)

- **Enforced cURL Usage**: cURL is the industry-standard tool for HTTP requests. Consistently using cURL simplifies debugging by providing well-documented error messages and avoids unnecessary complexity caused by switching between different request methods in pursuit of negligible performance optimizations
- **Exponential Backoff Retry Mechanism**: Retries failed requests with increasing delays based on HTTP code
- **Configurable Retry Behavior**: Custom retry logic via a callback. This callback allows fine-grained control over retry logic by evaluating the request, response, and exception details
- **Retry-After Header Support**: Automatically respects server-provided retry delays
- **Extended Exception Messages**: Guzzle imposes an absurdly restrictive character limit on exception messages, [which is actively harmful when debugging](https://github.com/guzzle/guzzle/issues/1722). To address this, this class automatically overrides the behavior by increasing the limit to 32767 characters
- **Support for Mock Responses**: Allows for testing without making actual HTTP requests

Installation
------------

[](#installation)

```
composer require mensbeam/http-client
```

Class Synopsis
--------------

[](#class-synopsis)

**Note**: This class does not have the async methods (`Client::requestAsync` and `Client::sendAsync`) from Guzzle. While Guzzle internally relies on promises, they introduce unnecessary complexity without providing any real multitasking capabilities. Since PHP only introduced cooperative multitasking with Fibers in PHP 8.1 — and Guzzle does not utilize them — its promise-based implementation offers no actual performance benefits. Instead, it adds misleading abstraction without true async behavior.

```
namespace MensBeam\HTTP;

class Client {
    public const REQUEST_STOP = 0;
    public const REQUEST_FAIL = 1;
    public const REQUEST_RETRY = 2;
    public const REQUEST_CONTINUE = 3;

    public function __construct(array $config = []);

    public function request(string $method, string|Psr\Http\Message\UriInterface $uri = '', array $options = []): Psr\Http\Message\ResponseInterface;
    public function send(Psr\Http\Message\RequestInterface $request, array $options = []): Psr\Http\Message\ResponseInterface;
}
```

### Client::\_\_construct

[](#client__construct)

#### Description

[](#description)

```
public function Client::__construct(array $config = [])
```

Returns new `Client` object.

#### Parameters

[](#parameters)

**config** - An array of configuration options

### Client::request

[](#clientrequest)

#### Description

[](#description-1)

```
public function Client::request(string $method, string|Psr\Http\Message\UriInterface $uri = '', array $options = []): Psr\Http\Message\ResponseInterface
```

Creates and sends an HTTP request with built-in retry logic defaults.

#### Parameters

[](#parameters-1)

**method** - HTTP method **uri** - URI object or string **options** - Request options to apply

### Client::send

[](#clientsend)

#### Description

[](#description-2)

```
public function Client::send(Psr\Http\Message\RequestInterface $request, array $options = []): Psr\Http\Message\ResponseInterface;
```

Sends a supplied HTTP request with built-in retry logic defaults.

#### Parameters

[](#parameters-2)

**request** - PSR-7 Request object **options** - Request options to apply

Usage
-----

[](#usage)

### Basic Example

[](#basic-example)

```
use MensBeam\HTTP\Client;

$client = new Client();
$response = $client->request('GET', 'https://api.example.com/data');
echo (string)$response->getBody();
```

### Custom Retry Logic

[](#custom-retry-logic)

```
use MensBeam\HTTP\Client;

$callback = function (
    int $retryAttempt,
    string $method,
    string|UriInterface $uri,
    array $options,
    int $delay,
    ?ResponseInterface $response = null,
    ConnectException|RequestException|null $exception = null
): int {
    if ($response?->getStatusCode() === 400) {
        return Client::REQUEST_RETRY;
    }
    return Client::REQUEST_CONTINUE;
};

$client = new Client('GET', 'https://ook.com', [ 'retry_callback' => $callback ]);
```

### Custom Retry Logic with Modified Request

[](#custom-retry-logic-with-modified-request)

```
use MensBeam\HTTP\Client;

$callback = function (
    int $retryAttempt,
    string $method,
    string|UriInterface &$uri,
    array $options,
    int $delay,
    ?ResponseInterface $response = null,
    ConnectException|RequestException|null $exception = null
): int {
    if ($response?->getStatusCode() === 400) {
        $uri = 'https://eek.com';
        return Client::REQUEST_RETRY;
    }
    return Client::REQUEST_CONTINUE;
};

$client = new Client([ 'retry_callback' => $callback ]);
```

### Using a Logger

[](#using-a-logger)

```
use MensBeam\{
    HTTP\Client,
    Logger,
    Logger\StreamHandler
};

$logger = new Logger('http_logger', new StreamHandler('php://stdout', range(0, 7)));
$client = new Client(['logger' => $logger]);
$response = $client->request('GET', 'https://api.example.com/data');
```

### Mocking Responses for Testing

[](#mocking-responses-for-testing)

```
use MensBeam\HTTP\Client,
    GuzzleHttp\Psr7\Response;

$client = new Client([
    'dry_run' => [
        new Response(418, [], 'Short and stout?'),
        new Response(200, [], 'Mock response data')
    ],
    'retry_callback' => function (
        int $retryAttempt,
        string $method,
        string|UriInterface $uri,
        array $options,
        int $delay,
        ?ResponseInterface $response = null,
        ConnectException|RequestException|null $exception = null
    ): int {
        $code = $response?->getStatusCode();

        if ($code === 418) {
            return Client::REQUEST_RETRY;
        }
        return Client::REQUEST_CONTINUE;
    }
]);
$response = $client->request('GET', 'https://api.example.com/data');
echo (string)$response->getBody();
```

### Adding a middleware to the stack

[](#adding-a-middleware-to-the-stack)

```
use MensBeam\HTTP\Client;
use GuzzleHttp\Middleware;

$tapMiddleware = Middleware::tap(function (RequestInterface $request) {
    echo 'Sending request to: ' . $request->getUri() . PHP_EOL;
});

$client = new Client([ 'middleware' => $tapMiddleware ]);
$response = $client->request('GET', 'https://api.example.com/data');
echo (string)$response->getBody();
```

Configuration Options
---------------------

[](#configuration-options)

OptionTypeDefaultDescription`dry_run``bool|\Psr\Http\Message\ResponseInterface|array``false`Enables mock responses`logger``Psr\Log\LoggerInterface|null``null`Logs debugging information`max_retries``int``10`Maximum number of retries`middleware``array|Closure``null`Middleware stack configuration`retry_callback``?callable``null`Custom retry logic callback; fires after every response**NOTE**: Earlier versions of *HTTP-Client* had the `on_retry` option. It was replaced with `retry_callback` in version 1.5. This is because the original name didn't accurately reflect when it was called. It fires after every response regardless of outcome, and its return value determines if it is retried.

Configuration may be applied both in the constructor and in `Client::request` and `Client::send`. All configuration on the request overrides any configuration on the class but only for that request. *HTTP-Client* will also accept the remaining Guzzle [Client configuration](https://docs.guzzlephp.org/en/stable/quickstart.html#creating-a-client) and [request options](https://docs.guzzlephp.org/en/stable/request-options.html).

License
-------

[](#license)

This project is licensed under the MIT License. See the `LICENSE` file for details.

###  Health Score

28

—

LowBetter than 52% of packages

Maintenance43

Moderate activity, may be stable

Popularity6

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~10 days

Recently: every ~3 days

Total

6

Last Release

459d ago

Major Versions

1.5.0 → 2.0.02025-03-21

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/89590?v=4)[J. King](/maintainers/JKingweb)[@JKingweb](https://github.com/JKingweb)

![](https://www.gravatar.com/avatar/fa45644fa8847647e3d12802571072bbd4f3da8d27ed03f6f8df3c3859db6f6e?d=identicon)[dustinwilson](/maintainers/dustinwilson)

---

Top Contributors

[![dustinwilson](https://avatars.githubusercontent.com/u/1885928?v=4)](https://github.com/dustinwilson "dustinwilson (17 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/mensbeam-http-client/health.svg)

```
[![Health](https://phpackages.com/badges/mensbeam-http-client/health.svg)](https://phpackages.com/packages/mensbeam-http-client)
```

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k5.9M738](/packages/sylius-sylius)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6942.5M420](/packages/drupal-core-recommended)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

751291.4k43](/packages/civicrm-civicrm-core)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
