Skip to content

Commit 47a2b31

Browse files
[filesystem] Isolate Finder creation behind a factory (#90) (#91)
* Inject finder factory for mutable scans * Update wiki submodule pointer for PR #91 * Use local filesystem interface for services * Update wiki submodule pointer for PR #91 --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 3e009b8 commit 47a2b31

26 files changed

Lines changed: 310 additions & 63 deletions

.github/wiki

Submodule wiki updated from 8852048 to db93e83

src/Agent/Skills/SkillsSynchronizer.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919

2020
namespace FastForward\DevTools\Agent\Skills;
2121

22+
use FastForward\DevTools\Filesystem\FinderFactoryInterface;
23+
use FastForward\DevTools\Filesystem\FilesystemInterface;
2224
use Psr\Log\LoggerAwareInterface;
2325
use Psr\Log\LoggerInterface;
24-
use Symfony\Component\Filesystem\Filesystem;
25-
use Symfony\Component\Finder\Finder;
2626
use Symfony\Component\Filesystem\Path;
2727

2828
/**
@@ -35,15 +35,15 @@
3535
final class SkillsSynchronizer implements LoggerAwareInterface
3636
{
3737
/**
38-
* Initializes the synchronizer with a filesystem and finder instance.
38+
* Initializes the synchronizer with a filesystem and finder factory.
3939
*
40-
* @param Filesystem $filesystem Filesystem instance for file operations
41-
* @param Finder $finder Finder instance for locating skill directories in the package
40+
* @param FilesystemInterface $filesystem Filesystem instance for file operations
41+
* @param FinderFactoryInterface $finderFactory Factory for locating skill directories in the package
4242
* @param LoggerInterface $logger Logger for recording synchronization actions and decisions
4343
*/
4444
public function __construct(
45-
private readonly Filesystem $filesystem,
46-
private readonly Finder $finder,
45+
private readonly FilesystemInterface $filesystem,
46+
private readonly FinderFactoryInterface $finderFactory,
4747
private LoggerInterface $logger,
4848
) {}
4949

@@ -103,7 +103,8 @@ private function syncPackageSkills(
103103
string $packageSkillsPath,
104104
SynchronizeResult $result,
105105
): void {
106-
$finder = $this->finder
106+
$finder = $this->finderFactory
107+
->create()
107108
->directories()
108109
->in($packageSkillsPath)
109110
->depth('== 0');

src/Console/Command/CopyResourceCommand.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
namespace FastForward\DevTools\Console\Command;
2121

2222
use Composer\Command\BaseCommand;
23+
use FastForward\DevTools\Filesystem\FinderFactoryInterface;
2324
use FastForward\DevTools\Filesystem\FilesystemInterface;
2425
use Symfony\Component\Config\FileLocatorInterface;
2526
use Symfony\Component\Console\Attribute\AsCommand;
2627
use Symfony\Component\Console\Input\InputInterface;
2728
use Symfony\Component\Console\Input\InputOption;
2829
use Symfony\Component\Console\Output\OutputInterface;
2930
use Symfony\Component\Filesystem\Path;
30-
use Symfony\Component\Finder\Finder;
3131

3232
/**
3333
* Copies packaged or local resources into the consumer repository.
@@ -44,12 +44,12 @@ final class CopyResourceCommand extends BaseCommand
4444
*
4545
* @param FilesystemInterface $filesystem the filesystem used for copy operations
4646
* @param FileLocatorInterface $fileLocator the locator used to resolve source resources
47-
* @param Finder $finder the finder used to iterate directory resources
47+
* @param FinderFactoryInterface $finderFactory the factory used to create finders for directory resources
4848
*/
4949
public function __construct(
5050
private readonly FilesystemInterface $filesystem,
5151
private readonly FileLocatorInterface $fileLocator,
52-
private readonly Finder $finder,
52+
private readonly FinderFactoryInterface $finderFactory,
5353
) {
5454
parent::__construct();
5555
}
@@ -126,7 +126,8 @@ private function copyDirectory(
126126
bool $overwrite,
127127
OutputInterface $output
128128
): int {
129-
$files = $this->finder
129+
$files = $this->finderFactory
130+
->create()
130131
->files()
131132
->in($sourcePath);
132133

src/Console/Command/GitAttributesCommand.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919

2020
namespace FastForward\DevTools\Console\Command;
2121

22+
use Composer\Command\BaseCommand;
2223
use FastForward\DevTools\Composer\Json\ComposerJsonInterface;
2324
use FastForward\DevTools\Filesystem\FilesystemInterface;
24-
use Composer\Command\BaseCommand;
2525
use FastForward\DevTools\GitAttributes\CandidateProviderInterface;
2626
use FastForward\DevTools\GitAttributes\ExistenceCheckerInterface;
2727
use FastForward\DevTools\GitAttributes\ExportIgnoreFilterInterface;
@@ -31,7 +31,6 @@
3131
use Symfony\Component\Console\Attribute\AsCommand;
3232
use Symfony\Component\Console\Input\InputInterface;
3333
use Symfony\Component\Console\Output\OutputInterface;
34-
use Symfony\Component\Filesystem\Filesystem;
3534

3635
use function Safe\getcwd;
3736

@@ -66,7 +65,7 @@ final class GitAttributesCommand extends BaseCommand
6665
* @param MergerInterface $merger the merger component
6766
* @param ReaderInterface $reader the reader component
6867
* @param WriterInterface $writer the writer component
69-
* @param Filesystem $filesystem the filesystem component
68+
* @param FilesystemInterface $filesystem the filesystem component
7069
* @param ComposerJsonInterface $composer the composer.json accessor
7170
*/
7271
public function __construct(

src/Console/Command/GitHooksCommand.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespace FastForward\DevTools\Console\Command;
2121

2222
use Composer\Command\BaseCommand;
23+
use FastForward\DevTools\Filesystem\FinderFactoryInterface;
2324
use FastForward\DevTools\Filesystem\FilesystemInterface;
2425
use FastForward\DevTools\Process\ProcessBuilderInterface;
2526
use FastForward\DevTools\Process\ProcessQueueInterface;
@@ -29,7 +30,6 @@
2930
use Symfony\Component\Console\Input\InputOption;
3031
use Symfony\Component\Console\Output\OutputInterface;
3132
use Symfony\Component\Filesystem\Path;
32-
use Symfony\Component\Finder\Finder;
3333

3434
/**
3535
* Installs Git hooks and initializes GrumPHP hooks for the consumer repository.
@@ -48,14 +48,14 @@ final class GitHooksCommand extends BaseCommand
4848
* @param FileLocatorInterface $fileLocator the locator used to find packaged hooks
4949
* @param ProcessBuilderInterface $processBuilder the builder used to assemble GrumPHP processes
5050
* @param ProcessQueueInterface $processQueue the queue used to execute GrumPHP initialization
51-
* @param Finder $finder the finder used to iterate hook files
51+
* @param FinderFactoryInterface $finderFactory the factory used to create finders for hook files
5252
*/
5353
public function __construct(
5454
private readonly FilesystemInterface $filesystem,
5555
private readonly FileLocatorInterface $fileLocator,
5656
private readonly ProcessBuilderInterface $processBuilder,
5757
private readonly ProcessQueueInterface $processQueue,
58-
private readonly Finder $finder,
58+
private readonly FinderFactoryInterface $finderFactory,
5959
) {
6060
parent::__construct();
6161
}
@@ -118,7 +118,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
118118
$targetPath = (string) $this->filesystem->getAbsolutePath((string) $input->getOption('target'));
119119
$overwrite = ! $input->getOption('no-overwrite');
120120

121-
$files = $this->finder
121+
$files = $this->finderFactory
122+
->create()
122123
->files()
123124
->in($sourcePath);
124125

src/Console/CommandLoader/DevToolsCommandLoader.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919

2020
namespace FastForward\DevTools\Console\CommandLoader;
2121

22-
use ReflectionClass;
22+
use FastForward\DevTools\Filesystem\FinderFactoryInterface;
2323
use Psr\Container\ContainerInterface;
24+
use ReflectionClass;
2425
use Symfony\Component\Console\Attribute\AsCommand;
2526
use Symfony\Component\Console\Command\Command;
2627
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
27-
use Symfony\Component\Finder\Finder;
2828

2929
/**
3030
* Responsible for dynamically discovering and loading Symfony Console commands
@@ -50,26 +50,27 @@ final class DevToolsCommandLoader extends ContainerCommandLoader
5050
* instantiable and have the AsCommand attribute.
5151
* It builds a command map associating command names with their respective classes.
5252
*
53-
* @param Finder $finder
53+
* @param FinderFactoryInterface $finderFactory
5454
* @param ContainerInterface $container
5555
*/
56-
public function __construct(Finder $finder, ContainerInterface $container)
56+
public function __construct(FinderFactoryInterface $finderFactory, ContainerInterface $container)
5757
{
58-
parent::__construct($container, $this->getCommandMap($finder));
58+
parent::__construct($container, $this->getCommandMap($finderFactory));
5959
}
6060

6161
/**
6262
* Builds a command map by scanning the Command directory for classes that are instantiable and have the AsCommand attribute.
6363
*
64-
* @param Finder $finder
64+
* @param FinderFactoryInterface $finderFactory
6565
*
6666
* @return array
6767
*/
68-
private function getCommandMap(Finder $finder): array
68+
private function getCommandMap(FinderFactoryInterface $finderFactory): array
6969
{
7070
$commandMap = [];
7171

72-
$commandsDirectory = $finder
72+
$commandsDirectory = $finderFactory
73+
->create()
7374
->files()
7475
->in(__DIR__ . '/../Command')
7576
->name('*.php');

src/Filesystem/Filesystem.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,44 @@ public function chmod(string|iterable $files, int $mode, int $umask = 0o000, boo
102102
parent::chmod($this->getAbsolutePath($files), $mode, $umask, $recursive);
103103
}
104104

105+
/**
106+
* Removes files, symbolic links, or directories.
107+
*
108+
* @param iterable<string>|string $files the file(s), link(s), or directory(ies) to remove
109+
*/
110+
#[Override]
111+
public function remove(string|iterable $files): void
112+
{
113+
parent::remove($this->getAbsolutePath($files));
114+
}
115+
116+
/**
117+
* Creates a symbolic link.
118+
*
119+
* @param string $originDir the origin path the link MUST point to
120+
* @param string $targetDir the link path to create
121+
* @param bool $copyOnWindows whether directories SHOULD be copied on Windows instead of linked
122+
*/
123+
#[Override]
124+
public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false): void
125+
{
126+
parent::symlink($this->getAbsolutePath($originDir), $this->getAbsolutePath($targetDir), $copyOnWindows);
127+
}
128+
129+
/**
130+
* Reads a symbolic link target.
131+
*
132+
* @param string $path the symbolic link path
133+
* @param bool $canonicalize whether the returned path SHOULD be canonicalized
134+
*
135+
* @return string|null the link target, or null when the path is not a symbolic link
136+
*/
137+
#[Override]
138+
public function readlink(string $path, bool $canonicalize = false): ?string
139+
{
140+
return parent::readlink($this->getAbsolutePath($path), $canonicalize);
141+
}
142+
105143
/**
106144
* Resolves a path or iterable of paths into their absolute path representation.
107145
*

src/Filesystem/FilesystemInterface.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,32 @@ public function copy(string $originFile, string $targetFile, bool $overwriteNewe
7575
*/
7676
public function chmod(string|iterable $files, int $mode, int $umask = 0o000, bool $recursive = false): void;
7777

78+
/**
79+
* Removes files, symbolic links, or directories.
80+
*
81+
* @param iterable<string>|string $files the file(s), link(s), or directory(ies) to remove
82+
*/
83+
public function remove(string|iterable $files): void;
84+
85+
/**
86+
* Creates a symbolic link.
87+
*
88+
* @param string $originDir the origin path the link MUST point to
89+
* @param string $targetDir the link path to create
90+
* @param bool $copyOnWindows whether directories SHOULD be copied on Windows instead of linked
91+
*/
92+
public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false): void;
93+
94+
/**
95+
* Reads a symbolic link target.
96+
*
97+
* @param string $path the symbolic link path
98+
* @param bool $canonicalize whether the returned path SHOULD be canonicalized
99+
*
100+
* @return string|null the link target, or null when the path is not a symbolic link
101+
*/
102+
public function readlink(string $path, bool $canonicalize = false): ?string;
103+
78104
/**
79105
* Resolves a path or iterable of paths into their absolute path representation.
80106
*

src/Filesystem/FinderFactory.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Fast Forward Development Tools for PHP projects.
7+
*
8+
* This file is part of fast-forward/dev-tools project.
9+
*
10+
* @author Felipe Sayão Lobato Abreu <github@mentordosnerds.com>
11+
* @license https://opensource.org/licenses/MIT MIT License
12+
*
13+
* @see https://github.com/php-fast-forward/
14+
* @see https://github.com/php-fast-forward/dev-tools
15+
* @see https://github.com/php-fast-forward/dev-tools/issues
16+
* @see https://php-fast-forward.github.io/dev-tools/
17+
* @see https://datatracker.ietf.org/doc/html/rfc2119
18+
*/
19+
20+
namespace FastForward\DevTools\Filesystem;
21+
22+
use Symfony\Component\Finder\Finder;
23+
24+
/**
25+
* Default factory for Symfony Finder instances.
26+
*/
27+
final class FinderFactory implements FinderFactoryInterface
28+
{
29+
/**
30+
* {@inheritDoc}
31+
*/
32+
public function create(): Finder
33+
{
34+
return new Finder();
35+
}
36+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Fast Forward Development Tools for PHP projects.
7+
*
8+
* This file is part of fast-forward/dev-tools project.
9+
*
10+
* @author Felipe Sayão Lobato Abreu <github@mentordosnerds.com>
11+
* @license https://opensource.org/licenses/MIT MIT License
12+
*
13+
* @see https://github.com/php-fast-forward/
14+
* @see https://github.com/php-fast-forward/dev-tools
15+
* @see https://github.com/php-fast-forward/dev-tools/issues
16+
* @see https://php-fast-forward.github.io/dev-tools/
17+
* @see https://datatracker.ietf.org/doc/html/rfc2119
18+
*/
19+
20+
namespace FastForward\DevTools\Filesystem;
21+
22+
use Symfony\Component\Finder\Finder;
23+
24+
/**
25+
* Creates fresh Symfony Finder instances for each filesystem scan.
26+
*/
27+
interface FinderFactoryInterface
28+
{
29+
/**
30+
* Creates a new mutable Finder instance.
31+
*
32+
* @return Finder a Finder instance that callers MAY configure for one scan
33+
*/
34+
public function create(): Finder;
35+
}

0 commit comments

Comments
 (0)