workerman/http-client
An asynchronous, non-blocking HTTP client for Workerman. It supports GET/POST requests, file uploads and downloads, redirects, cookies, proxies, timeouts, keep-alive, and concurrent callback-based requests for high-performance PHP applications.
Architecture fit. workerman/http-client is an event-loop-driven, non-blocking HTTP client, so its value is entirely a function of the runtime it lands in. It fits naturally where a Workerman loop already owns the process: webman services, custom Workerman workers, queue/consumer daemons, and internal fan-out or aggregation services. It is a poor architectural fit for a conventional Laravel deployment on nginx + PHP-FPM, where each request is a blocking execution slice — you would have to start and drain a loop inside the request to get any benefit, adding lifecycle risk for near-zero latency gain. It is also a risky fit inside Laravel Octane: Octane's Swoole/RoadRunner/FrankenPHP workers already own a concurrency model, and introducing a second event loop means two schedulers competing over the same process, with subtle blocking and shutdown-ordering hazards.
Integration feasibility. There is no Laravel service provider, no facade, and no PSR-18/PSR-7 compliance — the API is promise/callback oriented, which means it does not drop into Illuminate\Support\Facades\Http. Everything Laravel teams assume around outbound HTTP is absent: Guzzle middleware, Http::fake() in tests, retry/backoff helpers, Telescope and HTTP-client events, and the standard logging/telemetry hooks. Integration therefore requires a thin internal adapter (a HttpTransport-style interface with a Guzzle implementation as default and this package as an opt-in implementation) plus bespoke test doubles. That is a bounded, well-understood piece of work, but it is work the team owns forever.
Technical risk. Medium-to-high, concentrated in four areas: (1) event-loop ownership and lifecycle — who starts it, who stops it, and what happens to in-flight promises on worker reload; (2) error semantics — unhandled promise rejections can surface as fatal errors or, worse, silently dropped requests depending on version and loop state; (3) long-lived process hygiene — connection pool state, DNS caching, keep-alive timeouts, and incremental memory growth only appear under sustained load, not in a demo; (4) ecosystem signal — the listed metadata (55 stars, 0 dependents, low opportunity score) is weak, and the English documentation trail is thin relative to Guzzle. The package is a legitimate component of the broader Workerman ecosystem rather than a standalone project, but a TPM should treat maintainer concentration and upstream Workerman major-version churn as real bus-factor exposure.
Key questions.
Stack fit. Segment by runtime before anything else. For webman/Workerman services and CLI workers doing high-fan-out I/O (aggregation, webhook dispatch, enrichment pipelines, scrapers), this is a native fit and should be the default client. For Octane, evaluate the Swoole coroutine HTTP client or Guzzle's request pool first — they solve the same problem inside the runtime the application already uses, with existing Laravel observability. For FPM, decline unless the workload is a batch/console job where a loop can legitimately run to completion.
Migration path. Do not rip and replace Guzzle. Introduce an internal HttpTransport contract, keep Laravel's Http/Guzzle as the default binding, and register this client as an alternate implementation for specific, named call sites. Migrate one workload at a time, gated behind a config flag, with a parallel-run period where the same request is issued through both paths in staging and responses are diffed. Retain Guzzle as a permanent fallback so rollback is a config change, not a deployment.
Compatibility. Pin PHP and workerman/workerman constraints explicitly and verify them against the rest of the lockfile — Workerman major versions have moved fast enough that transitive pins are a realistic conflict. Confirm whether a native event extension (ext-event/ext-ev) is available in the target images; stream_select fallback changes throughput characteristics and should be a conscious decision, not a surprise. Verify TLS/CA bundle behavior and HTTP/2 expectations against the package's actual capabilities rather than assuming Guzzle parity.
Sequencing.
Maintenance. Low release velocity is double-edged: few breaking changes, but also slow fixes for edge-case bugs and security advisories in the HTTP/TLS path. Budget a named owner and a quarterly review of upstream Workerman compatibility. Treat upgrades as planned work tied to Workerman majors, not as passive dependency bumps.
Support. No commercial backing and a small contributor base. Internal expertise will be one or two engineers deep unless deliberately spread. Plan for a written runbook covering loop lifecycle, pool sizing, timeout policy, and rollback to Guzzle — this is non-negotiable given the low external support surface.
Scaling. Strong where it matters: I/O-bound fan-out to many hosts benefits substantially from non-blocking sockets and keep-alive pooling relative to a blocking client issuing serial or thread-bound requests. Scaling constraints shift from request latency to per-worker memory, pool size, and file-descriptor limits — all of which need explicit configuration rather than defaults. Long-lived workers accumulate state; cap worker lifetime or add periodic recycling if memory does not stabilize in the soak test.
Failure modes. The dangerous ones are quiet: unhandled rejections that vanish instead of erroring, callbacks that block the loop and stall every other in-flight request, pool exhaustion under burst traffic, DNS or TLS handshakes that stall without honoring timeout expectations, and partial failures inside a batch that look like success at the aggregate level. Retry logic must live at the adapter layer with jitter and a global budget, or a downstream incident becomes a self-inflicted retry storm. Because Laravel's usual HTTP telemetry will not see these calls, missing instrumentation is itself a failure mode.
Ramp-up. A Laravel developer unfamiliar with async PHP will be productive in days on the happy path and dangerous within a week — the failure modes above are learned through incidents unless taught. Expect two to three weeks of genuine ramp-up for on-call competence, plus the adapter as living documentation. If the team cannot commit that, the correct decision is to stay on Guzzle/Octane-native clients and revisit when a concrete Workerman-based service makes the fit obvious.
How can I help you explore Laravel packages today?