philiprehberger / safe-file-writer
Atomic file writes with temp-file swap and file locking.
Package info
github.com/philiprehberger/safe-file-writer
pkg:composer/philiprehberger/safe-file-writer
v1.0.1
2026-03-17 20:06 UTC
Requires
- php: ^8.2
Requires (Dev)
- laravel/pint: ^1.0
- phpstan/phpstan: ^1.12|^2.0
- phpunit/phpunit: ^11.0
README
Atomic file writes with temp-file swap and file locking for PHP 8.2+. Framework-agnostic, zero dependencies.
Features
- Atomic writes via temp-file + rename — no partial/corrupt files on crash
- Exclusive locking (
LOCK_EX) on write and append operations - Shared locking (
LOCK_SH) on read operations - Automatic parent directory creation
- Permission preservation when overwriting existing files
- JSON read/write helpers with
json_encode/json_decode - Clear exception types for read and write failures
- PHPStan level 6 clean, PSR-12 code style
Requirements
- PHP ^8.2
- No extensions required
Installation
composer require philiprehberger/safe-file-writer
Usage
Write a file atomically
use PhilipRehberger\SafeFileWriter\SafeFile; // Writes to a temp file first, then renames — atomic on POSIX systems SafeFile::write('/path/to/config.txt', 'key=value'); // Parent directories are created automatically SafeFile::write('/path/to/deep/nested/file.txt', 'content');
Write JSON
SafeFile::writeJson('/path/to/data.json', [ 'name' => 'Philip', 'scores' => [10, 20, 30], ]); // Writes pretty-printed JSON with a trailing newline // Custom JSON flags SafeFile::writeJson('/path/to/compact.json', $data, JSON_UNESCAPED_UNICODE);
Append to a file
// Appends with exclusive lock; creates file if it doesn't exist SafeFile::append('/var/log/app.log', "[2026-03-13] Info: started\n"); SafeFile::append('/var/log/app.log', "[2026-03-13] Info: finished\n");
Read a file
// Reads with a shared lock $content = SafeFile::read('/path/to/config.txt');
Read JSON
$data = SafeFile::readJson('/path/to/data.json'); // Returns decoded array/object
Check existence
if (SafeFile::exists('/path/to/file.txt')) { // ... }
Delete a file
$deleted = SafeFile::delete('/path/to/file.txt'); // Returns true if deleted, false if file didn't exist
API
| Method | Description | Returns |
|---|---|---|
SafeFile::write(string $path, string $content) |
Atomic write via temp-file + rename | void |
SafeFile::writeJson(string $path, mixed $data, int $flags = ...) |
Atomic JSON write | void |
SafeFile::append(string $path, string $content) |
Append with exclusive lock | void |
SafeFile::read(string $path) |
Read with shared lock | string |
SafeFile::readJson(string $path) |
Read and decode JSON | mixed |
SafeFile::exists(string $path) |
Check if file exists | bool |
SafeFile::delete(string $path) |
Delete file if it exists | bool |
Exceptions
| Exception | When thrown |
|---|---|
FileWriteException |
Directory creation, temp-file creation, write, or rename failure |
FileReadException |
File not found, open failure, lock failure, or read failure |
\JsonException |
Invalid JSON on writeJson() or readJson() |
use PhilipRehberger\SafeFileWriter\Exceptions\FileWriteException; use PhilipRehberger\SafeFileWriter\Exceptions\FileReadException; try { SafeFile::read('/nonexistent/file.txt'); } catch (FileReadException $e) { // "File not found: '/nonexistent/file.txt'." }
Running Tests
composer install
composer test
Static analysis:
composer phpstan
Code style check:
composer pint
Run everything at once:
composer check
License
MIT — see LICENSE.