puff / file
Lightweight native PHP filesystem component
Requires
- php: ^8.2
- ext-fileinfo: *
- puff/di: dev-main
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-08-19 22:49:17 UTC
README
A lightweight wrapper around PHP's native filesystem functions, with Puff container registration and a global facade.
Requirements
- PHP 8.2 or later
puff/di- The Fileinfo extension when using
mimeType()
Installation
composer require puff/file
Puff discovers Puff\File\ServiceProvider automatically. It registers Puff\File\File as a singleton and exposes the container alias file.
Basic Usage
Inject the service where possible:
use Puff\File\File; final readonly class ReportStore { public function __construct(private File $files) { } public function save(string $path, string $report): void { $this->files->put($path, $report, lock: true); } }
The global File facade provides the same methods:
File::put('/tmp/puff/example.txt', 'Hello Puff', lock: true); $contents = File::get('/tmp/puff/example.txt', lock: true);
Parent directories are created automatically by put(), append(), copy(), move(), and touch().
Reading and Writing Files
$contents = $files->get($path); $locked = $files->get($path, lock: true); $bytes = $files->put($path, 'contents'); $bytes = $files->append($path, 'more'); $bytes = $files->prepend($path, 'first');
get() throws FileException when the file cannot be read. put(), append(), and prepend() return the number of bytes written and throw FileException on failure.
get($path, lock: true) uses a shared lock while reading. Writes with lock: true use an exclusive lock. prepend() performs a separate read followed by a locked write, so it is not an atomic read-modify-write operation when multiple processes update the same file.
File Information
$files->exists($path); // bool $files->isFile($path); // bool $files->isDirectory($path); // bool $files->isWritable($path); // bool $files->size($path); // int|false $files->lastModified($path); // int|false $files->type($path); // string|false $files->mimeType($path); // string|false $files->dirname($path); // string $files->basename($path); // string $files->name($path); // string without the extension $files->extension($path); // string
Copying, Moving, and Deleting
$files->copy($source, $target); $files->move($source, $target); $files->delete($path); $files->delete([$first, $second]); $files->makeDirectory($directory); $files->copyDirectory($sourceDirectory, $targetDirectory); $files->moveDirectory($sourceDirectory, $targetDirectory, overwrite: true); $files->cleanDirectory($directory); $files->deleteDirectory($directory, preserve: true);
These mutation methods return true on success and throw FileException when the operating-system operation fails.
cleanDirectory() removes the contents but preserves the supplied directory. deleteDirectory(..., preserve: true) has the same effect. Without preserve, the directory itself is removed after its contents.
Listing Files and Directories
use SplFileInfo; /** @var list<SplFileInfo> $filesInTree */ $filesInTree = $files->allFiles($directory); /** @var list<SplFileInfo> $includingHiddenNames */ $includingHiddenNames = $files->allFiles($directory, hidden: true); /** @var list<string> $directories */ $directories = $files->directories($directory); /** @var list<string> $matches */ $matches = $files->glob('/tmp/puff/*.log');
files() returns regular files directly inside the supplied directory. allFiles() recursively returns regular files. Both return sorted SplFileInfo lists and skip symbolic links.
With hidden: false, both methods exclude names beginning with .. Recursive traversal also prunes hidden directories. Pass hidden: true to include them.
Use glob() separately for wildcard patterns. Glob results and directory listings are sorted by pathname for deterministic output.
Loading PHP Files
Pass variables to a PHP file before loading it:
// template.php: <?php return "Hello {$name}"; $result = $files->getRequire('/path/to/template.php', ['name' => 'Puff']);
getRequire() uses require; requireOnce() uses require_once. Both return the loaded file's return value. Never pass an untrusted path because loading a PHP file executes code in the current process.
Symbolic-Link Safety
Recursive operations never follow symbolic links:
delete()anddeleteDirectory()unlink the link itself without modifying its target.cleanDirectory()unlinks child links and refuses a symlink as its root argument.copy()andcopyDirectory()reject symbolic links; directory copies are preflighted before the destination is created.- File listings skip symbolic links.
The component still does not establish a filesystem sandbox or prevent path traversal. If a path is derived from user input, resolve it against an application-owned root and verify the resolved path remains inside that root before calling this API.
Error Handling
Operational failures throw Puff\File\FileException:
- Metadata methods generally return
falsewhen information is unavailable. get(), writes, copies, moves, directory mutations, deletion,touch(), and PHP-file loading throw on failure.- Mutation methods returning
boolreturntruewhen they complete. - Native filesystem warnings are suppressed and converted to component exceptions where applicable.
Catch FileException at the application boundary when a filesystem failure is recoverable.
License
Puff File is open-source software licensed under the MIT license.