Search by

phatnt93 / php-file-helper

phatnt93

A high-performance, expressive, and secure PHP file and directory manipulation library supporting create, delete, rename, copy, move, atomic writes, streaming, path traversal protection, finder, zip archiving, and remote FTP management.

v1.0.0 2026-10-16 01:07 UTC

This package is auto-updated.

Last update: 2026-10-16 01:27:06 UTC


README

English | Tiếng Việt

Tests Latest Version PHP Version Total Downloads License

A secure, dependency-free PHP 8.1+ library for working with files and directories: atomic writes, streaming reads, path-traversal protection, ZIP/TAR with Zip Slip protection, checksums, filesystem transactions with rollback, a fluent Finder, FTP/SFTP clients and 48 short global f_* helpers.

  • Every failure throws a typed exception — no silent false returns.
  • Writes are atomic by default and create missing parent directories.
  • Works the same on Windows, Linux and macOS.
  • Statically analysed with PHPStan level 8, tested on PHP 8.1–8.4 with ≥ 90 % line coverage.

Installation

composer require phatnt93/php-file-helper

Requirements: PHP >= 8.1 with ext-dom, ext-fileinfo, ext-json and ext-libxml (all enabled in standard PHP builds).

Optional:

  • ext-zip for ZIP archives
  • ext-phar (and ext-zlib for .tar.gz) for TAR archives
  • ext-ftp for FTP/FTPS
  • phpseclib/phpseclib ^3 (recommended) or ext-ssh2 for SFTP

Quick start

use PhpFileHelper\File;

File::write('/var/app/storage/report.txt', 'Hello'); // atomic, creates /var/app/storage if needed
echo File::read('/var/app/storage/report.txt');

// Or with the global helpers
f_write(f_path($dir, 'notes.txt'), 'Hello');
echo f_read(f_path($dir, 'notes.txt'));

Runnable scripts live in examples/:

Script Shows
01_quick_start.php f_* helpers, atomic writes, JSON, .env, head/tail, replace
02_folders_and_finder.php scoped temp workspaces, mirror with ignore patterns, Finder
03_advanced_features.php transactions, locks, chunk stitching, stream copy progress, snapshots, shredding
04_archives_and_security.php ZIP/TAR, traversal guard, filename and SVG sanitising, checksum manifests

How to write files atomically?

File::write() writes to a temporary file in the same directory and renames it over the target, so readers never see a half-written file. Parent directories are created automatically.

use PhpFileHelper\File;

File::write('/path/to/file.txt', 'Hello World!');               // atomic + LOCK_EX (default)
File::write('/path/to/file.txt', 'fast', atomic: false);        // plain file_put_contents
File::append('/path/to/file.txt', "\nNew line");
File::prepend('/path/to/file.txt', "Header line\n");

File::writeJson('/path/to/data.json', ['user' => 'John', 'age' => 30]);
$data = File::readJson('/path/to/data.json');

File::replace('/path/to/app.env', 'APP_ENV=local', 'APP_ENV=production');
File::replace('/path/to/template.html', ['{{title}}' => 'Home', '{{year}}' => '2026']);
File::replaceRegex('/path/to/config.php', '/debug\s*=\s*true/', 'debug = false');

How to read large files without running out of memory?

// Generator: one line at a time
foreach (File::readLines('/var/log/app.log', trim: true, skipEmpty: true) as $line) {
    // ...
}

$first10 = File::head('/var/log/app.log', lines: 10);
$last20  = File::tail('/var/log/app.log', lines: 20);   // seeks backwards from the end

File::writeCsv('/path/to/users.csv', [['Name', 'Role'], ['Alice', 'Admin']]);
foreach (File::readCsv('/path/to/users.csv') as $row) {
    // ['Alice', 'Admin']
}

// Copy a large file with progress: fn(int $copiedBytes, int $totalBytes, float $percentage)
File::streamCopy('/source/backup.dump', '/dest/backup.dump', function (int $copied, int $total, float $pct) {
    printf("\r%.1f%%", $pct);
});

