alexskrypnyk / file
Provides file manipulations functionality.
Fund package maintenance!
alexskrypnyk
Patreon
Requires
- php: >=8.2
- symfony/filesystem: ^7.2
Requires (Dev)
README
File manipulations
Table of Contents
Installation
composer require alexskrypnyk/file
Usage
This library provides a comprehensive set of static methods for file and directory operations, including high-performance batch operations for processing multiple files efficiently.
All methods are available through the AlexSkrypnyk\File\File
class.
use AlexSkrypnyk\File\Exception\FileException;use AlexSkrypnyk\File\ExtendedSplFileInfo;use AlexSkrypnyk\File\File; try { // Get current working directory $cwd = File::cwd(); // Copy a directory recursively File::copy('/path/to/source', '/path/to/destination'); // Check if a file contains a string if (File::contains('/path/to/file.txt', 'search term')) { // Do something } // Process string content directly $content = File::read('/path/to/file.txt'); $processed = File::replaceContent($content, 'old', 'new'); $processed = File::removeToken($processed, '# BEGIN', '# END'); File::dump('/path/to/file.txt', $processed); // Or use batch operations for better performance File::addTaskDirectory(function(ExtendedSplFileInfo $file_info): ExtendedSplFileInfo { $content = File::replaceContent($file_info->getContent(), 'old', 'new'); $file_info->setContent($content); return $file_info; }); File::runTaskDirectory('/path/to/directory'); } catch (FileException $exception) { // Handle any file operation errors echo $exception->getMessage(); }
Available Functions
Function | Description |
---|---|
absolute() |
Get absolute path for provided absolute or relative file. |
compare() |
Compare files between source and destination directories. |
contains() |
Check if file contains a specific string or matches a pattern. |
containsInDir() |
Find all files in directory containing a specific string. |
copy() |
Copy file or directory. |
copyIfExists() |
Copy file or directory if it exists. |
cwd() |
Get current working directory with absolute path. |
diff() |
Create diff files between baseline and destination directories. See Diff Operations below. |
dir() |
Get absolute path for existing directory. |
dirIsEmpty() |
Check if directory is empty. |
dump() |
Write content to a file. |
exists() |
Check if file or directory exists. |
findMatchingPath() |
Find first path that matches a needle among provided paths. |
mkdir() |
Creates a directory if it doesn't exist. |
patch() |
Apply patch files to a baseline and produce a destination. |
read() |
Read file contents. |
realpath() |
Replacement for PHP's realpath resolves non-existing paths. |
remove() |
Remove file or directory. |
removeLine() |
Remove lines containing a specific string from a file. |
removeToken() |
Remove tokens and optionally content between tokens from a string. |
removeTokenInFile() |
Remove tokens and optionally content between tokens from a file. |
removeTokenInDir() |
Remove tokens and optionally content between tokens from all files in a directory. |
renameInDir() |
Rename files in directory by replacing part of the filename. |
replaceContent() |
Replace content in a string. |
replaceContentInFile() |
Replace content in a file. |
replaceContentInDir() |
Replace content in all files in a directory. |
collapseRepeatedEmptyLines() |
Remove multiple consecutive empty lines, keeping at most one empty line between content blocks. |
rmdir() |
Remove directory recursively. |
rmdirEmpty() |
Remove directory recursively if empty. |
scandirRecursive() |
Recursively scan directory for files. |
sync() |
Synchronize files from source to destination directory. |
tmpdir() |
Create temporary directory. |
Batch Operations
For improved performance when processing multiple files, the library provides batch operations that minimize directory scans and optimize I/O operations:
Function | Description |
---|---|
addTaskDirectory() |
Add a batch task to be executed on all files in a directory. |
runTaskDirectory() |
Execute all queued tasks on files in a directory with optimized I/O. |
clearTaskDirectory() |
Clear all queued batch tasks. |
replaceContent() |
Replace content in a string (base method for batch processing). |
removeToken() |
Remove tokens from a string (base method for batch processing). |
collapseRepeatedEmptyLines() |
Remove multiple consecutive empty lines from a string (base method for batch processing). |
Performance Benefits
The batch operations provide significant performance improvements over traditional file operations:
- Single directory scan: Instead of scanning the directory multiple times
- Single I/O per file: Each file is read once, processed by all tasks, then written once
- Memory efficient: Uses generators to handle large file sets without loading everything into memory
Usage Example
use AlexSkrypnyk\File\ExtendedSplFileInfo;use AlexSkrypnyk\File\File; // Traditional approach (slow for multiple operations) File::replaceContentInDir('/path/to/dir', 'old1', 'new1'); File::replaceContentInDir('/path/to/dir', 'old2', 'new2'); File::removeTokenInDir('/path/to/dir', '# token'); // Batch approach (significantly faster) File::addTaskDirectory(function(ExtendedSplFileInfo $file_info): ExtendedSplFileInfo { $content = File::replaceContent($file_info->getContent(), 'old1', 'new1'); $content = File::replaceContent($content, 'old2', 'new2'); $content = File::removeToken($content, '# token'); $content = File::collapseRepeatedEmptyLines($content); $file_info->setContent($content); return $file_info; }); File::runTaskDirectory('/path/to/dir');
Performance Results: In tests with 5,000 files across 100 directories performing 10 operations per file:
- Traditional approach: ~16s (multiple directory scans, multiple I/O per file)
- Batch approach: ~1.7s (~89% faster, single directory scan, single I/O per file)
Architecture
The batch operations are powered by an internal Tasker
queue management system
that:
- Uses PHP generators for memory-efficient processing of large file sets
- Implements a two-way communication pattern between the queue and file processors
- Leverages
ExtendedSplFileInfo
objects for rich file context and metadata - Provides type-safe object validation to ensure data integrity
- Maintains complete separation between the generic queue system and file-specific operations
This architecture allows the library to scale efficiently from small single-file operations to large-scale batch processing scenarios.
Diff Operations
The diff()
, patch()
, and compare()
functions provide powerful tools for
working with file differences between directories:
use AlexSkrypnyk\File\File; use AlexSkrypnyk\File\Exception\PatchException; // Generate diff files between baseline and destination directories File::diff('/path/to/baseline', '/path/to/destination', '/path/to/diff'); // Compare directories to determine if they're equal $result = File::compare('/path/to/source', '/path/to/destination'); // Apply patches to transform a baseline directory try { File::patch('/path/to/baseline', '/path/to/diff', '/path/to/patched'); } catch (PatchException $exception) { echo $exception->getMessage(); // Returns a detailed error message. // Additional contextual information $path = $exception->getFilePath(); // Gets the affected file path. $line_number = $exception->getLineNumber(); // Gets the line number where the error occurred. $line_content = $exception->getLineContent(); // Gets the content of the problematic line. }
The diff functionality allows you to:
- Generate differences between two directory structures
- Store those differences as patch files
- Apply those patches to recreate directory structures elsewhere
The PatchException
provides detailed error messages with contextual
information when patch operations fail, making debugging easier.
Ignoring Files and Content Changes
You can create a .ignorecontent
file in your directories to specify patterns
for files or content that should be ignored during comparison. This is useful
for timestamps, randomly generated values, or files that shouldn't be compared.
The syntax for .ignorecontent
file is similar to .gitignore
with additional
content ignoring capabilities:
# Comments start with #
file.txt # Ignore this specific file
logs/ # Ignore this directory and all subdirectories
temp/* # Ignore all files in directory, but not subdirectories
^config.json # Ignore content changes in this file, but check file exists
^data/ # Ignore content changes in all files in dir and subdirs
^cache/* # Ignore content changes in all files in dir, but not subdirs
!important.txt # Do not ignore this file (exception)
!^settings.php # Do not ignore content changes in this file
Prefix meanings:
- No prefix: Ignore file/directory completely
^
: Ignore content changes but verify file/directory exists!
: Exception - do not ignore this file/directory!^
: Exception - do not ignore content changes in this file/directory
When parsing these rules, the library may throw a RulesException
if there are
issues:
use AlexSkrypnyk\File\File; use AlexSkrypnyk\File\Exception\RulesException; try { // Operations using .ignorecontent rules File::compare('/path/to/source', '/path/to/destination'); } catch (RulesException $exception) { // Handle rules parsing errors echo $exception->getMessage(); }
Assertion Traits
The library includes PHPUnit traits for testing files and directories:
Directory Assertions Trait
Assertion Method | Description |
---|---|
assertDirectoryContainsString() |
Assert that a directory contains files with a specific string. |
assertDirectoryNotContainsString() |
Assert that a directory does not contain files with a specific string. |
assertDirectoryContainsWord() |
Assert that a directory contains files with a specific word (bounded by word boundaries). |
assertDirectoryNotContainsWord() |
Assert that a directory does not contain files with a specific word. |
assertDirectoryEqualsDirectory() |
Assert that two directories have identical structure and content. |
assertDirectoryEqualsPatchedBaseline() |
Assert that a directory is equal to the patched baseline (baseline + diff). |
Usage example:
use PHPUnit\Framework\TestCase; use AlexSkrypnyk\File\Tests\Traits\DirectoryAssertionsTrait; class MyTest extends TestCase { use DirectoryAssertionsTrait; public function testDirectories(): void { // Assert directory contains "example" string in at least one file $this->assertDirectoryContainsString('example', '/path/to/directory'); // Assert two directories are identical $this->assertDirectoryEqualsDirectory('/path/to/dir1', '/path/to/dir2'); } }
File Assertions Trait
Assertion Method | Description |
---|---|
assertFileContainsString() |
Assert that a file contains a specific string. |
assertFileNotContainsString() |
Assert that a file does not contain a specific string. |
assertFileContainsWord() |
Assert that a file contains a specific word (bounded by word boundaries). |
assertFileNotContainsWord() |
Assert that a file does not contain a specific word. |
assertFileEqualsFile() |
Assert that a file equals another file in contents. |
assertFileNotEqualsFile() |
Assert that a file does not equal another file in contents. |
Usage example:
use PHPUnit\Framework\TestCase; use AlexSkrypnyk\File\Tests\Traits\FileAssertionsTrait; class MyTest extends TestCase { use FileAssertionsTrait; public function testFiles(): void { // Assert file contains "example" string $this->assertFileContainsString('example', '/path/to/file.txt'); // Assert file contains "test" as a complete word $this->assertFileContainsWord('test', '/path/to/file.txt'); // Assert file does not contain a partial word $this->assertFileNotContainsWord('exampl', '/path/to/file.txt'); // Assert two files have identical content $this->assertFileEqualsFile('/path/to/expected.txt', '/path/to/actual.txt'); // Assert two files have different content $this->assertFileNotEqualsFile('/path/to/expected.txt', '/path/to/actual.txt'); } }
Maintenance
composer install composer lint composer test # Run performance tests ./vendor/bin/phpunit --testsuite=performance
This repository was created using the Scaffold project template