MidПрактика5 min

Filesystem и Finder

Filesystem component, Finder component, файловые операции, поиск файлов

Filesystem Component

Symfony 8.0: Компоненты Filesystem и Finder явно указаны в темах экзамена.

Filesystem предоставляет кроссплатформенные операции с файловой системой. Все методы выбрасывают IOException при ошибке.

<?php

declare(strict_types=1);

namespace App\Service;

use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOException;

final class FileManager
{
    private readonly Filesystem $filesystem;

    public function __construct()
    {
        $this->filesystem = new Filesystem();
    }

    public function createProjectStructure(string $baseDir): void
    {
        // Create directory (recursive by default)
        $this->filesystem->mkdir([
            "$baseDir/src",
            "$baseDir/config",
            "$baseDir/tests",
            "$baseDir/public",
        ]);

        // Create file with content
        $this->filesystem->dumpFile(
            "$baseDir/README.md",
            "# My Project\n\nDescription here.",
        );

        // Append to file
        $this->filesystem->appendToFile(
            "$baseDir/README.md",
            "\n\n## Installation\n\nTODO",
        );
    }

    public function copyAssets(string $source, string $target): void
    {
        // Copy file
        $this->filesystem->copy(
            "$source/logo.png",
            "$target/logo.png",
            overwriteNewerFiles: true,
        );

        // Mirror directory (copy entire directory tree)
        $this->filesystem->mirror(
            "$source/assets",
            "$target/public/assets",
        );
    }

    public function cleanup(string $dir): void
    {
        // Check existence
        if (!$this->filesystem->exists($dir)) {
            return;
        }

        // Remove files and directories
        $this->filesystem->remove([
            "$dir/cache",
            "$dir/logs",
            "$dir/temp.txt",
        ]);
    }
}

Основные методы Filesystem

Метод Описание
mkdir($dirs) Создать директории
exists($files) Проверить существование
copy($source, $target) Копировать файл
mirror($source, $target) Зеркалировать директорию
remove($files) Удалить файлы/директории
rename($source, $target) Переименовать/переместить
symlink($source, $target) Создать символическую ссылку
dumpFile($file, $content) Создать/перезаписать файл
appendToFile($file, $content) Дописать в файл
chmod($files, $mode) Изменить права
chown($files, $user) Изменить владельца
chgrp($files, $group) Изменить группу
touch($files) Обновить время модификации
tempnam($dir, $prefix) Создать временный файл

Работа с путями

<?php

declare(strict_types=1);

use Symfony\Component\Filesystem\Path;

// Canonicalize path
$path = Path::canonicalize('/var/www/../log/app.log');
// Result: /var/log/app.log

// Make path relative
$relative = Path::makeRelative(
    '/var/www/project/public/index.php',
    '/var/www/project',
);
// Result: public/index.php

// Make path absolute
$absolute = Path::makeAbsolute('config/routes.yaml', '/var/www/project');
// Result: /var/www/project/config/routes.yaml

// Join paths
$joined = Path::join('/var/www', 'project', 'public');
// Result: /var/www/project/public

// Get extension
$ext = Path::getExtension('/path/to/file.txt');
// Result: txt

// Check if absolute
$isAbsolute = Path::isAbsolute('/var/www');  // true
$isAbsolute = Path::isAbsolute('var/www');   // false

Finder Component

Finder -- мощный инструмент для поиска файлов и директорий с fluent-интерфейсом.

<?php

declare(strict_types=1);

namespace App\Service;

use Symfony\Component\Finder\Finder;

final class CodeAnalyzer
{
    public function findPhpFiles(string $directory): Finder
    {
        $finder = new Finder();

        $finder
            ->files()             // Only files (not directories)
            ->in($directory)      // Search directory
            ->name('*.php')       // File name pattern
            ->notName('*Test.php')// Exclude pattern
            ->exclude('vendor')   // Exclude directory
            ->sortByName();       // Sort results

        return $finder;
    }

    public function countLines(string $directory): int
    {
        $finder = $this->findPhpFiles($directory);
        $totalLines = 0;

        foreach ($finder as $file) {
            // $file is SplFileInfo
            $totalLines += substr_count($file->getContents(), "\n");
        }

        return $totalLines;
    }
}

Фильтры поиска

<?php

declare(strict_types=1);

use Symfony\Component\Finder\Finder;

