octoper/cuzzle
Cuzzle is a Laravel-friendly cURL wrapper that simplifies HTTP requests with a clean, fluent API. Send GET, POST, and other requests, manage headers and options, and handle responses without boilerplate—ideal for quick API integrations.
Cuzzle solves one narrow, high-value problem: turning a request object you already have into a copy-pasteable curl string. Treat it as a debugging/logging utility, not an HTTP abstraction.
Install and locate the entry point
composer require octoper/cuzzle
The package is Laravel-friendly but framework-agnostic. On Laravel 5.5+ the service provider/facade should be auto-discovered; if Cuzzle isn't resolvable, register the provider and alias manually in config/app.php. Look at vendor/octoper/cuzzle/src/ first — the public surface is small (a Cuzzle facade/class plus a formatter), and the exact conversion method name (fromRequest(), toCurl(), or similar) is worth confirming in one glance rather than guessing. The core usage is:
use Octoper\Cuzzle\Cuzzle;
$request = new \GuzzleHttp\Psr7\Request(
'POST',
'https://api.example.com/orders?expand=items',
['Authorization' => 'Bearer '.$token, 'Content-Type' => 'application/json'],
json_encode(['sku' => 'ABC-1', 'qty' => 2])
);
echo Cuzzle::fromRequest($request); // curl -X POST ... 'https://api.example.com/orders?expand=items' ...
First use case: reproduce a failing outbound call
The highest-return first step is a Guzzle middleware or a Laravel beforeSending hook that dumps the curl command whenever an outbound API call fails. In a Laravel client class:
$response = Http::withToken($token)
->beforeSending(function ($request, $options) {
// $request is a PSR-7 request
logger()->debug('curl', ['cmd' => (string) Cuzzle::fromRequest($request)]);
})
->post('https://api.example.com/orders', $payload);
Then take the log line, paste it into a terminal, add -v, and diff Guzzle's behaviour against the raw call. That loop — log → paste → compare — is where the package earns its keep.
1. Opt-in debugging via config/env. Never format requests unconditionally in production. Gate it behind config('app.debug'), app()->isLocal(), or a dedicated services.cuzzle.enabled flag, plus a sampling switch for high-traffic endpoints.
2. Capture on failure only. Logging every outgoing request creates noise and leaks credentials. Wrap in try/catch or inspect status codes and emit the curl string only for 4xx/5xx:
try {
$response = Http::withToken($token)->post($url, $payload);
$response->throw();
} catch (\Throwable $e) {
logger()->error('Outbound API failed', [
'curl' => (string) Cuzzle::fromRequest($request),
'status' => optional($e->response)->status(),
]);
throw $e;
}
This is the single most useful pattern: it turns a vague "the API returned 422" bug report into a runnable artifact.
3. Guzzle handler-stack middleware for non-Laravel code. For a raw GuzzleHttp\Client, push a middleware that converts each request, either into a PSR-3 debug log or into a Middleware::history() container you inspect later. This keeps the conversion out of every call site and makes it switchable per client instance (e.g. only the "third-party billing" client logs curl).
4. Base-class hook in a Laravel API wrapper. If you have several Services\*Client classes, put the conversion in a shared trait/base class and expose a curlFor($request) helper. Centralizing it means one place to add redaction and one place to swap the implementation if the package goes unmaintained.
5. Bug reports and documentation. Paste the generated command into issue templates, PR descriptions, or integration docs. It's genuinely better than documenting headers in a table because recipients can run it immediately. Keep a "reproduce with" snippet in your internal wiki for each third-party integration.
6. Local verification loop. Use php artisan tinker to build the request, print the curl string, and run it. This decouples "is the API broken?" from "is our Laravel/Guzzle config broken?" — timeouts, proxies, TLS settings, and content-type quirks surface fast.
7. Redaction wrapper. Build a thin CurlDumper service that clones the request, replaces sensitive headers, optionally truncates very large bodies for log readability, then delegates to Cuzzle. That gives you a single, testable place to enforce what leaves the process.
$request->getBody()->rewind()), or clone the request before formatting. This is the most common way this package breaks working code.Authorization, Cookie, X-Api-Key, and signed query strings will be printed verbatim. Redact before logging, and check your log retention/aggregation before enabling this in staging.multipart/form-data request with a file stream cannot be faithfully represented as a curl string; expect placeholders or inline content. Use -F manually for those cases.->retry() attempts. With beforeSending, you'll get one line per attempt — make sure you log which attempt failed.composer require octoper/cuzzle -W and read the solver output). Isolate the dependency behind your own small CurlDumper interface so a replacement — even a 30-line handwritten string builder — is a one-file change. For the common case (method, URL, headers, body), the fallback is trivial.-v and compare against Guzzle's own debug => true output. Differences usually come from Guzzle's default headers (User-Agent, Content-Length) or option-level behaviour (http_errors, timeout, verify) that curl doesn't share — remember the generated command reproduces the request, not the client configuration.How can I help you explore Laravel packages today?