PHP Logosymfony/finder

The `symfony/finder` component is a powerful and flexible PHP library designed to find files and directories within a given directory structure. It provides an intuitive, fluent interface for constructing complex search queries, making it a superior alternative to manual file system scanning functions like `glob()`, `scandir()`, or direct usage of `RecursiveDirectoryIterator`.

Key features and functionalities of `symfony/finder` include:

1. Fluent Interface: Allows chaining multiple methods to build complex search criteria easily.
2. Directory Specification: You can specify one or more directories to search within using the `in()` method.
3. File/Directory Type Filtering: Methods like `files()` and `directories()` allow you to restrict results to only files or only directories.
4. Name Filtering: Use `name()` and `notName()` with patterns (wildcards like `*.php`) or regular expressions to include or exclude files/directories by their names.
5. Path Filtering: Similar to name filtering but applies to the full path of the item using `path()` and `notPath()`.
6. Depth Control: Limit the search depth within subdirectories using `depth()`, e.g., `depth('< 2')` to only search the immediate subdirectories.
7. Size Filtering: Filter by file size using `size()`, supporting comparisons like `'> 10K'`, `'<= 1M'`, `'== 200'`. `K`, `M`, `G` suffixes are supported.
8. Date Filtering: Filter by last modified date using `date()`, e.g., `date('> now - 1 week')`, `date('since yesterday')`.
9. Ignore VCS Files/Directories: Automatically ignore common version control system directories (`.git`, `.svn`, etc.) with `ignoreVCS(true)`.
10. Ignore Dot Files: Exclude files and directories starting with a dot (like `.htaccess`) using `ignoreDotFiles(true)`.
11. Custom Filters: Apply arbitrary PHP callables as filters using `filter()` for highly specific conditions.
12. Sorting: Sort results by name, type, size, or date using `sortByName()`, `sortByType()`, `sortBySize()`, `sortByModifiedTime()`.
13. Iterator-based: The `Finder` instance itself is traversable, returning `SplFileInfo` objects for each found item, providing rich file information.

`symfony/finder` is an essential tool for many common tasks in PHP applications, such as:
* Locating configuration files.
* Finding assets (CSS, JS, images).
* Clearing cache directories.
* Scanning log files.
* Discovering test files or classes within a project.

Its robustness and clear API make it a go-to choice for any file system traversal needs.

Example Code

<?php

require_once __DIR__ . '/vendor/autoload.php'; // Assuming Composer autoload

use Symfony\Component\Finder\Finder;

// Create a temporary directory and some files for demonstration
$tempDir = __DIR__ . '/temp_finder_test';
if (!is_dir($tempDir)) {
    mkdir($tempDir, 0777, true);
}
file_put_contents($tempDir . '/file1.php', '<?php echo "Hello";');
file_put_contents($tempDir . '/index.html', '<html></html>');
file_put_contents($tempDir . '/.env', 'APP_ENV=dev');
mkdir($tempDir . '/src', 0777);
file_put_contents($tempDir . '/src/Service.php', '<?php class Service {}');
mkdir($tempDir . '/src/tests', 0777);
file_put_contents($tempDir . '/src/tests/ServiceTest.php', '<?php class ServiceTest {}');
mkdir($tempDir . '/config', 0777);
file_put_contents($tempDir . '/config/app.yaml', 'version: 1.0');

// ----------------------------------------------------------------------------
// Example 1: Find all PHP files, excluding test files, in 'src' directory
// ----------------------------------------------------------------------------
echo "\n--- Example 1: PHP files excluding tests in 'src' ---\n";
$finder1 = new Finder();
$finder1->files()
        ->in($tempDir . '/src')
        ->name('*.php')
        ->notName('*Test.php')
        ->ignoreDotFiles(true);

if ($finder1->hasResults()) {
    foreach ($finder1 as $file) {
        echo $file->getRelativePathname() . "\n";
    }
} else {
    echo "No results found.\n";
}
// Expected output: src/Service.php

// ----------------------------------------------------------------------------
// Example 2: Find all files modified in the last 24 hours, less than 1MB, anywhere
// ----------------------------------------------------------------------------
echo "\n--- Example 2: Files modified recently and small size ---\n";
// Touch a file to simulate recent modification for demonstration
touch($tempDir . '/config/app.yaml', strtotime('-1 hour'));

$finder2 = new Finder();
$finder2->files()
        ->in($tempDir)
        ->date('> now - 24 hours') // Files modified in the last 24 hours
        ->size('< 1M')           // Files smaller than 1 megabyte
        ->ignoreDotFiles(false)  // Include dot files for this example
        ->sortByName();          // Sort results by name

if ($finder2->hasResults()) {
    foreach ($finder2 as $file) {
        echo $file->getPathname() . " (size: " . $file->getSize() . " bytes, modified: " . date('Y-m-d H:i:s', $file->getMTime()) . ")\n";
    }
} else {
    echo "No results found.\n";
}
// Expected output will include newly created files and touched app.yaml

// ----------------------------------------------------------------------------
// Example 3: Find all directories at depth 1 (immediate subdirectories)
// ----------------------------------------------------------------------------
echo "\n--- Example 3: Directories at depth 1 ---\n";
$finder3 = new Finder();
$finder3->directories()
        ->in($tempDir)
        ->depth('== 0') // Depth 0 means direct children of the 'in' directory
        ->sortByName();

if ($finder3->hasResults()) {
    foreach ($finder3 as $directory) {
        echo $directory->getRelativePathname() . "\n";
    }
} else {
    echo "No results found.\n";
}
// Expected output: config, src

// ----------------------------------------------------------------------------
// Clean up temporary files and directories
// ----------------------------------------------------------------------------
$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($tempDir, RecursiveDirectoryIterator::SKIP_DOTS),
    RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $item) {
    if ($item->isDir()) {
        rmdir($item->getPathname());
    } else {
        unlink($item->getPathname());
    }
}
rmdir($tempDir);

?>