Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

File Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it as a normal Composer dependency:

composer require amphp/file

Requirements: PHP 8.1+. It works without extensions, but ext-eio, ext-uv, or ext-parallel will be used automatically when available for better performance.

Look first at the procedural Amp\File functions, because they cover most Laravel-side use cases:

use Amp\File;

$contents = File\read(storage_path('app/imports/report.csv'));

File\write(storage_path('app/imports/report.copy.csv'), $contents);

if (File\exists(storage_path('app/imports/report.csv'))) {
    $size = File\getSize(storage_path('app/imports/report.csv'));
}

The first realistic use case is a long-running Artisan command or queued worker that needs to inspect or ingest many files without blocking the process on every read. For multiple files, create concurrent operations with Amp\async() and await them together:

use Amp\File;
use function Amp\async;
use function Amp\Future\await;

$paths = glob(storage_path('app/incoming/*.csv')) ?: [];

foreach (array_chunk($paths, 8) as $batch) {
    $futures = [];

    foreach ($batch as $path) {
        $futures[$path] = async(fn () => File\read($path));
    }

    foreach (await($futures) as $path => $contents) {
        // Parse and persist each file.
    }
}

Use this package for CLI workers, Amp-based services, and carefully validated Octane scenarios. For ordinary Laravel FPM controllers, Laravel’s Storage facade is still the simpler and often more appropriate choice.

Implementation Patterns

Concurrent batch reads and imports. Wrap independent file operations in Amp\async() and gather them with Amp\Future\await(). Chunk the input to bound process/worker pressure instead of launching one operation per file in a large directory.

$futures = [];

foreach (array_chunk($paths, 16) as $chunk) {
    $chunkFutures = [];
    foreach ($chunk as $path) {
        $chunkFutures[$path] = async(fn () => File\read($path));
    }

    foreach (await($chunkFutures) as $path => $contents) {
        // Process.
    }
}

Streaming instead of loading whole files. Use openFile() plus Amp\ByteStream\pipe() for large exports, imports, or transformations. This is the pattern to reach for when File\read() would exhaust memory.

use Amp\ByteStream;
use Amp\File;

$source = File\openFile($inputPath, 'rb');
$destination = File\openFile($outputPath, 'wb');

try {
    ByteStream\pipe($source, $destination);
} finally {
    $source->close();
    $destination->close();
}

File handles for incremental work. File supports read(), write(), seek(), tell(), eof(), truncate(), getPath(), and getMode(). Use Amp\File\Whence with seek() in v3+ rather than the removed File::SEEK_* constants.

$file = File\openFile($path, 'rb');

try {
    while (($chunk = $file->read()) !== null) {
        // Process chunk.
    }
} finally {
    $file->close();
}

Metadata-driven workflows. Check exists(), getSize(), getModificationTime(), and isFile() before reading or reprocessing. This is useful for “process new files only” importers, cache invalidation, upload validation, and export freshness checks.

if (!File\isFile($path)) {
    return;
}

$mtime = File\getModificationTime($path);

if ($mtime <= $lastProcessedAt) {
    return;
}

File organization and atomic-ish moves. Use move(), deleteFile(), the recursive directory helper, and listFiles() to implement inbox/processing/done folders. Join listFiles() results with the directory path, because the returned names are not guaranteed to be full paths.

$incoming = storage_path('app/incoming');
$processed = storage_path('app/processed');

foreach (File\listFiles($incoming) as $name) {
    $source = $incoming . DIRECTORY_SEPARATOR . $name;
    $target = $processed . DIRECTORY_SEPARATOR . $name;

    File\move($source, $target);
}

Locking and coordination. v4 adds File::lock(), File::tryLock(), and File::unlock() for non-blocking shared/exclusive locking. For 3.x, use Amp\File\FileMutex or KeyedFileMutex from amphp/sync-style patterns to serialize work across workers. Use locks for export generation, log rotation, or “one importer at a time” guards.

Laravel integration wrapper. Keep Amp\File calls behind a small service so path normalization and runtime assumptions are centralized. Relative paths are resolved from the process working directory, so always convert to absolute paths first.

