Search by

ngawidev / nethttp

ngawidev

Lightweight PHP HTTP client with fluent interface, cURL-powered, async/await support

Package info

github.com/ngawidev/nethttp

pkg:composer/ngawidev/nethttp

Statistics

Installs: 12

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.3 2026-09-18 05:10 UTC

This package is auto-updated.

Last update: 2026-09-18 05:16:57 UTC


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

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