ngawidev / nethttp
Lightweight PHP HTTP client with fluent interface, cURL-powered, async/await support
Requires
- php: >=7.4
- ext-curl: *
Requires (Dev)
None
Suggests
- ext-zstd: Required for auto zstd decode
Provides
None
Conflicts
None
Replaces
None
README
Lightweight PHP HTTP client with fluent interface, cURL-powered.
Installation
composer require ngawidev/nethttp
Quick Start
use Ngawidev\NetHttp\Client; $client = Client::new()->base('https://api.example.com'); $response = $client->request() ->acceptJson() ->bearer('your-token') ->get('/users') ->send(); echo $response->status; // 200 echo $response->ok(); // true echo $response->data; // array
Table of Contents
- Client
- Request
- Response
- Headers
- Authentication
- Query Parameters
- Request Body
- File Upload
- Download File
- Proxy
- Cookies
- SSL
- Timeout
- Retry
- Gzip
- Redirect
- Curl Options
- Middleware
- Debug Mode
- Request Logging
- Concurrent Requests
- Async/Await
- Zstd Auto Decode
- Browser Mimic
- Error Handling
Client
Client holds connection configuration. Each Client::new() creates a fresh instance.
use Ngawidev\NetHttp\Client; $client = Client::new() ->base('https://api.example.com') ->userAgent('MyApp/1.0') ->sslVerify(true) ->defaultTimeout(30) ->maxRedirects(5);
| Method | Description |
|---|---|
Client::new() |
Create new client instance |
base(string $url) |
Set base URL |
userAgent(string $agent) |
Set default User-Agent (default: NetHttp/1.0) |
sslVerify(bool) |
Enable/disable SSL verification (default: false) |
defaultTimeout(int $seconds) |
Set default timeout (default: 300) |
maxRedirects(int $max) |
Set max redirects (default: 5) |
debug(bool) |
Enable debug mode |
retry(int $times, int $delayMs) |
Set retry count and delay |
proxy(string $ip, int $port) |
Set HTTP proxy |
proxyHttp(string $ip, int $port) |
Set HTTP proxy |
proxySocks5(string $ip, int $port) |
Set SOCKS5 proxy |
proxyAuth(string $user, string $pass) |
Set proxy authentication |
cookie() |
Enable cookies |
cookieJar(string $path) |
Set cookie file (read/write) |
cookieFile(string $path) |
Set cookie file (read only) |
sendCookie(string $key, string $value) |
Send a cookie |
sendCookies(array $cookies) |
Send multiple cookies |
curlOpt(array $options) |
Set default cURL options |
before(callable $callback) |
Add before middleware |
after(callable $callback) |
Add after middleware |
getLogs() |
Get request logs |
clearLogs() |
Clear request logs |
Request
Request is built from $client->request().
$response = $client->request() ->get('/users') ->send();
| Method | Description |
|---|---|
get(string $url) |
Set GET request |
post(string $url) |
Set POST request |
put(string $url) |
Set PUT request |
patch(string $url) |
Set PATCH request |
delete(string $url) |
Set DELETE request |
timeout(int $seconds) |
Set request timeout |
connectTimeout(int $seconds) |
Set connection timeout |
maxRedirects(int $max) |
Set max redirects for this request |
gzip(bool) |
Enable gzip encoding |
asFile(string $path) |
Save response to file |
debug(bool) |
Enable debug for this request |
curlOpt(array $options) |
Set cURL options for this request |
send() |
Execute request and return Response |
Response
$response = $client->request()->get('/data')->send(); $response->status; // int: HTTP status code $response->success; // bool: true if 2xx $response->ok(); // bool: true if success $response->failed(); // bool: true if failed $response->data; // mixed: decoded JSON or string $response->error; // ?string: error message $response->elapsed; // float: time in seconds $response->url; // string: full URL $response->requestHeaders; // array: request headers $response->responseHeaders; // array: response headers
| Method | Description |
|---|---|
header(string $key) |
Get response header (case-insensitive) |
json() |
Get data as array |
xml() |
Get data as SimpleXMLElement |
body() |
Get raw body as string |
Headers
$response = $client->request() ->header('X-Custom', 'value') ->headers([ 'X-Api-Key' => 'abc123', 'Accept' => 'text/html', ]) ->get('/data') ->send();
Authentication
// Basic Auth $client->request() ->basicAuth('username', 'password') ->get('/protected') ->send(); // Bearer Token $client->request() ->bearer('your-token-here') ->get('/protected') ->send();
Query Parameters
$client->request() ->query(['page' => 1, 'limit' => 10]) ->get('/users') ->send(); // URL: /users?page=1&limit=10
Request Body
// JSON $client->request() ->json(['name' => 'John', 'email' => 'john@test.com']) ->post('/users') ->send(); // Form URL Encoded $client->request() ->form(['username' => 'admin', 'password' => 'secret']) ->post('/login') ->send(); // Multipart Form Data $client->request() ->multipart(['name' => 'John Doe', 'email' => 'john@test.com']) ->post('/upload') ->send();
File Upload
$response = $client->request() ->multipart(['description' => 'My photo']) ->attach('photo', '/path/to/photo.jpg', 'image/jpeg') ->post('/upload') ->send();
Download File
$response = $client->request() ->asFile('/path/to/save/file.zip') ->get('/download/archive.zip') ->send(); // File saved to /path/to/save/file.zip
Proxy
// HTTP Proxy $client = Client::new() ->base('https://api.com') ->proxyHttp('127.0.0.1', 8080); // SOCKS5 Proxy $client = Client::new() ->base('https://api.com') ->proxySocks5('127.0.0.1', 1080); // With Auth $client = Client::new() ->base('https://api.com') ->proxyHttp('127.0.0.1', 8080) ->proxyAuth('user', 'pass');
Cookies
// Send cookies $client = Client::new() ->base('https://api.com') ->cookie() ->sendCookie('session', 'abc123') ->sendCookies(['theme' => 'dark', 'lang' => 'id']); // Cookie jar (read/write) $client = Client::new() ->base('https://api.com') ->cookieJar('/tmp/cookies.txt'); // Cookie file (read only) $client = Client::new() ->base('https://api.com') ->cookieFile('/tmp/cookies.txt');
SSL
// Enable SSL verification $client = Client::new() ->base('https://api.com') ->sslVerify(true); // Disable SSL verification (default) $client = Client::new() ->base('https://api.com') ->sslVerify(false);
Timeout
// Default timeout for all requests $client = Client::new() ->base('https://api.com') ->defaultTimeout(60); // Per-request timeout $client->request() ->timeout(10) // total timeout ->connectTimeout(5) // connection timeout ->get('/slow-endpoint') ->send();
Retry
$client = Client::new() ->base('https://api.com') ->retry(3, 2000); // 3 retries, 2s delay $response = $client->request() ->get('/flaky-endpoint') ->send();
Gzip
$response = $client->request() ->gzip() ->get('/large-data') ->send(); // Response automatically decoded
Zstd Auto Decode
Response dengan Content-Encoding: zstd atau zstd-compressed data akan otomatis di-decode jika ext-zstd terinstall.
// Install ext-zstd terlebih dahulu // pecl install zstd // Auto decode - user gak perlu ngapa-ngapain $response = $client->request() ->get('/compressed-data') ->send(); // $response->data sudah decoded otomatis
Cek availability:
if (Client::isZstdAvailable()) { echo "ext-zstd installed\n"; }
Kalau ext-zstd tidak terinstall:
RequestException: Response is zstd-compressed. Install ext-zstd: pecl install zstd
Browser Mimic
Mimic browser fingerprint dengan versi yang presisi. Setiap browser mengirim headers yang berbeda, termasuk Sec-CH-UA (Client Hints) untuk Chrome/Edge.
Basic Usage
// Specific browser (random version) $client = Client::new('chrome')->base('https://example.com'); // Specific browser + version $client = Client::new('chrome:150')->base('https://example.com'); // Random version within range $client = Client::new('chrome:130-150')->base('https://example.com'); // Random browser + random version $client = Client::new('random')->base('https://example.com');
Supported Browsers
| Browser | Version Range | Sec-CH-UA | Sec-Fetch-* |
|---|---|---|---|
| Chrome | 124 - 154 | Yes | Yes |
| Firefox | 124 - 156 | No | Yes |
| Edge | 124 - 154 | Yes | Yes |
| Safari | 18 - 26 | No | No |
Version Ranges
Chrome: 124 (Apr 2024) - 154 (Sep 2026)
Firefox: 124 (Mar 2024) - 156 (Sep 2026)
Edge: 124 (Apr 2024) - 154 (Sep 2026)
Safari: 18 (2024) - 26 (2026)
Header Details
Chrome 150 - includes Client Hints:
User-Agent: Mozilla/5.0 ... Chrome/150.0.0.0 ...
Sec-CH-UA: "Google Chrome";v="150", "Chromium";v="150", "Not.A/Brand";v="24"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Firefox 153 - NO Client Hints (Firefox tidak kirim):
User-Agent: Mozilla/5.0 ... Firefox/153.0
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Safari 26 - NO Sec-Fetch (Safari tidak kirim):
User-Agent: Mozilla/5.0 ... Version/26.0 Safari/605.1.15
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Get Profile Info
$client = Client::new('chrome:150'); $profile = $client->getBrowserProfile(); echo $profile->getBrowser(); // "chrome" echo $profile->getVersion(); // 150 print_r($profile->getHeaders()); // All headers
Override Headers
$client = Client::new('chrome:150') ->base('https://example.com'); // Override User-Agent per-request $client->request() ->getUserAgent('MyBot/1.0') ->get('/path') ->send();
Redirect
// Set max redirects $client = Client::new() ->base('https://api.com') ->maxRedirects(3); // Or per-request $client->request() ->maxRedirects(10) ->get('/redirect-chain') ->send();
Curl Options
// Default options for all requests $client = Client::new() ->base('https://api.com') ->curlOpt([ CURLOPT_SSL_VERIFYPEER => true, CURLOPT_TIMEOUT => 30, ]); // Per-request options (override defaults) $client->request() ->curlOpt([ CURLOPT_TIMEOUT => 60, CURLOPT_POSTFIELDS => null, ]) ->get('/endpoint') ->send();
Middleware
$client = Client::new()->base('https://api.com'); // Before request $client->before(function ($request, $ch) { echo "Sending: {$request->getMethod()} {$request->getUrl()}\n"; }); // After request $client->after(function ($response, $request) { echo "Got: {$response->status} in {$response->elapsed}s\n"; }); $client->request()->get('/data')->send();
Debug Mode
// Enable on client $client = Client::new() ->base('https://api.com') ->debug(); // Or per-request $client->request() ->debug() ->get('/data') ->send();
Output:
[NetHttp DEBUG]
GET https://api.com/data
Status: 200
Time: 150.23ms
Request Headers:
Host: api.com
user-agent: NetHttp/1.0
Response Headers:
content-type: application/json
[/NetHttp DEBUG]
Request Logging
$client = Client::new()->base('https://api.com'); $client->request()->get('/users')->send(); $client->request()->post('/users')->send(); $logs = $client->getLogs(); // [ // ['method' => 'GET', 'url' => 'https://api.com/users', 'status' => 200, 'time' => 0.15], // ['method' => 'POST', 'url' => 'https://api.com/users', 'status' => 201, 'time' => 0.23], // ] $client->clearLogs();
Concurrent Requests
$client = Client::new()->base('https://api.com'); $requests = [ $client->request()->acceptJson()->get('/users'), $client->request()->acceptJson()->get('/posts'), $client->request()->acceptJson()->get('/comments'), ]; $results = Client::pool($requests); foreach ($results as $i => $response) { echo "Request {$i}: {$response->status}\n"; }
Async/Await (Promise-like)
Async execution tanpa dependency external. Menggunakan curl_multi di backend.
Single Async
$client = Client::new()->base('https://api.com'); // Block sampai response ready $response = $client->request() ->acceptJson() ->get('/users') ->async() ->await(); echo $response->status;
Async with Callbacks
$client->request() ->acceptJson() ->get('/users') ->async() ->then(function ($response) { echo "Success: {$response->status}\n"; }) ->catch(function ($error) { echo "Error: {$error}\n"; }) ->await();
Parallel Async Pool
$client = Client::new()->base('https://api.com'); $results = Client::asyncPool([ $client->request()->acceptJson()->get('/users'), $client->request()->acceptJson()->get('/posts'), $client->request()->acceptJson()->get('/comments'), ])->await(); // Semua request dieksekusi secara parallel foreach ($results as $i => $response) { echo "Request {$i}: {$response->status}\n"; }
Parallel with Callbacks
Client::asyncPool([ $client->request()->acceptJson()->get('/users'), $client->request()->acceptJson()->get('/posts'), ]) ->then(function ($responses) { // Semua berhasil echo "All " . count($responses) . " requests succeeded!\n"; }) ->catch(function ($error) { // Ada error echo "Error: {$error}\n"; }) ->await();
Error Handling
use Ngawidev\NetHttp\Exception\RequestException; try { $response = $client->request() ->get('/data') ->send(); if ($response->failed()) { echo "Request failed: {$response->error}\n"; } } catch (RequestException $e) { echo "cURL error: {$e->getMessage()}\n"; echo "cURL error detail: {$e->getCurlError()}\n"; }
License
MIT