final class AsyncFileService
{
    public function read(string $path): string
    {
        return \Amp\File\read($this->absolute($path));
    }

    private function absolute(string $path): string
    {
        return str_starts_with($path, DIRECTORY_SEPARATOR)
            ? $path
            : base_path($path);
    }
}

Driver-level extension points. The package exposes a Filesystem abstraction with alternate drivers: BlockingFilesystemDriver, ParallelFilesystemDriver, EioFilesystemDriver, UvFilesystemDriver, and StatusCachingFilesystemDriver. FileCache and the mutex classes are useful if you need simple file-backed caching or cross-process coordination. For tests, prefer the blocking driver or a temporary directory so you do not depend on worker processes.

Gotchas and Tips

This is not a drop-in replacement for Laravel’s Storage facade. A single File\read() is still a file read. The gain comes from not blocking an event-loop worker and from running many operations concurrently. In a normal FPM request, calling this sequentially usually adds complexity without a meaningful benefit.

Runtime context matters. amphp/file is built on Amp and Revolt. It works well in CLI commands and workers where Amp can drive the event loop. In Octane, be careful: Swoole or RoadRunner own the runtime, and mixing Revolt loops inside a request can deadlock or behave unexpectedly. Validate the specific Octane server and version before putting Amp\File on a request hot path.

Use absolute paths. Functions like File\read('storage/app/file.txt') resolve relative to the process working directory, which may not be the Laravel project root in queue workers or Octane. Prefer storage_path(), base_path(), or base_path()-derived absolute paths.

Do not expect boolean returns from most operations. getStatus() and getLinkStatus() return null for missing paths, but most other failures throw exceptions. Wrap risky operations in try/catch and handle FilesystemException or the package’s driver-specific exceptions.

File::write() returns void in v3/v4. Do not rely on a byte count. Release notes explicitly moved away from returning the written length. Check the resulting file with getSize() if you need to verify output.

The default parallel driver has process overhead. It uses multiple worker processes by default. That is powerful for batch work, but the first use has startup cost, and long-lived workers keep the pool alive. In v4, worker sizing is controlled through LimitedWorkerPool; in v3, the constructor accepted a worker limit but that is deprecated. If you need deterministic behavior in tests or small CLI scripts, the blocking driver is simpler.

Driver differences have caused bugs in truncation, append modes, touch(), and seek-then-write sequences. Keep the package on the latest patch release. The changelog shows multiple fixes specifically for ext-eio, ext-uv, and append/truncate behavior.

Symlink metadata is split. getStatus() follows symlinks and describes the target. Use getLinkStatus() when you need metadata about the link itself.

File locks are not magic across network filesystems. v4’s lock()/tryLock() is convenient, but advisory locks can be unreliable on NFS or clustered storage. For local disk coordination it is fine. For multi-host workers, prefer a database or Redis lock.

Custom implementations break on v4. The File interface now includes lock(), tryLock(), and unlock(). If you wrote a custom File implementation, you must update it. If you constructed ParallelFilesystemDriver with a custom worker count, update to LimitedWorkerPool.

Caching drivers can serve stale metadata. StatusCachingFilesystemDriver and FileCache are useful for read-heavy workloads, but invalidate or bypass them after writes, moves, and deletes. Do not use cached exists() or getSize() checks as the final authority for uploads or destructive operations.

Watch listFiles() semantics. It returns entry names, not necessarily absolute paths, and directory entries may include dot entries depending on driver behavior. Filter names explicitly and join with the directory path.

Close handles deterministically. Use try/finally around openFile() calls. File::onClose() is available for cleanup hooks, but explicit close() is easier to reason about in queue workers.

Cancellation and timeouts. File::read() accepts a Cancellation as its first parameter in v3+. If you are doing batch reads inside a request or job with a deadline, pass a cancellation or wrap the operation with Amp’s timeout utilities instead of waiting indefinitely.

Testing tip. Swap to BlockingFilesystemDriver for predictable unit tests, use temporary directories, and clean up lock files. Do not let tests spawn parallel workers unless you are explicitly integration-testing the parallel driver.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin