Namespace: Zero\Lib\Http (the Http facade), Zero\Lib\Http\PendingRequest, Zero\Lib\Http\ClientResponse.
The HTTP client wraps PHP's cURL extension. There are two ways to use it:
- Fluent (preferred) — returns a
ClientResponse - Legacy static (back-compatible) — returns a plain
stdClasswithok,status,body,json,error
use Zero\Lib\Http;
// Fluent
$r = Http::timeout(10)->acceptJson()->get('https://api.example.com/posts');
if ($r->successful()) {
$posts = $r->json('data', []);
}
// Legacy
$r = Http::get($url, $query, $headers, $timeout);
if ($r->ok) {
$data = $r->json;
}Requests don't throw by default — call $r->throw() (or ->throw(true) on the pending request) to opt in.
Implementation: PendingRequest.php. Every fluent setter on Http::* returns a PendingRequest. Terminal verbs (get/post/put/patch/delete/head/send) execute and return a ClientResponse.
Total request timeout (also applies to connect when connectTimeout not set).
Http::timeout(5)->get($url);Http::connectTimeout(2)->timeout(10)->get($url);Http::withHeaders(['X-A' => '1', 'X-B' => '2'])->get($url);Http::withHeader('X-Trace', $traceId)->get($url);Http::acceptJson()->get($url);Http::accept('application/xml')->get($url);JSON-encode the body (default).
Http::asJson()->post($url, ['k' => 'v']);URL-encoded body.
Http::asForm()->post($url, ['email' => $email]);Multipart body — usually pair with attach().
Http::asMultipart()->attach('file', '/tmp/x.png')->post($url);Low-level switch: json, form, multipart, or body (raw string passthrough).
Http::bodyFormat('body')->contentType('text/xml')->post($url, $rawXml);Http::contentType('application/xml')->post($url, $xml);Http::withToken($accessToken)->get($url);Http::withBasicAuth('user', 'pass')->get($url);Merged with any query passed to the verb.
Http::withQueryParameters(['page' => 2])->get($url);Http::withCookies(['session' => $sid, 'theme' => 'dark'])->get($url);Http::withUserAgent('Zero/1.0')->get($url);Disable TLS peer/host verification (development only).
Http::withoutVerifying()->get('https://self-signed.test');Prefix relative URLs.
Http::baseUrl('https://api.example.com')->get('/users'); // https://api.example.com/usersRaw curl option overrides.
Http::withOptions([CURLOPT_FOLLOWLOCATION => false])->get($url);Http::attach(string $name, mixed $contents, ?string $filename = null, array $headers = []): PendingRequest
Multipart upload. $contents may be a path on disk or an in-memory string.
Http::attach('avatar', '/tmp/me.png', 'avatar.png')->post($url);
Http::attach('payload', $bytes, 'payload.bin')->post($url);Auto-retry on failure. Optional $when callback receives ($response, $attempt).
Http::retry(3, 200)->get($url);
Http::retry(3, 200, fn ($r, $i) => $r->serverError())->get($url);Debug the outgoing request (then continue / die).
Http::dump()->withToken($t)->get($url);Http::setDefaultHeaders(['X-Client' => 'zero/1.0']);
Http::addDefaultHeader('X-Trace', $traceId);These persist for both legacy and fluent calls in the same process.
All return a ClientResponse when called fluently.
Http::acceptJson()->get('https://api.example.com/users', ['page' => 2]);Returns headers only (no body).
Http::acceptJson()->head($url);Http::asJson()->post($url, ['email' => $email]);Http::asJson()->put("$url/$id", $payload);Http::asJson()->patch("$url/$id", ['status' => 'active']);Http::asJson()->delete("$url/$id");Custom verb (or runtime-chosen).
Http::asJson()->send('PURGE', $url);Implementation: ClientResponse.php.
Http::get($url)->status(); // 200$body = Http::get($url)->body();Decode the JSON body. Dot-notation supported.
$r = Http::asJson()->get($url);
$r->json(); // full decoded array
$r->json('data.user.id'); // dot-path lookup
$r->json('missing', 'fallback'); // defaultJSON decoded as stdClass.
Http::asJson()->get($url)->object()?->user?->id;$r = Http::get($url);
$r->headers(); // ['Content-Type' => ['application/json'], ...]
$r->header('Content-Type'); // 'application/json'$r = Http::get($url);
$r->ok(); // status === 200
$r->successful(); // 200..299
$r->redirect(); // 300..399
$r->clientError(); // 400..499
$r->serverError(); // 500..599
$r->failed(); // connection error OR 4xx OR 5xx
$r->unauthorized(); // 401
$r->forbidden(); // 403
$r->notFound(); // 404Underlying curl error, if any.
Http::timeout(1)->get('http://10.255.255.1')->error(); // 'Operation timed out...'Throw RuntimeException if failed(); otherwise return $this for chaining.
$r = Http::asJson()->get($url)->throw();Casts to the response body.
$body = (string) Http::get($url);The original positional-argument API still works and returns a plain stdClass:
$r = Http::get($url, $query, $headers, $timeout);
$r->ok; // bool
$r->status; // int
$r->body; // string|null
$r->json; // decoded array or null
$r->error; // string|nullAvailable on Http::get, Http::post, Http::put, Http::patch, Http::delete. Default headers from Http::setDefaultHeaders() apply to both APIs.