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

Ringphp Laravel Package

guzzlehttp/ringphp

RingPHP provides a low-level API for sending HTTP requests in PHP, with pluggable handlers for cURL, streams, and mock transports. It powers Guzzle's request layer and offers a simple, extensible interface for building custom HTTP clients.

View on GitHub
Deep Wiki
Context7

Getting Started

RingPHP is archived (last release 2018-07-31) and is not a dependency you should add to a new Laravel app. You are almost certainly here because a legacy package pulled it in transitively — typically Guzzle 5.x, or an older SDK that pinned guzzlehttp/guzzle: ~5.0. Confirm with:

composer why guzzlehttp/ringphp
composer show guzzlehttp/ringphp

If nothing depends on it, remove it. If something does, pin it (composer require guzzlehttp/ringphp:^1.1) and plan a migration rather than new work on top of it.

Where to look first:

  • README.md — the "Simple API" section is the entire contract: a handler is a callable function (array $request): array $response.
  • src/Core.php — static helpers for headers, URLs, bodies, success checks.
  • src/Client/CurlHandler.php, StreamHandler.php, MockHandler.php, CurlMultiHandler.php — the concrete transports.
  • src/Middleware.php — mapRequest, mapResponse, history.

First use case — a single request against a transport directly, no Guzzle involved:

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

$handler = new CurlHandler();

$response = $handler([
    'http_method' => 'GET',
    'scheme'      => 'https',
    'uri'         => '/posts/1',
    'headers'     => [
        'Host'   => ['jsonplaceholder.typicode.com'],
        'Accept' => ['application/json'],
    ],
    'client' => [
        'curl' => [CURLOPT_TIMEOUT => 5],
    ],
]);

if (isset($response['error'])) {
    throw $response['error'];          // transport-level failure
}

$body = Core::body($response);         // reads the stream into a string

That is the package in one snippet: an array in, an array out, errors returned rather than thrown.

Implementation Patterns

Wrap a handler with defaults instead of repeating request keys. Core::wrap() merges base client options and is the idiomatic place for a base URL, default headers, and timeouts:

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

$handler = Core::wrap(new CurlHandler(), [
    'client' => [
        'curl'     => [CURLOPT_CONNECTTIMEOUT => 2],
        'base_url' => 'https://api.example.com',
    ],
]);

Core::url($request) then resolves relative URIs against base_url, and Core::applyOptions() merges per-request client overrides.

Compose middleware as decorators. Every middleware is just a callable that takes a handler and returns a handler, so they nest in either direction and stack cleanly:

use GuzzleHttp\Ring\Middleware;

$handler = new CurlHandler();

// Record every exchange for debugging or audit logs.
$handler = Middleware::history($handler, $container);

// Add auth + correlation id to every request.
$handler = Middleware::mapRequest($handler, function (array $request) {
    $request['headers']['Authorization'] = ['Bearer ' . config('services.api.token')];
    $request['headers']['X-Request-Id']  = [(string) Str::uuid()];
    return $request;
});

// Normalize JSON responses into arrays, leaving errors untouched.
$handler = Middleware::mapResponse($handler, function (array $response) {
    if (!isset($response['error']) && $response['status'] === 200) {
        $response['json'] = json_decode(Core::body($response), true);
    }
    return $response;
});

Bind the handler as a singleton in a service provider. RingPHP ships no framework integration; wiring is yours. Registering a single wrapped handler lets you swap transports per environment (curl in prod, mock in tests) through the container:

$this->app->singleton('api.handler', fn () => /* composed handler above */);

Use MockHandler for tests. It takes a queue of response arrays and satisfies the same callable contract, so tests exercise your middleware and mapping code without touching the network:

use GuzzleHttp\Ring\Client\MockHandler;

$handler = new MockHandler([
    ['status' => 200, 'headers' => ['Content-Type' => ['application/json']], 'body' => '{"id":1}'],
    ['status' => 500, 'body' => ''],
]);

Parallel fan-out with CurlMultiHandler. Unlike CurlHandler, it returns a FutureArrayInterface. This is the one capability RingPHP still does well and the main reason legacy code holds onto it:

$multi = new CurlMultiHandler();
$futures = collect($urls)->map(fn ($u) => $multi->__invoke([...]));
foreach ($futures as $f) { $f->wait(); }   // or ->then(...)

Choose the transport deliberately. CurlHandler needs ext-curl and is fastest; StreamHandler works with only allow_url_fopen and is the fallback when curl is unavailable. Do not rely on behavioral parity between them — see below.

Gotchas and Tips

  • It is abandoned. No PHP 8.x fixes, no security patches. Vendor the source ("repositories" + a fork) if you must run it on PHP 8.1+, and budget a migration. The modern equivalent for everything here is Guzzle 7's GuzzleHttp\HandlerStack + PSR-7/PSR-18; Laravel's Http facade already sits on it. Map Middleware::mapRequest → HandlerStack::push middleware, and $response['status']/Core::body() → $response->status()/(string) $response->getBody().
  • Errors are values, not exceptions. Always check isset($response['error']) before reading status; error responses have no status key at all. The value is an exception (ConnectException, CurlException), and HTTP 4xx/5xx is a perfectly normal response.
  • Headers are always arrays of strings, never scalars, and the headers key may be missing entirely on empty responses. Use Core::hasHeader() / Core::headerLines() rather than indexing directly.
  • body is a stream resource for curl/stream handlers but a plain string for MockHandler. Never assume one or the other — always funnel through Core::body($response), which handles both and rewinds.
  • client is the only key transports read for configuration, and the only place curl options, base_url, verify, and connect_timeout should live. Raw CURLOPT_* arrays are passed straight through, so an accidental CURLOPT_HEADER => true corrupts body parsing; and CURLOPT_* options are ignored entirely by StreamHandler.
  • Curl and stream transports diverge on redirects, TLS verification defaults, and timeouts (follow_location vs CURLOPT_FOLLOWLOCATION, timeout vs CURLOPT_TIMEOUT). If you support both, test both — or hard-require ext-curl and delete the stream path.
  • History middleware leaks secrets. It captures full request arrays including Authorization headers and bodies; scrub before persisting, and disable it outside debug.
  • Debugging tip: dump $container from Middleware::history to inspect the exact request array your middleware produced versus what the transport sent — most "wrong URL/header" bugs are middleware ordering, since decorators apply in reverse of wrapping order.
  • Extension point: a custom callable handler is the cheapest hook for retries, circuit breaking, and metrics — it wraps the transport without subclassing anything. For curl-level hooks (connection reuse, custom response parsing), extend GuzzleHttp\Ring\Client\CurlFactory instead.
  • Composer conflict trap: ringphp and guzzlehttp/guzzle:^6|^7 cannot coexist. If a transitive dependency forces both, use Composer replace/provide shims only as a temporary bridge and treat it as a migration signal.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core