How to prevent path traversal?

use PhpFileHelper\Path;
use PhpFileHelper\Exceptions\PathTraversalException;

try {
    $safePath = Path::assertWithinPath($_GET['file'], '/var/www/uploads');
    // $safePath is normalised and guaranteed to be inside /var/www/uploads
} catch (PathTraversalException $e) {
    // "../../etc/passwd" and similar attempts end up here
}

Path::normalize('C:\app/storage//logs/../cache/app.log');     // "C:/app/storage/cache/app.log"
Path::join('/base', 'uploads', 'avatars', 'user.png');       // "/base/uploads/avatars/user.png"
Path::sanitizeFilename('user:avatar/test*?.png');            // "user_avatar_test_.png"

Paths containing null bytes throw InvalidPathException.

How to zip or tar a folder?

use PhpFileHelper\Tar;
use PhpFileHelper\Zip;

Zip::archive('/var/app/source', '/backup/source.zip');
Zip::extract('/backup/source.zip', '/var/app/restored');

Tar::archive('/var/app/source', '/backup/source.tar.gz', gzip: true);
Tar::extract('/backup/source.tar.gz', '/var/app/restored', entries: ['source/config.php']);

Before anything is extracted, every entry is checked: absolute paths, drive letters (C:/…), .. segments and null bytes are rejected with PathTraversalException (Zip Slip / Tar Slip).

How to copy, mirror or clean up directories?

use PhpFileHelper\Folder;

Folder::create('/path/to/nested/directory');
Folder::copy('/src', '/dist', ignore: ['.git', 'node_modules', '*.log']); // ignoring a directory skips its contents
Folder::move('/old', '/new');
Folder::delete('/path/to/dir');                      // recursive by default

// One-way sync: copies changed files, optionally deletes files missing from the source
Folder::mirror('/var/source', '/var/backup', [
    'delete' => true,
    'ignore' => ['.git', 'node_modules', '*.tmp'],
]);

// Temporary workspace that is always deleted, even when the callback throws
$result = Folder::scopedTemp(function (string $tempDir) {
    File::write($tempDir . '/temp.txt', 'processing...');
    return 'Done';
});

Folder::purge('/var/log/app', olderThan: '30 days');  // retention policy
Folder::hasEnoughSpace('/var/www', requiredBytes: 500 * 1024 * 1024);

How to find files by name, size or date?

use PhpFileHelper\Search\Finder;

$logs = Finder::create()
    ->in('/var/log')
    ->files()
    ->name('*.log')            // glob, or a regex such as '/^app-\d+\.log$/'
    ->notName('*error*')
    ->size('> 10MB')
    ->size('<= 100MB')
    ->modified('< 7 days ago')
    ->depth('<= 2')
    ->get();                   // FileInfo[] (SplFileInfo with helpers such as read(), getFormattedSize())

How to roll back several file changes when something fails?

use PhpFileHelper\Transaction\FilesystemTransaction;

FilesystemTransaction::run(function (FilesystemTransaction $tx) {
    $tx->createDir('/var/app/v2');
    $tx->copy('/var/app/v1/config.json', '/var/app/v2/config.json');
    $tx->write('/var/app/v2/.env', 'APP_ENV=production');
    $tx->delete('/var/app/v1/old_cache.txt');
    // Any exception thrown here restores every file and directory touched above.
});

How to lock a file between processes?

$next = File::withLock('/var/run/counter.lock', function () {
    $count = (int) File::read('/var/run/counter.txt') + 1;
    File::write('/var/run/counter.txt', (string) $count);
    return $count;
}, timeoutSeconds: 5);   // PermissionDeniedException after 5 s

File::withSharedLock('/var/run/config.lock', fn() => File::readJson('/var/run/config.json'));

How to verify file integrity?

use PhpFileHelper\Folder;
use PhpFileHelper\Metadata;

