puff/file

Lightweight native PHP filesystem component

Maintainers

Package info

github.com/php-puff/file

pkg:composer/puff/file

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-19 22:46 UTC

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() and deleteDirectory() unlink the link itself without modifying its target.
  • cleanDirectory() unlinks child links and refuses a symlink as its root argument.
  • copy() and copyDirectory() 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 false when information is unavailable.
  • get(), writes, copies, moves, directory mutations, deletion, touch(), and PHP-file loading throw on failure.
  • Mutation methods returning bool return true when 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.