Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ring Laravel Package

guzzlehttp/ring

A simple handler stack for Guzzle that lets PHP developers compose HTTP client middleware such as retries, redirects, cookies, and request history. Lightweight and easy to integrate, it helps customize request and response behavior in existing Guzzle clients with minimal overhead.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the honest context: this package is archived and deprecated. RingPHP was the handler layer behind Guzzle 4/5; Guzzle 6 replaced it with HandlerStack + PSR-7 middleware. With 0 dependents and a last release in 2018, it is not a candidate for new code. The realistic reason to read this assessment is that you're maintaining a legacy Guzzle 5 application, a library that still exposes a Ring handler, or you need to understand an old stack before migrating off it.

Composer install (historical package name is guzzlehttp/ringphp):

composer require guzzlehttp/ringphp

Look here first:

  • GuzzleHttp\Ring\HandlerInterface — the entire contract is one method: __invoke(array $request): array (or a future).
  • GuzzleHttp\Ring\Core — the utility belt: wrap(), url(), response(), exception(), header helpers. This is where 90% of the semantics live.
  • GuzzleHttp\Ring\Client\CurlHandler, CurlMultiHandler, StreamHandler, MockHandler — the shipped transports.
  • GuzzleHttp\Ring\Future\FutureArrayInterface — then(), wait(), realized().

Minimal first request (most people reached Ring indirectly through Guzzle, but you can drive a handler directly):

use GuzzleHttp\Ring\Client\CurlHandler;

$handler = new CurlHandler();

$response = $handler([
    'http_method' => 'GET',
    'uri'         => 'https://httpbin.org/get',
    'headers'     => ['Accept' => ['application/json']],
    'body'        => null,
]);

if ($response['error'] !== null) {
    throw $response['error'];
}

echo $response['status'];                       // 200
echo stream_get_contents($response['body']);   // body is a stream-ish

Requests and responses are plain arrays. That is the defining feature — and the biggest source of friction when you move to PSR-7.

Implementation Patterns

Middleware via Core::wrap(). This is the canonical composition primitive. You pass a handler and one or more middleware callables; each middleware receives the next handler and returns a new callable:

use GuzzleHttp\Ring\Core;
use GuzzleHttp\Ring\Client\CurlHandler;

$handler = Core::wrap(
    new CurlHandler(),
    function ($handler) {
        return function (array $request) use ($handler) {
            $request['headers']['X-Trace-Id'] = [bin2hex(random_bytes(8))];
            return $handler($request);
        };
    },
    function ($handler) {
        return function (array $request) use ($handler) {
            $start = microtime(true);
            $response = $handler($request);
            error_log(sprintf('%s took %.3fs', $request['uri'], microtime(true) - $start));
            return $response;
        };
    }
);

Order matters: the first middleware is the outermost layer, so it sees the request first and the response last.

Handler swapping for tests. MockHandler takes a queue (or callable) of canned response arrays and records what it received. Because handlers are just callables, this is dependency-free — no HTTP server, no container:

use GuzzleHttp\Ring\Client\MockHandler;

$mock = new MockHandler(['status' => 200, 'body' => fopen('php://temp', 'r+')]);
$client = new \GuzzleHttp\Client(['handler' => $mock]);

Async with CurlMultiHandler. Set 'client' => ['future' => true] in the request (or have the client do it) and you get back a FutureArrayInterface. Do useful work, then wait():

$handler = new CurlMultiHandler();

$a = $handler(['http_method' => 'GET', 'uri' => 'https://a.example/', 'client' => ['future' => true]]);
$b = $handler(['http_method' => 'GET', 'uri' => 'https://b.example/', 'client' => ['future' => true]]);

$ra = $a->wait();
$rb = $b->wait();

Internally the multi handler needs tick() calls to progress; wait() on a future from a CurlMultiHandler drives it, but if you're doing manual loops, remember to call $handler->tick().

Environment-based transport selection. Common legacy pattern: use CurlHandler/CurlMultiHandler when the cURL extension is present, StreamHandler (PHP wrappers) otherwise. Keep that decision in one factory function rather than sprinkling extension_loaded('curl') checks.

Bridging to PSR-7. When a modern component needs to consume a Ring handler, write an adapter: translate ServerRequestInterface/RequestInterface → Ring request array (http_method, uri, headers as name => [values], body), invoke the handler, then rebuild a PSR-7 response from status, headers, body, reason. Keep effective_url and transfer_stats on the new response as attributes; they're often the only way to debug redirects and timings.

Capturing traffic for debugging. There is no built-in history middleware. Roll one: a middleware that appends [$request, $response] to a local array (careful with bodies — clone/copy or buffer them, see gotchas).

Gotchas and Tips

  • $response['error'] is the source of truth, not status. On a failed request, status can be null or a stale curl code and error holds the exception/cURL error. Always check error before branching on status.
  • Bodies are streams, not strings. Reading $response['body'] with stream_get_contents() consumes it. If you need the body in middleware and the caller needs it, buffer it into a php://temp and rewind, or you'll hand downstream code an empty stream.
  • Arrays are passed by value but bodies aren't. PHP copies the request array cheaply, but a stream resource inside it is shared. Middleware that mutates a body affects everyone.
  • Headers are name => [values] with array values. Forget the inner array and cURL will silently send something unexpected. Use Core::header()/Core::headers() helpers rather than hand-rolling.
  • StreamHandler is not a drop-in for CurlHandler. No true async (futures resolve immediately), different timeout semantics (timeout vs. connect_timeout behave differently per transport), and transfer_stats keys differ. Tests that pass on cURL can fail on streams.
  • Future exceptions surface late. With FutureArrayInterface, an error only appears when you wait()/resolve. Exceptions thrown inside then() are easy to swallow — always attach an error path or resolve explicitly in a finally.
  • Middleware order is easy to invert. Read Core::wrap left-to-right as onion layers: first = outermost. Auth middleware that must run after redirect handling (or before) depends on this.
  • transfer_stats is the debugging goldmine. total_time, namelookup_time, connect_time, redirect_count come straight from cURL — log them in a middleware before you reach for a packet capture.
  • Migrating to Guzzle 6+ is the real endgame. The mapping: Ring\HandlerInterface → GuzzleHttp\HandlerStack; Core::wrap middleware → Middleware::mapRequest/mapResponse or a custom function (callable $handler) taking (RequestInterface, array $options); array request/response → PSR-7. The shape is deliberately similar, so a Ring middleware usually translates in an afternoon — but the array-to-object change means no mechanical copy-paste.
  • No support, no patches. Archived means you own any bug you find. Pin the version, vendor the source if you need to patch it, and put a migration ticket on the board rather than building new features on it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons