n5s / http-cli
Serverless HTTP client - make requests to PHP scripts on the command line
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/n5s/http-cli
Requires
- php: ^8.2
- symfony/http-foundation: ^7.3 || ^8.0
- symfony/process: ^7.3 || ^8.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.48
- guzzlehttp/guzzle: ^7.10
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^11.0 || ^12.0
- rector/rector: ^2.3
- rmccue/requests: ^2.0
- spatie/temporary-directory: ^2.3
- symfony/http-client: ^7.3 || ^8.0
- symplify/easy-coding-standard: ^13.0
Suggests
- guzzlehttp/guzzle: For Guzzle HTTP Client adapter integration
- rmccue/requests: For WordPress Requests library adapter integration
- symfony/http-client: For Symfony HTTP Client adapter integration
README
Serverless HTTP client - make requests to PHP scripts on the CLI.
This library lets you make HTTP requests to PHP applications without running a web server. Instead of Apache or nginx, it executes your PHP scripts directly via the command line while emulating the full HTTP environment ($_GET, $_POST, $_SERVER, $_SESSION, headers, cookies, etc.).
Perfect for testing, CI pipelines, unreleased deployment or any scenario where spinning up a web server is overkill or not possible.
Installation
composer require n5s/http-cli
Usage
use n5s\HttpCli\Client; use n5s\HttpCli\RequestOptions; $client = new Client('/path/to/your/app'); // Simple GET request $response = $client->request('GET', 'https://example.com/api/users'); echo $response->getContent(); // POST with JSON $response = $client->request('POST', '/api/users', RequestOptions::create() ->json(['name' => 'John', 'email' => 'john@example.com']) ->build() ); // POST with form data $response = $client->request('POST', '/login', RequestOptions::create() ->formParams(['username' => 'admin', 'password' => 'secret']) ->build() );
How It Works
When you make a request, the library:
- Spawns a PHP CLI process targeting your script
- Injects a bootstrap that populates
$_GET,$_POST,$_SERVER,$_COOKIE, and$_SESSION - Provides polyfills for specific HTTP context functions:
header(),headers_sent(),http_response_code(), etc. - Captures the output and headers, returning a
Responseobject
Your PHP scripts run exactly as they would under a web server, but without one.
Framework Adapters
Use your favorite HTTP client library - just swap in our handler.
Guzzle
composer require guzzlehttp/guzzle
use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use n5s\HttpCli\Guzzle\CliHandler; use n5s\HttpCli\Client; $cliClient = new Client('/path/to/your/app'); $handler = new CliHandler($cliClient); $client = new Client([ 'handler' => HandlerStack::create($handler), ]); // Use Guzzle as normal $response = $client->get('/api/users'); $response = $client->post('/api/users', [ 'json' => ['name' => 'John'], ]);
Symfony HttpClient
composer require symfony/http-client
use n5s\HttpCli\Symfony\CliClient; use n5s\HttpCli\Client; $cliClient = new Client('/path/to/your/app'); $client = new CliClient($cliClient); // Use Symfony HttpClient as normal $response = $client->request('GET', '/api/users'); $data = $response->toArray();
WordPress Requests
composer require rmccue/requests
use WpOrg\Requests\Requests; use n5s\HttpCli\WordPress\Cli; use n5s\HttpCli\Client; $cliClient = new Client('/path/to/your/app'); Requests::set_transport([Cli::class]); Cli::setClient($cliClient); // Use WordPress Requests as normal $response = Requests::get('/api/users');
Request Options
Build requests with a fluent API:
use n5s\HttpCli\RequestOptions; $options = RequestOptions::create() // Body ->json(['key' => 'value']) // JSON payload ->formParams(['field' => 'value']) // Form data (application/x-www-form-urlencoded) ->body('raw content') // Raw body ->multipart([ // Multipart form data ['name' => 'file', 'contents' => 'data', 'filename' => 'test.txt'], ]) // Headers & Auth ->headers(['X-Custom' => 'value']) ->basicAuth('user', 'pass') ->bearerToken('token') ->cookies(['session' => 'abc123']) // Other ->query(['page' => 1, 'limit' => 10]) ->timeout(30.0) ->build(); $response = $client->request('POST', '/api/endpoint', $options);
Response
$response = $client->request('GET', '/api/users'); $response->getStatusCode(); // 200 $response->getHeaders(); // ['Content-Type: application/json', ...] $response->getContent(); // Response body as string $response->getSession(); // Session data array $response->getProcess(); // Symfony Process instance (for debugging)
Configuration
$client = new Client( documentRoot: '/path/to/your/app', // Required: your app's root directory file: 'index.php', // Entry point (default: index.php) phpExecutable: null, // PHP binary path (auto-detected) );
Adapter Options Support
Guzzle
| Option | Supported |
|---|---|
timeout |
✅ |
headers |
✅ |
query |
✅ |
body |
✅ |
json |
✅ |
form_params |
✅ |
multipart |
✅ |
auth |
✅ |
cookies |
✅ |
allow_redirects |
✅ |
http_errors |
✅ |
decode_content |
✅ |
version |
✅ |
sink |
✅ |
on_headers |
✅ (callback) |
on_stats |
✅ (callback) |
connect_timeout |
❌ ignored |
verify |
❌ ignored |
cert |
❌ ignored |
proxy |
❌ ignored |
ssl_key |
❌ |
progress |
❌ |
debug |
❌ |
Symfony HttpClient
| Option | Supported |
|---|---|
timeout |
✅ |
headers |
✅ |
query |
✅ |
body |
✅ |
json |
✅ |
auth_basic |
✅ |
auth_bearer |
✅ |
max_redirects |
✅ |
verify_peer |
❌ ignored |
verify_host |
❌ ignored |
cafile |
❌ ignored |
proxy |
❌ ignored |
http_version |
❌ |
on_progress |
❌ |
resolve |
❌ |
local_cert |
❌ |
local_pk |
❌ |
ciphers |
❌ |
WordPress Requests
| Option | Supported |
|---|---|
timeout |
✅ |
useragent |
✅ |
redirects |
✅ |
follow_redirects |
✅ |
auth |
✅ |
cookies |
✅ |
connect_timeout |
❌ ignored |
proxy |
❌ ignored |
verify |
❌ ignored |
verifyname |
❌ ignored |
filename |
❌ |
hooks |
❌ |
max_bytes |
❌ |
Limitations
Running PHP scripts via CLI instead of a web server comes with inherent limitations:
Not Supported
| Feature | Reason |
|---|---|
| Persistent connections | Each request spawns a new PHP process |
| Keep-alive | No connection reuse between requests |
| HTTP/2, HTTP/3 | CLI execution doesn't use HTTP protocol |
| WebSockets | Requires persistent connection |
| Server-Sent Events | Requires streaming connection |
| Real SSL/TLS | No actual HTTPS handshake (URLs are parsed, not connected) |
| Output streaming | Response is captured after script completes |
fastcgi_finish_request() |
FPM-specific function |
| APCu user cache | Not shared between CLI processes |
| OPcache benefits | Each process starts fresh |
| Static files | Only executes PHP - images, fonts, CSS, JS won't be served |
Behavioral Differences
- Performance: Process spawning overhead, but no DNS resolution, TCP/SSL handshake, or network latency
$_SERVERvalues: Some values likeSERVER_SOFTWAREwill differ from Apache/nginx- File uploads: Multipart parts are written to temp files and populated in
$_FILES - Session handling: Works but uses an in-memory handler, not file-based persistence
php://input: Custom stream wrapper provides the request body
License
MIT