File
The File component provides typed file handles for reading and writing files. It builds on the IO component's handle interfaces, adding file-specific concerns like write modes and advisory locking.
Quick Read and Write
For common cases, use the convenience functions:
use Psl\File;
use Psl\Filesystem;
use Psl\IO;
$tmp = Filesystem\create_temporary_file(prefix: 'psl_file_');
// Write to a file (creates it if it does not exist)
File\write($tmp, '{"key": "value"}');
// Read an entire file
$content = File\read($tmp);
IO\write_line('Full content: %s', $content);
// Read with offset and length
$header = File\read($tmp, offset: 0, length: 6);
IO\write_line('Header: %s', $header);
// Overwrite with truncation
File\write($tmp, 'New content', File\WriteMode::Truncate);
IO\write_line('After truncate: %s', File\read($tmp));
Filesystem\delete_file($tmp);
Both File\read() and File\write() automatically acquire a lock (shared for reads, exclusive for writes) and release it when done.
Write Modes
When opening a file for writing, you choose a WriteMode that controls how the file pointer is positioned and whether the file must already exist:
| Mode | Behavior |
|---|---|
OpenOrCreate |
Opens or creates the file. Pointer at the beginning. Existing content is preserved. (default) |
Truncate |
Opens or creates the file and truncates it to zero length. |
Append |
Opens or creates the file. All writes go to the end regardless of seek position. |
MustCreate |
Creates a new file. Throws if the file already exists. |
use Psl\File;
use Psl\Filesystem;
use Psl\IO;
$logFile = Filesystem\create_temporary_file(prefix: 'psl_log_');
$lockFile = sys_get_temp_dir() . '/psl-session-' . uniqid() . '.lock';
// Append a log line
File\write($logFile, "event occurred\n", File\WriteMode::Append);
File\write($logFile, "another event\n", File\WriteMode::Append);
IO\write_line('Log contents: %s', File\read($logFile));
// Ensure we are creating a fresh file
$id = 'session-abc123';
File\write($lockFile, $id, File\WriteMode::MustCreate);
IO\write_line('Lock file contents: %s', File\read($lockFile));
Filesystem\delete_file($logFile);
Filesystem\delete_file($lockFile);
File Handles
For more control, open a file handle directly. Handles implement the IO handle interfaces and add getPath(), getSize(), and locking.
use Psl\File;
use Psl\Filesystem;
use Psl\IO;
use Psl\Str;
$csvFile = Filesystem\create_temporary_file(prefix: 'psl_csv_');
$outputFile = Filesystem\create_temporary_file(prefix: 'psl_out_');
$datFile = Filesystem\create_temporary_file(prefix: 'psl_dat_');
// Prepare CSV data
File\write($csvFile, "name,age\nAlice,30\nBob,25\n");
// Read-only
$handle = File\open_read_only($csvFile);
$all = $handle->readAll();
$handle->close();
IO\write_line('CSV data: %s', Str\trim_right($all));
// Write-only with truncation
$handle = File\open_write_only($outputFile, File\WriteMode::Truncate);
$handle->writeAll("line 1\nline 2\n");
$handle->close();
IO\write_line('Output: %s', Str\trim_right(File\read($outputFile)));
// Read-write
File\write($datFile, 'hello world');
$handle = File\open_read_write($datFile);
$existing = $handle->readAll();
$handle->seek(0);
$handle->writeAll(Str\uppercase($existing));
$handle->close();
IO\write_line('Uppercased: %s', File\read($datFile));
Filesystem\delete_file($csvFile);
Filesystem\delete_file($outputFile);
Filesystem\delete_file($datFile);
File Locking
File handles support advisory locking via lock() and tryLock(). The returned Lock object is released by calling release() or when it is garbage collected.
LockType::Shared-- Multiple processes can hold a shared lock simultaneously. Use for reads.LockType::Exclusive-- Only one process can hold an exclusive lock. Use for writes.
use Psl\File;
use Psl\Filesystem;
use Psl\IO;
$counterFile = Filesystem\create_temporary_file(prefix: 'psl_counter_');
File\write($counterFile, '0');
$handle = File\open_read_write($counterFile);
$lock = $handle->lock(File\LockType::Exclusive);
$count = (int) $handle->readAll();
$handle->seek(0);
$handle->writeAll((string) ($count + 1));
$lock->release();
$handle->close();
IO\write_line('Counter incremented to: %s', File\read($counterFile));
Filesystem\delete_file($counterFile);
tryLock() acquires the lock immediately or throws AlreadyLockedException:
use Psl\File;
use Psl\Filesystem;
use Psl\IO;
$tmp = Filesystem\create_temporary_file(prefix: 'psl_lock_');
$handle = File\open_read_write($tmp);
try {
$lock = $handle->tryLock(File\LockType::Exclusive);
IO\write_line('Lock acquired successfully');
$lock->release();
} catch (File\Exception\AlreadyLockedException) {
IO\write_line('Another process holds the lock');
}
$handle->close();
Filesystem\delete_file($tmp);
Blocking Behavior in Async Code
All file operations are blocking: they block the current fiber until the operation completes. This is inherent to filesystem I/O; operating systems do not provide truly non-blocking file I/O the way they do for sockets.
In concurrent applications, this means a file read or write in one fiber will prevent other fibers from running until it finishes. For most workloads this is fine since file I/O is fast, but if you are doing heavy file I/O alongside latency-sensitive network operations, consider offloading file work to a separate process.
The amphp/file package does exactly this: it spawns a worker process and delegates all file operations to it, communicating over IPC. Because amphp uses the same RevoltPHP event loop as PSL's async component, it integrates seamlessly:
use Amp\File;
use Psl\Async;
Async\main(static function(): int {
// Non-blocking file read; other fibers can run while this waits
$content = File\read('/path/to/large-file.dat');
return 0;
});
Integration with IO
Because file handles implement the IO interfaces, they work with any code that accepts IO\ReadHandleInterface or IO\WriteHandleInterface:
use Psl\File;
use Psl\Filesystem;
use Psl\IO;
$sourceFile = Filesystem\create_temporary_file(prefix: 'psl_src_');
$destFile = Filesystem\create_temporary_file(prefix: 'psl_dst_');
File\write($sourceFile, 'binary data to copy');
$source = File\open_read_only($sourceFile);
$dest = File\open_write_only($destFile, File\WriteMode::Truncate);
IO\copy($source, $dest);
$source->close();
$dest->close();
IO\write_line('Copied: %s', File\read($destFile));
Filesystem\delete_file($sourceFile);
Filesystem\delete_file($destFile);
See src/Psl/File/ for the full API.