PHPackages                             alexskrypnyk/file - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. alexskrypnyk/file

ActiveLibrary[File &amp; Storage](/categories/file-storage)

alexskrypnyk/file
=================

Provides file manipulations functionality.

0.19.0(4mo ago)138.5k↓25%[2 issues](https://github.com/AlexSkrypnyk/file/issues)1GPL-2.0-or-laterPHPPHP &gt;=8.2CI passing

Since Mar 20Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/AlexSkrypnyk/file)[ Packagist](https://packagist.org/packages/alexskrypnyk/file)[ Docs](https://github.com/alexskrypnyk/file)[ GitHub Sponsors](https://github.com/alexskrypnyk)[ Patreon](https://www.patreon.com/alexskrypnyk)[ RSS](/packages/alexskrypnyk-file/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (34)Used By (1)

  ![File logo](logo.png)

Utilities to work with files and directories
============================================

[](#utilities-to-work-with-files-and-directories)

[![GitHub Issues](https://camo.githubusercontent.com/76cd1e6eab6a23f787126adafe45c80c27f04dd3933262d3403c72edfade1456/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f416c6578536b7279706e796b2f66696c652e737667)](https://github.com/AlexSkrypnyk/file/issues)[![GitHub Pull Requests](https://camo.githubusercontent.com/df768fdfdb45de7ed0bf2ccd44f755024b1bba6a9fe72ff7777969ae39dffd77/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732d70722f416c6578536b7279706e796b2f66696c652e737667)](https://github.com/AlexSkrypnyk/file/pulls)[![Test PHP](https://github.com/AlexSkrypnyk/file/actions/workflows/test-php.yml/badge.svg)](https://github.com/AlexSkrypnyk/file/actions/workflows/test-php.yml)[![codecov](https://camo.githubusercontent.com/29fda8462c67c5869875eb59b6a638c45946c34e4a99198ef7cc0b6b760d0034/68747470733a2f2f636f6465636f762e696f2f67682f416c6578536b7279706e796b2f66696c652f67726170682f62616467652e7376673f746f6b656e3d37574542314958425954)](https://codecov.io/gh/AlexSkrypnyk/file)[![GitHub release (latest by date)](https://camo.githubusercontent.com/83d7dd7a311f68209a9d5490e4568ea936392f17ac7b546824f52878f9cb8752/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f416c6578536b7279706e796b2f66696c65)](https://camo.githubusercontent.com/83d7dd7a311f68209a9d5490e4568ea936392f17ac7b546824f52878f9cb8752/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f416c6578536b7279706e796b2f66696c65)[![LICENSE](https://camo.githubusercontent.com/0bf9c025c1ba60bb4bae551cf8b43681d1cddb5cfdd8afeb5b682ef939b7ebcd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f416c6578536b7279706e796b2f66696c65)](https://camo.githubusercontent.com/0bf9c025c1ba60bb4bae551cf8b43681d1cddb5cfdd8afeb5b682ef939b7ebcd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f416c6578536b7279706e796b2f66696c65)[![Renovate](https://camo.githubusercontent.com/35389190ce58a3690fe850342c1c3fd4f54e4c10ba8996741c8558ee24bf50dc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72656e6f766174652d656e61626c65642d677265656e3f6c6f676f3d72656e6f76617465626f74)](https://camo.githubusercontent.com/35389190ce58a3690fe850342c1c3fd4f54e4c10ba8996741c8558ee24bf50dc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72656e6f766174652d656e61626c65642d677265656e3f6c6f676f3d72656e6f76617465626f74)

---

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Usage](#usage)
    - [Available Methods](#available-methods)
    - [Batch Operations](#batch-operations)
    - [Content Replacement](#content-replacement)
    - [Assertion Traits](#assertion-traits)
        - [Directory Assertions Trait](#directory-assertions-trait)
        - [File Assertions Trait](#file-assertions-trait)
- [Maintenance](#maintenance)

Installation
------------

[](#installation)

```
composer require alexskrypnyk/file
```

Usage
-----

[](#usage)

This library provides a comprehensive set of utility 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\ContentFile\ContentFile;
use AlexSkrypnyk\File\Exception\FileException;
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);

  // Append content to an existing file
  File::append('/path/to/log.txt', "\nNew log entry: " . date('Y-m-d H:i:s'));

  // Or use batch operations for better performance
  File::addDirectoryTask(function(ContentFile $file_info): ContentFile {
    $content = File::replaceContent($file_info->getContent(), 'old', 'new');
    $file_info->setContent($content);
    return $file_info;
  });
  File::runDirectoryTasks('/path/to/directory');

} catch (FileException $exception) {
  // Handle any file operation errors
  echo $exception->getMessage();
}
```

### Available Methods

[](#available-methods)

MethodDescription`absolute()`Get absolute path for provided absolute or relative file.`append()`Append content to an existing file.`collapseEmptyLines()`Remove multiple consecutive empty lines from a string.`collapseEmptyLinesInFile()`Remove multiple consecutive empty lines from a file.`collapseEmptyLinesInDir()`Remove multiple consecutive empty lines from all files in a directory.`contains()`Check if file contains a specific string or matches a pattern.`copy()`Copy file or directory.`copyIfExists()`Copy file or directory if it exists.`cwd()`Get current working directory with absolute path.`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.`findContainingInDir()`Find all files in directory containing a specific string.`findMatchingPath()`Find first path that matches a needle among provided paths.`mkdir()`Creates a directory if it doesn't exist.`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 string.`removeLineInFile()`Remove lines containing a specific string from a file.`removeLineInDir()`Remove lines containing a specific string from all files in a directory.`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.`rmdir()`Remove directory recursively.`rmdirIfEmpty()`Remove directory recursively if empty.`scandir()`Recursively scan directory for files.`tmpdir()`Create temporary directory.### Batch Operations

[](#batch-operations)

For improved performance when processing multiple files, the library provides batch operations that minimize directory scans and optimize I/O operations:

MethodDescription`addDirectoryTask()`Add a batch task to be executed on all files in a directory.`runDirectoryTasks()`Execute all queued tasks on files in a directory with optimized I/O.`clearDirectoryTasks()`Clear all queued batch tasks.#### Performance Benefits

[](#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

[](#usage-example)

```
use AlexSkrypnyk\File\ContentFile\ContentFile;
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 because while tasks are added first,
// the directory is scanned only once and each file is read/written only once.
File::addDirectoryTask(function(ContentFile $file_info): ContentFile {
  $content = File::replaceContent($file_info->getContent(), 'old1', 'new1');
  $content = File::replaceContent($content, 'old2', 'new2');
  $content = File::removeToken($content, '# token');
  $content = File::collapseEmptyLines($content);
  $file_info->setContent($content);
  return $file_info;
});

File::runDirectoryTasks('/path/to/dir');
```

```
use AlexSkrypnyk\File\ContentFile\ContentFile;
use AlexSkrypnyk\File\File;
use AlexSkrypnyk\File\Replacer\Replacement;
use AlexSkrypnyk\File\Replacer\Replacer;

// Batch approach with a custom Replacer for complex replacements.
File::addDirectoryTask(function(ContentFile $file_info): ContentFile {
  $content = $file_info->getContent();

  Replacer::create()
    ->addReplacement(Replacement::create('version', '/v\d+\.\d+\.\d+/', '__VERSION__'))
    ->addReplacement(Replacement::create('year', '/20\d{2}/', '__YEAR__'))
    ->replace($content);

  $file_info->setContent($content);
  return $file_info;
});

File::runDirectoryTasks('/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

[](#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 `ContentFile` objects for file content manipulation
- 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.

### Content Replacement

[](#content-replacement)

The `Replacer` class provides pattern-based content replacement in files and directories. It's particularly useful for normalizing volatile content like version numbers, hashes, and timestamps.

#### Basic Usage

[](#basic-usage)

```
use AlexSkrypnyk\File\Replacer\Replacement;
use AlexSkrypnyk\File\Replacer\Replacer;

// Use preset version patterns
$replacer = Replacer::create()->addVersionReplacements();
$replacer->replaceInDir($directory);

// Or create custom replacer
$replacer = Replacer::create()
    ->addReplacement(Replacement::create('version', '/v\d+\.\d+\.\d+/', '__VERSION__'))
    ->addReplacement(Replacement::create('date', '/\d{4}-\d{2}-\d{2}/', '__DATE__'));

// Apply to string content
$content = 'Version: v1.2.3';
$replacer->replace($content);  // $content is now 'Version: __VERSION__'

// Apply to directory
$replacer->replaceInDir($directory);
```

#### Version Patterns Preset

[](#version-patterns-preset)

The `addVersionReplacements()` method adds common patterns for normalizing volatile content:

PatternExample InputOutputSemver versions`1.2.3`, `v1.2.3-beta.1``__VERSION__`Git hashes`@abc123...` (40 chars)`@__HASH__`SRI integrity hashes`sha512-...``__INTEGRITY__`Docker image tags`nginx:1.21.0``nginx:__VERSION__`GitHub Actions versions`actions/checkout@v4``actions/checkout@__VERSION__`Package versions in JSON`"^1.2.3"``"__VERSION__"`#### Exclusions

[](#exclusions)

Add exclusions to prevent specific matches from being replaced:

```
$replacer = Replacer::create()
    ->addVersionReplacements()
    ->setMaxReplacements(0)
    ->addExclusions(['/^0\.0\./'], 'semver');  // Don't replace 0.0.x versions

// Or add exclusions to all rules
$replacer->addExclusions(['127.0.0.1']);  // Don't replace IP addresses

// Exclusions can be:
// - Regex patterns: '/^0\./'
// - Exact strings: '127.0.0.1'
// - Callbacks: fn(string $match): bool => $match === '9.9.9'
```

#### Custom Replacements

[](#custom-replacements)

Create custom replacement patterns:

```
$replacer = Replacer::create()
    ->addReplacement(Replacement::create('build', '/BUILD-\d+/', '__BUILD__'))
    ->addReplacement(Replacement::create('timestamp', '/\d{10}/', '__TIMESTAMP__'))
    ->setMaxReplacements(0);  // 0 = unlimited replacements

$replacer->replaceInDir($directory, ['/path/to/ignore']);
```

### Assertion Traits

[](#assertion-traits)

The library includes PHPUnit traits for testing files and directories:

#### Directory Assertions Trait

[](#directory-assertions-trait)

Assertion MethodDescription`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.Usage example:

```
use PHPUnit\Framework\TestCase;
use AlexSkrypnyk\File\Testing\DirectoryAssertionsTrait;

class MyTest extends TestCase {
  use DirectoryAssertionsTrait;

  public function testDirectories(): void {
    // Assert directory contains "example" string in at least one file
    $this->assertDirectoryContainsString('/path/to/directory', 'example');

    // Assert directory contains "example" string, ignoring specific files
    $this->assertDirectoryContainsString('/path/to/directory', 'example', ['temp.log', 'cache']);

    // Assert directory does not contain specific word
    $this->assertDirectoryNotContainsWord('/path/to/directory', 'forbidden');
  }
}
```

##### Ignoring Paths in Directory Assertions

[](#ignoring-paths-in-directory-assertions)

The directory assertion methods support ignoring specific paths during searches. You can ignore paths in two ways:

1. **Per-method ignoring**: Pass an `$ignored` array parameter to individual assertion methods
2. **Global ignoring**: Override the `ignoredPaths()` method in your test class

```
class MyTest extends TestCase {
  use DirectoryAssertionsTrait;

  // Global ignored paths for all directory assertions in this test class
  public static function ignoredPaths(): array {
    return ['.git', 'node_modules', 'vendor', 'temp/cache'];
  }

  public function testWithIgnoredPaths(): void {
    // This will ignore both global ignored paths AND 'logs' directory
    $this->assertDirectoryContainsString('/path/to/dir', 'search_term', ['logs']);

    // Global ignored paths are automatically applied to all directory assertions
    $this->assertDirectoryNotContainsWord('/path/to/dir', 'forbidden');
  }
}
```

**Important Notes:**

- Ignored paths are literal subpaths (not wildcard patterns)
- Global `ignoredPaths()` and per-method `$ignored` parameters are merged together
- Both file names and directory paths can be ignored
- Ignored paths are relative to the directory being searched

#### File Assertions Trait

[](#file-assertions-trait)

Assertion MethodDescription`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.`assertFilesExist()`Assert that multiple files exist in a directory.`assertFilesDoNotExist()`Assert that multiple files do not exist in a directory.`assertFilesWildcardExists()`Assert that files matching wildcard pattern(s) exist.`assertFilesWildcardDoNotExist()`Assert that files matching wildcard pattern(s) do not exist.Usage example:

```
use PHPUnit\Framework\TestCase;
use AlexSkrypnyk\File\Testing\FileAssertionsTrait;

class MyTest extends TestCase {
  use FileAssertionsTrait;

  public function testFiles(): void {
    // Assert file contains "example" string
    $this->assertFileContainsString('/path/to/file.txt', 'example');

    // Assert file contains "test" as a complete word
    $this->assertFileContainsWord('/path/to/file.txt', 'test');

    // Assert file does not contain a partial word
    $this->assertFileNotContainsWord('/path/to/file.txt', 'exampl');

    // 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');

    // Assert that multiple files exist in a directory
    $this->assertFilesExist('/path/to/directory', ['file1.txt', 'file2.txt']);

    // Assert that multiple files do not exist in a directory
    $this->assertFilesDoNotExist('/path/to/directory', ['file1.txt', 'file2.txt']);

    // Assert that files matching wildcard pattern(s) exist
    $this->assertFilesWildcardExists('*.txt');
    $this->assertFilesWildcardExists(['*.txt', '*.json']);

    // Assert that files matching wildcard pattern(s) do not exist
    $this->assertFilesWildcardDoNotExist('*.log');
    $this->assertFilesWildcardDoNotExist(['*.tmp', '*.cache']);

    // All assertion methods support optional custom failure messages
    $this->assertFileContainsString('/path/to/file.txt', 'example', 'Custom failure message');
    $this->assertFilesExist('/path/to/directory', ['file1.txt'], 'Files should exist');
    $this->assertDirectoryContainsString('/path/to/dir', 'search_term', [], 'Custom message');
  }
}
```

Maintenance
-----------

[](#maintenance)

```
composer install
composer lint
composer test

# Run performance benchmarks (PHPBench)
composer benchmark
```

---

*This repository was created using the [Scaffold](https://getscaffold.dev/)project template*

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance61

Regular maintenance activity

Popularity31

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 69.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~9 days

Total

30

Last Release

147d ago

PHP version history (2 changes)0.1.0PHP &gt;=8.3

0.1.2PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/b57b0fd0b96f77f2efa1a1889af0ae607fa139bcc1256e809ee3ebbb30907364?d=identicon)[alexdrevops](/maintainers/alexdrevops)

---

Top Contributors

[![AlexSkrypnyk](https://avatars.githubusercontent.com/u/378794?v=4)](https://github.com/AlexSkrypnyk "AlexSkrypnyk (69 commits)")[![renovate[bot]](https://avatars.githubusercontent.com/in/2740?v=4)](https://github.com/renovate[bot] "renovate[bot] (28 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (2 commits)")

---

Tags

filefilesystemphpreplacetoken

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Type Coverage Yes

### Embed Badge

![Health badge](/badges/alexskrypnyk-file/health.svg)

```
[![Health](https://phpackages.com/badges/alexskrypnyk-file/health.svg)](https://phpackages.com/packages/alexskrypnyk-file)
```

###  Alternatives

[aws/aws-sdk-php

AWS SDK for PHP - Use Amazon Web Services in your PHP project

6.2k511.3M2.2k](/packages/aws-aws-sdk-php)[terminal42/contao-fineuploader

FineUploader bundle for Contao Open Source CMS

2052.9k4](/packages/terminal42-contao-fineuploader)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