Metadata::hash('/path/to/video.mp4', algo: 'sha256');     // streamed
Metadata::isSame('/a.bin', '/b.bin');                     // size check, then chunked comparison

Metadata::generateChecksumFile('/var/app/release', 'SHA256SUMS');
$report = Metadata::verifyChecksumFile('/var/app/release/SHA256SUMS');
// ['matched' => [...], 'failed' => [...], 'missing' => [...]]

$snapshot = Folder::snapshot('/var/app/config');
// ... later
$diff = Folder::detectDiff('/var/app/config', $snapshot); // ['added' => [], 'modified' => [], 'deleted' => []]

How to securely delete sensitive files?

File::shred('/path/to/private.key', passes: 3);           // overwrite with random bytes, then delete
Folder::shred('/path/to/sensitive_workspace', passes: 3);

How to edit .env, INI and other config files?

File::setEnvValue('/var/app/.env', 'APP_ENV', 'production'); // keeps comments and other keys
File::getEnvValue('/var/app/.env', 'APP_ENV');
File::readEnv('/var/app/.env');                              // ['APP_ENV' => 'production', ...]

File::writeIni('/etc/app.ini', ['database' => ['host' => 'localhost', 'port' => 3306]]);
File::readIni('/etc/app.ini');

File::imageDimensions('/path/to/photo.png');                 // ['width' => 800, 'height' => 600, 'mime' => 'image/png']
File::sanitizeSvg('/path/to/upload.svg');                    // DOM allow-list: drops scripts, handlers, external links

How to upload and download over FTP or SFTP?

use PhpFileHelper\Ftp;
use PhpFileHelper\Sftp;

$ftp = Ftp::connect([
    'host'     => 'ftp.example.com',
    'username' => 'deployer',
    'password' => 'secret',
    'ssl'      => true,   // FTPS only; never downgrades to plain FTP
    'passive'  => true,
]);
$ftp->uploadDirectory('/var/app/dist', '/public_html/app', ignore: ['.git', '*.tmp']);
$ftp->downloadFile('/public_html/backup.zip', '/var/app/backup.zip');
$items = $ftp->scanDir('/public_html');   // FtpFileItem[]
$ftp->close();

$sftp = Sftp::connect([
    'host'       => 'ssh.example.com',
    'username'   => 'ubuntu',
    'privateKey' => '/home/user/.ssh/id_rsa',   // or 'password' => 'secret'
]);
$sftp->uploadFile('/local/build.tar.gz', '/remote/releases/build.tar.gz'); // remote folders are created
$sftp->close();

FtpClient and SftpClient both implement PhpFileHelper\Contracts\RemoteFilesystemInterface.

Error handling

Every exception extends PhpFileHelper\Exceptions\FileSystemException (a \RuntimeException with getPath()), so one catch covers the whole library:

FileSystemException
├── FileNotFoundException          ├── PermissionDeniedException   (also lock timeouts)
├── FileAlreadyExistsException     ├── ReadFailedException
├── DirectoryNotFoundException     ├── WriteFailedException
├── DirectoryAlreadyExistsException├── DiskFullException
├── DirectoryNotEmptyException     ├── InvalidPathException
├── PathTraversalException         ├── FtpException  → FtpConnectionException, FtpTransferException
                                   └── SftpException → SftpConnectionException
use PhpFileHelper\Exceptions\FileNotFoundException;
use PhpFileHelper\Exceptions\FileSystemException;

try {
    $config = File::readJson('/etc/app/config.json');
} catch (FileNotFoundException $e) {
    $config = [];
} catch (FileSystemException $e) {
    error_log(sprintf('%s (%s)', $e->getMessage(), $e->getPath()));
    throw $e;
}

Invalid arguments (unknown hash algorithm, zero buffer size, invalid date expression) throw \InvalidArgumentException; JSON helpers throw \JsonException.

Comparison

