amphp/file
amphp/file provides non-blocking file access for PHP 8.1+ applications built with fibers and concurrency. Read, write, and stream files without blocking, using process, thread, or extension-based drivers for responsive I/O.
Architecture fit. amphp/file is a fiber-based non-blocking filesystem layer built on Amp v3 and Revolt. Laravel's default request lifecycle is synchronous (php-fpm/mod_php), which means the package's core value—keeping the event loop responsive during I/O—provides little benefit in the standard Laravel deployment. Value only materializes in long-lived process models: Laravel Octane (Swoole/RoadRunner), queue workers doing parallel file I/O, or Octane-hosted streaming/export endpoints. In those contexts, the package's File handles, seek()/tell(), and the newly added lock()/tryLock()/unlock() (v4.0.0) offer genuinely useful primitives that plain file_get_contents() cannot. However, the package does not integrate with Laravel's Filesystem/Flysystem abstraction, so the "fit" is really "an escape hatch for hot paths," not a replacement for Storage:: or File::.
Integration feasibility. Composer-installable, MIT-licensed, PHP 8.1+ — all compatible with modern Laravel (10/11/12). No hard extension requirements; falls back to a blocking driver if eio/uv/parallel are absent, but that fallback defeats the purpose. Real integration requires: (1) a service provider to bootstrap a FilesystemDriver singleton, (2) adapters bridging Amp\File\File to Laravel's Illuminate\Filesystem contracts where callers expect them, and (3) careful coordination with whichever event loop the runtime uses. Octane's Swoole/RoadRunner loops are not Revolt, so there is a genuine question of whether Amp's event loop can be safely entered inside an Octane worker request. That integration seam is the primary feasibility risk.
Technical risk. Medium-to-high for the intended deployment targets:
ParallelFilesystemDriver creates its own LimitedWorkerPool; in long-lived Octane workers this pool must be managed per-worker and torn down on reload, or you leak child processes.request()/container state; anything that touches the fiber after the request ends can access stale container state—a well-known Octane pitfall that this package makes more likely.fopen.Key questions.
Amp\File\File (which implements ReadableStream/WritableStream) and Illuminate\Filesystem / League\Flysystem\FilesystemOperator? If we shim incorrectly, we break the Storage facade contract.Stack fit. Poor as a broad replacement; viable as a targeted utility. In an Octane-hosted Laravel app, amphp/file can slot into: (a) streaming downloads/exports where a File handle is piped into Response::stream() or StreamedResponse, (b) queue jobs that currently file_get_contents many small files serially (S3 mirrors, log aggregation, media thumbnailing), and (c) a KeyedFileMutex wrapper around concurrent writers where flock() would otherwise block. It does not fit as the backend for Storage, File, or Flysystem — those contracts assume synchronous semantics and would need a parallel adapter layer that increases risk without replacing the existing API for callers.
Migration path. Do not migrate; introduce incrementally.
AsyncFilesystem) so application code depends on us, not Amp\File directly.Illuminate\Support\Facades\Storage as the default; add the async path only where the profiling data shows blocking I/O is a bottleneck.Compatibility. Composer constraints are clean (php >=8.1, revolt/event-loop, amphp/amp ^3, amphp/byte-stream ^2). The friction points are runtime, not package version: PHP 8.1 is now below Laravel 12's effective floor, so pin to the latest 4.x; and ext-eio/ext-uv/ext-parallel availability varies across hosting (Forge, Vapor, container images) — Vapor is essentially a non-starter since Lambda freezes after the response and child worker pools cannot survive. Forge/Docker is fine if the extensions are baked in.
Sequencing.
StreamedResponse) under Octane. Measure p95 latency and memory under load.AsyncFilesystem adapter and the Storage-to-File bridge.Maintenance. Low-to-moderate on the package itself — AMPHP's release cadence is disciplined, semver is respected (v3→v4 had a narrow breaking surface: custom File implementations and ParallelFilesystemDriver construction), and the maintainers are responsive. The maintenance burden lands on our adapter code and on keeping the driver pool lifecycle correct across Octane reloads. Realistically, treat the integration layer as owned code that requires quarterly review against AMPHP and Octane releases.
Support. The package has no commercial backing; support is the GitHub issues queue and the AMPHP Discord. Documentation is thin on operational topics: no guides on pool sizing, no Octane integration notes, no failure-mode documentation. For a TPM, that means we carry first-line support for our own engineers. The v4 release notes are informative but not operationally prescriptive.
Scaling. Scaling behavior depends entirely on the driver. The default parallel driver spawns child PHP processes; under high concurrency this can multiply process counts per Octane worker, and the LimitedWorkerPool sizing becomes a tuning parameter we must own. eio and uv avoid the process overhead but require PECL builds in every environment. The blocking fallback scales exactly like today (i.e., it does not solve the problem). Horizontal scaling of Octane workers plus vertical sizing of the file worker pool is the practical ceiling.
Failure modes. The realistic set: (1) worker pool exhaustion leading to queued or timed-out file operations; (2) leaked file descriptors from unclosed File handles — the README's close() advice is easy to miss under exceptions, and PHP's GC will not reliably clean up fibers holding open handles; (3) event-loop nesting failures under Octane causing silent blocking or deadlocks; (4) stale container/fiber state across requests, manifesting as authentication or tenant leakage — the highest-severity failure mode; (5) extension-related bugs, mitigated by preferring eio/uv and pinning versions. Every one of these should have an observability hook before the first line of integration code is written.
Ramp-up. The API is small and intuitive (read(), write(), openFile(), File methods mirror the standard library), so a mid-level Laravel engineer can use it in an afternoon. The hard part is not the API — it's operational literacy: understanding Revolt, fiber semantics, where await boundaries matter, how the multi-process driver interacts with Octane's worker model, and why close() on every path is mandatory. Budget one focused engineer-week for a senior dev to produce an internal standard and a working reference implementation; budget ongoing time for that engineer to remain the owner.
Bottom line. A well-built, narrowly useful package that is the wrong abstraction for most Laravel applications and a credible accelerator for a small subset. Recommend a time-boxed pilot against a single Octane-hosted streaming workload, gated on the event-loop nesting question and on profiling data that proves blocking file I/O is actually a bottleneck. Do not adopt as a Storage replacement.
How can I help you explore Laravel packages today?