amphp/amp
amphp/amp provides futures and cancellations as core primitives for asynchronous PHP. Built on PHP 8.1 fibers and Revolt, it lets you write concurrent code that reads like synchronous code, using Amp\async() and Future::await() without callbacks.
Architecture fit — poor as a general dependency, viable as a bounded concurrency kernel. amphp/amp is not an integration package; it is a primitives layer (futures, cancellation, Amp\async() coroutines over PHP 8.1 fibers) that delegates scheduling to Revolt's global event loop. Its entire value proposition depends on the process running non-blocking I/O. Laravel's default runtime is the opposite: one blocking request per PHP-FPM process, sequential execution, and a data layer (PDO, curl via Guzzle, phpredis) that blocks the calling stack. Dropping Amp into a Laravel request without also replacing the I/O path yields zero concurrency and adds a new mental model plus a global event loop for nothing. The fit becomes real only where a single request or CLI job must fan out to many independent I/O operations — aggregate several upstream APIs, query multiple read replicas, or coordinate cache lookups — and only if every call on that path is backed by an Amp-native driver (amphp/http-client, amphp/mysql, amphp/postgres, amphp/redis).
Integration feasibility — composer-install trivial, adoption cost material. composer require amphp/amp revolt/event-loop will resolve cleanly on any Laravel 10/11 app (PHP 8.1/8.2 satisfy the requirement, no extensions needed). The cost is not the install but the rewrite of the I/O path for whatever you touch. Eloquent and the DB facade are out of scope — you do not get concurrent Model::query(); you get concurrent raw queries through amphp/mysql in a separate connection pool, which means duplicating connection configuration, transaction handling, and query-building logic outside the ORM. That is a substantial bespoke adapter, not a package integration. The realistic integration shape is a thin internal service object that returns Futures, with Future::await() at exactly one boundary, so the rest of the application remains synchronous and unaware.
Technical risk — moderate to high, concentrated in three areas.
file_get_contents, an Eloquent call, sleep(), a logging handler that writes synchronously) stalls the whole event loop, not just one task. This failure is invisible in code review and appears only as latency under concurrency.Cancellation is a first-class primitive here but nothing enforces it. If it is not threaded from request disconnect through to every in-flight future, background work outlives the client and quietly consumes worker capacity.Data quality caveat. Two signals in the supplied metadata are unreliable and should be re-verified against Packagist and GitHub before any decision: "Dependents: 0" is implausible for a 4.4k-star foundational package that virtually every amphp/* library depends on, and a last-release date of 2026-07-26 cannot be genuine. Treat the score (59.4, opportunity 54.0) as directional only.
Key questions for the team.
Illuminate\Http\Client\Pool (concurrent request batching) or the Concurrency facade been evaluated first? Both solve the most common Laravel fan-out cases with zero new runtime semantics, and the Pool in particular covers the majority of Amp's practical value here.Stack fit — narrow and deliberate. Target only workloads that are unambiguously I/O-bound and high-fan-out: an aggregation endpoint calling 5–15 upstream services, a CLI/queue job enriching records from several APIs, or a health-check/probe path that must hit many dependencies with a short deadline. Explicitly out of scope: anything CPU-bound (that is amphp/parallel, a separate and much heavier decision), anything needing Eloquent's identity map, relationship loading, or model events, and any code path reached from a standard web request that we are not prepared to make fully non-blocking.
Migration path — adapter pattern, one boundary, feature-flagged. Build App\Concurrency\AsyncRunner (or a small internal package) exposing plain synchronous methods that internally construct the event loop scope, dispatch Amp\async() tasks, Future\await() them, and return ordinary arrays or DTOs. Callers see no Future types, no Cancellation, no Revolt imports. This keeps the blast radius to one class and lets us revert by changing one binding. If the workload is genuinely long-running or must sustain high connection counts, the lower-risk alternative is a separate amphp/http-server service behind HTTP, leaving Laravel untouched — more operational surface, but no runtime coupling and no risk to the main application.
Compatibility — verified constraints.
revolt/event-loop as a sibling dependency — pin both, since their majors move together.ev/uv/event only matter above ~1024 concurrent descriptors.static caches keyed on request state, no $_SERVER reads after suspension). Audit anything touched after an await.Sequencing.
amphp/http-client, measure p50/p95 against the existing Http::pool() implementation. Gate: material latency win under realistic concurrency.AsyncRunner, a coding standard forbidding blocking calls in async paths, and a static-analysis rule or grep check for file_get_contents/sleep/PDO in App\Concurrency.Cancellation sourced from request lifecycle. Roll out to a small traffic percentage.amphp/mysql adapter is proven separately.Maintenance — low code churn, high cognitive load. Amp's public surface is small and stable, but v3 was a ground-up rewrite from generator coroutines to fibers; a v4 will likely be breaking again. Budget for a coordinated Amp + Revolt major upgrade roughly every 18–24 months, plus a review of every internal async path against whatever semantics change. The adapter layer is the thing we own; treat it as production code with a named maintainer, not as glue.
Support — community-only. MIT-licensed, maintained by the AMPHP organisation, no commercial SLA, no vendor to escalate to. Issue turnaround depends on a small group of maintainers. Acceptable for a bounded internal use; not acceptable if the concurrency layer becomes load-bearing for revenue-critical paths without a documented fallback.
Scaling — helps with concurrency, not capacity. Fibers reduce wall-clock latency for I/O fan-out within a single process but do nothing for CPU throughput; a single core is still a single core, and CPU-bound work inside a coroutine starves everything. Watch three limits: file descriptors (default soft limit ~1024 constrains concurrent sockets), memory per suspended fiber (small but non-zero, and multiplied by concurrency), and, under Octane, per-worker memory that no longer resets between requests. Enforce a concurrency cap at the adapter level rather than allowing unbounded Amp\async() spawning.
Failure modes to instrument for.
Cancellation parameter, deadline on every adapter call.await(); a future that is never awaited, or a catch() that returns a default, silently loses failures. Mitigation: adapter always awaits or explicitly discards with logging; forbid fire-and-forget except through a helper that records errors.EventLoop::run() in a context where the loop is already running throws; under FPM the loop must be started and drained within the request. Mitigation: adapter owns loop lifecycle and asserts it, never callers.Ramp-up — 1–2 weeks to productive, 1–2 months to safe. The futures/cancellation model reads like synchronous code, which is the trap: it looks safe to a developer who does not yet know that one blocking call poisons the loop. Required onboarding: "what color is your function" framing, the cooperative-scheduling model, a hands-on exercise stalling a loop deliberately, and the review checklist (no blocking I/O, cancellation threaded, bounded concurrency, no unawaited futures). Pair that with an ADR recording why Amp was chosen over Http::pool() and the Concurrency facade, so the next engineer does not re-litigate it or, worse, remove the guardrails.
TPM recommendation. Conditional. The composite score (59.4) and a 54.0 opportunity rating fairly reflect a mature, well-regarded, narrow-purpose library. For a typical Laravel product, the correct answer is to reach for framework-native concurrency first and treat amphp/amp as a specialized tool for one or two proven high-fan-out I/O paths, isolated behind an internal adapter with a named owner. Adopt it as a platform-wide concurrency foundation only if we are separately committing to an async runtime (amp HTTP server or equivalent) — in which case that is a different decision, with a different assessment, and a much larger operational bill.
How can I help you explore Laravel packages today?