amphp/file
Non-blocking file I/O for PHP 8.1+ in the AMPHP ecosystem. Read/write files or stream via async file handles while keeping apps responsive. Uses multi-process by default, with optional eio/uv/parallel drivers when available.
Installation:
composer require amphp/file
Ensure PHP 8.1+ is used.
First Use Case: Read a file asynchronously:
use Amp\File;
$contents = File\read('/path/to/file.txt');
echo $contents;
Driver Selection:
ParallelFilesystemDriver (multi-process) if no extensions are installed.BlockingFilesystemDriver for single-process blocking I/O (no extensions required).ext-eio, ext-uv, or ext-parallel (e.g., pecl install eio).Where to Look First:
Amp\File namespace for core functions (read, write, openFile, etc.).Amp\File\File class for stream operations (e.g., seek, tell).Read/Write Entire Files:
$contents = Amp\File\read('/path/to/file.txt');
Amp\File\write('/path/to/file.txt', $newContents);
File\exists() to check existence before operations.Streaming with File Handles:
$file = Amp\File\openFile('/path/to/large-file.log', 'r');
Amp\ByteStream\pipe($file, getStdout()); // Stream to stdout
$file->close();
if (Amp\File\isFile('/path/to/file.txt') && Amp\File\isWritable('/path/to/file.txt')) {
Amp\File\changePermissions('/path/to/file.txt', 0644);
}
Amp\File\createDirectoriesRecursively('/path/to/nested/dir');
$file = Amp\File\openFile('/path/to/config.json', 'r+');
$file->lock(); // Exclusive lock
$data = $file->read(1024);
$file->unlock();
if ($file->tryLock()) {
// Critical section
$file->unlock();
}
Filesystem Adapter:
Create a custom Laravel Filesystem adapter wrapping amphp/file:
use Amp\File;
use Illuminate\Contracts\Filesystem\Filesystem;
class AmpFilesystem implements Filesystem {
public function read($path) {
return File\read(storage_path($path));
}
// Implement other methods...
}
Register in config/filesystems.php:
'amp' => [
'driver' => 'amp',
'root' => storage_path('app'),
],
Async Queue Workers:
Use amphp/file in Laravel queues for non-blocking file operations:
public function handle() {
$file = Amp\File\openFile(storage_path('logs/async.log'), 'a');
Amp\ByteStream\pipe($this->data, $file);
}
try-catch for Amp\File\FileException:
try {
$contents = Amp\File\read('/nonexistent.txt');
} catch (Amp\File\FileException $e) {
report($e);
}
Driver Customization:
Amp\File\setFilesystemDriver(new Amp\File\BlockingFilesystemDriver());
LimitedWorkerPool to constrain parallel operations:
use Amp\Worker\LimitedWorkerPool;
$pool = new LimitedWorkerPool(4);
$driver = new Amp\File\ParallelFilesystemDriver($pool);
Amp\File\setFilesystemDriver($driver);
File Caching:
FileCache for repeated metadata access:
$cache = new Amp\File\FileCache();
$size = $cache->getSize('/path/to/file.txt'); // Cached after first call
Symlink Handling:
$realPath = Amp\File\resolveSymlink('/path/to/symlink');
Atomic Operations:
lock() with write() for atomic updates:
$file = Amp\File\openFile('/path/to/atomic.txt', 'r+');
$file->lock();
$file->seek(0);
$file->write('updated');
$file->unlock();
Blocking vs. Non-Blocking:
BlockingFilesystemDriver uses synchronous PHP functions (e.g., fopen). Avoid mixing with fiber-based code unless explicitly needed.ParallelFilesystemDriver or EioFilesystemDriver for async workflows.File Locking Deadlocks:
unlock() can block other processes indefinitely.tryLock() with timeouts or wrap in finally:
$file->lock();
try {
// Critical section
} finally {
$file->unlock();
}
Path Normalization:
amphp/file does not normalize paths (e.g., ../ or ./). Use realpath() or Amp\File\resolveSymlink() for safety.$normalized = realpath('/path/with/../segments');
Driver-Specific Quirks:
ext-uv/ext-eio:
'a'). Use 'r+' and seek(0) for updates.Amp\File\getStatus() to verify behavior.BlockingFilesystemDriver:
Fiber Context:
Amp\run).Amp\run(function () {
$contents = Amp\File\read('/path/to/file.txt');
});
Permission Handling:
changePermissions() may fail silently on some systems (e.g., Windows).try-catch.Large File Streaming:
$file->close()) to avoid resource leaks.onClose() for cleanup:
$file->onClose(function () {
// Cleanup (e.g., log, release resources)
});
Log File Operations:
try {
$contents = Amp\File\read('/path/to/file.txt');
\Log::debug("Read file successfully", ['size' => strlen($contents)]);
} catch (Throwable $e) {
\Log::error("File read failed", ['path' => '/path/to/file.txt', 'error' => $e->getMessage()]);
}
Driver-Specific Logging:
ext-uv/ext-eio:
putenv('UV_LOGGING=1'); // For ext-uv
Check File Handles:
if (!$file->isClosed()) {
$file->close();
}
Race Conditions:
KeyedFileMutex for shared resources:
$mutex = new Amp\File\KeyedFileMutex('/path/to/mutex.lock');
$guard = $mutex->acquire('key');
try {
// Critical section
} finally {
$guard->release();
}
Amp\File\File for domain-specific logic (e.g., logging, encryption):
class LogFile extends Amp\File\File {
public function write(string $data): void {
\Log::info
How can I help you explore Laravel packages today?