$finder = new Finder();

// Search in multiple directories
$finder->in(['/var/www/project/src', '/var/www/project/lib']);

// Files only or directories only
$finder->files();       // Only files
$finder->directories(); // Only directories

// Name patterns (glob or regex)
$finder->name('*.php');                    // Glob
$finder->name('/\.php$/');                 // Regex
$finder->name(['*.php', '*.twig']);        // Multiple patterns
$finder->notName('*.bak');                 // Exclude

// Path filtering
$finder->path('Controller');               // Contains 'Controller' in path
$finder->notPath('vendor');                // Exclude path

// Size filtering
$finder->size('> 1K');                     // Greater than 1KB
$finder->size('< 100M');                   // Less than 100MB
$finder->size('>= 1K')->size('<= 2M');     // Range

// Date filtering
$finder->date('since yesterday');
$finder->date('> 2025-01-01');
$finder->date('until 2 days ago');

// Depth filtering
$finder->depth('== 0');                    // Only current directory
$finder->depth('< 3');                     // Max 3 levels deep
$finder->depth('>= 1');                    // Skip current directory

// Content filtering
$finder->contains('use Doctrine');          // File contains text
$finder->notContains('deprecated');         // File doesn't contain

// Exclude directories
$finder->exclude(['vendor', 'node_modules', '.git']);
$finder->ignoreDotFiles(true);             // Ignore .* files
$finder->ignoreVCS(true);                  // Ignore VCS files (.git, .svn)

// Symlinks
$finder->followLinks();                    // Follow symbolic links

Сортировка

<?php

declare(strict_types=1);

use Symfony\Component\Finder\Finder;

$finder = new Finder();
$finder->files()->in('/path/to/dir');

// Built-in sorts
$finder->sortByName();              // Alphabetical
$finder->sortByModifiedTime();      // Newest last
$finder->sortByAccessedTime();      // By access time
$finder->sortByType();              // Directories first, then files

// Custom sort
$finder->sort(function (\SplFileInfo $a, \SplFileInfo $b): int {
    return $b->getSize() <=> $a->getSize(); // Largest first
});

// Reverse sort
$finder->sortByName()->reverseSorting();

SplFileInfo методы

<?php

declare(strict_types=1);

use Symfony\Component\Finder\Finder;

$finder = new Finder();
$finder->files()->in('/path')->name('*.php');

foreach ($finder as $file) {
    $file->getFilename();        // "MyClass.php"
    $file->getExtension();       // "php"
    $file->getBasename('.php');   // "MyClass"
    $file->getRelativePath();    // Relative dir from search root
    $file->getRelativePathname();// Relative path with filename
    $file->getRealPath();        // Absolute path
    $file->getPath();            // Directory path
    $file->getSize();            // File size in bytes
    $file->getMTime();           // Modification time
    $file->getContents();        // File contents as string
    $file->isReadable();         // Is readable
    $file->isWritable();         // Is writable
}

Практический пример: очистка логов

<?php

declare(strict_types=1);

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;

#[AsCommand(name: 'app:cleanup-logs')]
final class CleanupLogsCommand extends Command
{
    public function __construct(
        private readonly string $logsDir,
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $filesystem = new Filesystem();

        $finder = new Finder();
        $finder
            ->files()
            ->in($this->logsDir)
            ->name('*.log')
            ->date('< 7 days ago')
            ->sortByModifiedTime();

        $count = $finder->count();

        if ($count === 0) {
            $io->success('No old log files to clean up');
            return Command::SUCCESS;
        }

        $totalSize = 0;
        foreach ($finder as $file) {
            $totalSize += $file->getSize();
            $filesystem->remove($file->getRealPath());
        }

        $io->success(sprintf(
            'Removed %d log files (%.2f MB)',
            $count,
            $totalSize / 1024 / 1024,
        ));

        return Command::SUCCESS;
    }
}

Итоги

  • Filesystem: кроссплатформенные файловые операции с обработкой ошибок
  • dumpFile(), copy(), mirror(), remove() -- основные методы
  • Path -- утилиты для работы с путями (canonicalize, join, makeRelative)
  • Finder: fluent-поиск файлов с фильтрами по имени, размеру, дате, содержимому
  • $finder->files()->in()->name()->size()->date() -- цепочка фильтров
  • SplFileInfo -- информация о найденном файле

Связанные темы