Capability PHP native symfony/filesystem league/flysystem php-file-helper
Atomic write (temp file + rename) manual ✅ dumpFile() ❌ local adapter writes in place ✅ default
Path traversal guard manual ⚠️ Path::isBasePath() helper ✅ inside the configured root ✅ Path::assertWithinPath()
Line-by-line / CSV streaming SplFileObject ❌ ⚠️ raw streams ✅ generators
ZIP / TAR with slip protection ❌ ❌ ⚠️ ZIP via extra adapter ✅
FTP / SFTP ext functions ❌ ✅ via adapters ✅
Cloud storage (S3, GCS, …) ❌ ❌ ✅ ❌
Directory mirror manual ✅ mirror() ❌ ✅ with ignore + delete
Transactions with rollback ❌ ❌ ❌ ✅
Checksum manifests / snapshots manual ❌ ⚠️ checksum() per file ✅
.env / INI / CSV helpers partial ❌ ❌ ✅
Runtime Composer dependencies — polyfills league/mime-type-detection, … none

Pick league/flysystem when you need one API over many storage backends (S3, GCS, Azure…). Pick this library for local-disk work where safety (atomic writes, traversal and slip protection, rollback) and a short API matter.

Global helpers (f_*)

Autoloaded through Composer. Each helper forwards to the facade method shown.

Helper Maps to
f_path(...$parts) Path::join
f_normalize($path) Path::normalize
f_read($path) File::read
f_read_lines($path, $trim, $skipEmpty) File::readLines
f_read_json($path) File::readJson
f_read_csv($path) File::readCsv
f_write($path, $content) File::write
f_write_json($path, $data) File::writeJson
f_write_csv($path, $rows) File::writeCsv
f_append($path, $content) File::append
f_prepend($path, $content) File::prepend
f_replace($path, $search, $replace) File::replace
f_head($path, $lines) File::head
f_tail($path, $lines) File::tail
f_copy($source, $target) File::copy
f_move($source, $target) File::move
f_delete($paths) File::delete
f_exists($path) File::exists
f_is_file($path) File::isFile
f_is_dir($path) Folder::isDir
f_temp($prefix, $dir) File::temp
f_env($path, $key) File::getEnvValue
f_env_set($path, $key, $value) File::setEnvValue
f_size($path) Metadata::size
f_size_format($pathOrBytes) Metadata::formattedSize
f_mime($path) Metadata::mimeType
f_hash($path, $algo) Metadata::hash
f_is_same($fileA, $fileB) Metadata::isSame
f_dir_create($path) Folder::create
f_dir_delete($path) Folder::delete
f_dir_empty($path) Folder::empty
f_dir_copy($source, $target) Folder::copy
f_dir_move($source, $target) Folder::move
f_dir_list($path) Folder::list
f_dir_size($path) Folder::size
f_scoped_temp($callback) Folder::scopedTemp
f_mirror($source, $target, $options) Folder::mirror
f_purge($path, $olderThan) Folder::purge
f_with_lock($path, $callback) File::withLock
f_shred($path) File::shred / Folder::shred
f_transaction($callback) FilesystemTransaction::run
f_finder() Finder::create
f_zip($sources, $destination) Zip::archive
f_unzip($zip, $destination) Zip::extract
f_tar($sources, $destination) Tar::archive
f_untar($tar, $destination) Tar::extract
f_ftp($config) Ftp::connect
f_sftp($config) Sftp::connect

Using with AI coding agents

  • llms.txt — short overview for LLMs (the llms.txt convention).
  • llms-full.txt — full API reference: every method signature, all f_* helpers, callback signatures and the exception tree.
  • AGENTS.md — commands and conventions for agents contributing to this repository.

Point your assistant at llms-full.txt (for example "use vendor/phatnt93/php-file-helper/llms-full.txt as the API reference") to get accurate method names and parameters.

Contributing & security

  • CONTRIBUTING.md — setup, test/analyse/style commands, pull request checklist.
  • SECURITY.md — supported versions and how to report a vulnerability privately.
  • CHANGELOG.md — release notes.

License

MIT License. Copyright (c) 2026 Phat Nguyen. See LICENSE.