Skip to content

Commit a1bc229

Browse files
andreaswolfsabbelasichon
authored andcommitted
[FEATURE] Add RulesProvider
1 parent be58b21 commit a1bc229

File tree

14 files changed

+210
-34
lines changed

14 files changed

+210
-34
lines changed

fractor-fluid/config/application.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
declare(strict_types=1);
44

5+
use a9f\Fractor\Rules\RulesProvider;
56
use a9f\FractorFluid\Contract\FluidFractorRule;
67
use a9f\FractorFluid\FluidFileProcessor;
78
use Symfony\Component\DependencyInjection\ContainerBuilder;
89
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
10+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
911
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
1012

1113
return static function (ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void {
@@ -16,7 +18,12 @@
1618

1719
$services->load('a9f\\FractorFluid\\', __DIR__ . '/../src/');
1820

19-
$services->set(FluidFileProcessor::class)->arg('$rules', tagged_iterator('fractor.fluid_rule'));
21+
$services->set('rules_providers.fluid_rule')
22+
->class(RulesProvider::class)
23+
->arg('$baseClassOrInterface', FluidFractorRule::class)
24+
->arg('$rules', tagged_iterator('fractor.fluid_rule'));
25+
$services->set(FluidFileProcessor::class)
26+
->arg('$rulesProvider', service('rules_providers.fluid_rule'));
2027

2128
$containerBuilder->registerForAutoconfiguration(FluidFractorRule::class)->addTag('fractor.fluid_rule');
2229
};

fractor-fluid/src/FluidFileProcessor.php

+4-5
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,17 @@
77
use a9f\Fractor\Application\Contract\FileProcessor;
88
use a9f\Fractor\Application\ValueObject\AppliedRule;
99
use a9f\Fractor\Application\ValueObject\File;
10+
use a9f\Fractor\Rules\RulesProvider;
1011
use a9f\FractorFluid\Contract\FluidFractorRule;
11-
use Webmozart\Assert\Assert;
1212

1313
final readonly class FluidFileProcessor implements FileProcessor
1414
{
1515
/**
16-
* @param iterable<FluidFractorRule> $rules
16+
* @param RulesProvider<FluidFractorRule> $rulesProvider
1717
*/
1818
public function __construct(
19-
private iterable $rules
19+
private RulesProvider $rulesProvider
2020
) {
21-
Assert::allIsInstanceOf($this->rules, FluidFractorRule::class);
2221
}
2322

2423
public function canHandle(File $file): bool
@@ -28,7 +27,7 @@ public function canHandle(File $file): bool
2827

2928
public function handle(File $file): void
3029
{
31-
foreach ($this->rules as $rule) {
30+
foreach ($this->rulesProvider->getApplicableRules($file) as $rule) {
3231
$newContent = $rule->refactor($file->getContent());
3332

3433
if ($newContent !== $file->getContent()) {

fractor-xml/composer.json

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"ext-xml": "*",
1717
"a9f/fractor": "@dev",
1818
"a9f/fractor-extension-installer": "@dev",
19-
"thecodingmachine/safe": "^2.5",
2019
"webmozart/assert": "^1.11"
2120
},
2221
"require-dev": {

fractor-xml/config/application.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
declare(strict_types=1);
44

5+
use a9f\Fractor\Rules\RulesProvider;
56
use a9f\FractorXml\Contract\XmlFractor;
67
use a9f\FractorXml\XmlFileProcessor;
78
use Symfony\Component\DependencyInjection\ContainerBuilder;
89
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
10+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
911
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
1012

1113
return static function (ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void {
@@ -16,7 +18,12 @@
1618

1719
$services->load('a9f\\FractorXml\\', __DIR__ . '/../src/');
1820

19-
$services->set(XmlFileProcessor::class)->arg('$rules', tagged_iterator('fractor.xml_rule'));
21+
$services->set('rules_providers.xml_rule')
22+
->class(RulesProvider::class)
23+
->arg('$baseClassOrInterface', XmlFractor::class)
24+
->arg('$rules', tagged_iterator('fractor.xml_rule'));
25+
$services->set(XmlFileProcessor::class)
26+
->arg('$rulesProvider', service('rules_providers.xml_rule'));
2027

2128
$containerBuilder->registerForAutoconfiguration(XmlFractor::class)->addTag('fractor.xml_rule');
2229
};

fractor-xml/src/DomDocumentIterator.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,20 @@
1616
*/
1717
public const REMOVE_NODE = 3;
1818

19+
/**
20+
* @var array<DomNodeVisitor>
21+
*/
22+
private iterable $visitors;
23+
1924
/**
2025
* @param list<DomNodeVisitor> $visitors
2126
*/
2227
public function __construct(
23-
private iterable $visitors
28+
iterable $visitors
2429
) {
25-
Assert::allIsInstanceOf($this->visitors, DomNodeVisitor::class);
30+
$visitors = iterator_to_array($visitors);
31+
Assert::allIsInstanceOf($visitors, DomNodeVisitor::class);
32+
$this->visitors = $visitors;
2633
}
2734

2835
public function traverseDocument(File $file, \DOMDocument $document): void

fractor-xml/src/XmlFileProcessor.php

+5-13
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,22 @@
55
namespace a9f\FractorXml;
66

77
use a9f\Fractor\Application\Contract\FileProcessor;
8-
use a9f\Fractor\Application\RuleSkipper;
98
use a9f\Fractor\Application\ValueObject\File;
109
use a9f\Fractor\Exception\ShouldNotHappenException;
10+
use a9f\Fractor\Rules\RulesProvider;
1111
use a9f\FractorXml\Contract\XmlFractor;
1212
use a9f\FractorXml\ValueObjectFactory\DomDocumentFactory;
1313
use DOMDocument;
14-
use Webmozart\Assert\Assert;
15-
use function Safe\file_get_contents;
1614

1715
final readonly class XmlFileProcessor implements FileProcessor
1816
{
1917
/**
20-
* @param XmlFractor[] $rules
18+
* @param RulesProvider<XmlFractor> $rulesProvider
2119
*/
2220
public function __construct(
23-
private iterable $rules,
2421
private DomDocumentFactory $domDocumentFactory,
25-
private RuleSkipper $ruleSkipper
22+
private RulesProvider $rulesProvider
2623
) {
27-
Assert::allIsInstanceOf($this->rules, XmlFractor::class);
2824
}
2925

3026
public function canHandle(File $file): bool
@@ -35,17 +31,13 @@ public function canHandle(File $file): bool
3531
public function handle(File $file): void
3632
{
3733
$document = $this->domDocumentFactory->create();
38-
$originalXml = file_get_contents($file->getFilePath());
34+
$originalXml = $file->getOriginalContent();
3935
$document->loadXML($originalXml);
4036

4137
// This is a hacky trick to keep format and create a nice diff later
4238
$file->changeOriginalContent($this->saveXml($document));
4339

44-
$applicableRulesForFile = array_filter(
45-
// for a large number of rules, this might be resource hungry; try to find a better alternative here
46-
iterator_to_array($this->rules),
47-
fn (XmlFractor $rule) => $this->ruleSkipper->shouldSkip($rule::class, $file->getFilePath()) === false
48-
);
40+
$applicableRulesForFile = $this->rulesProvider->getApplicableRules($file);
4941

5042
$iterator = new DomDocumentIterator($applicableRulesForFile);
5143
$iterator->traverseDocument($file, $document);

fractor-yaml/config/application.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
declare(strict_types=1);
44

5+
use a9f\Fractor\Rules\RulesProvider;
56
use a9f\FractorYaml\Contract\YamlDumper;
67
use a9f\FractorYaml\Contract\YamlFractorRule;
78
use a9f\FractorYaml\Contract\YamlParser;
@@ -10,6 +11,7 @@
1011
use a9f\FractorYaml\YamlFileProcessor;
1112
use Symfony\Component\DependencyInjection\ContainerBuilder;
1213
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
14+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
1315
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
1416

1517
return static function (ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void {
@@ -23,7 +25,12 @@
2325
$services->alias(YamlParser::class, SymfonyYamlParser::class);
2426
$services->alias(YamlDumper::class, SymfonyYamlDumper::class);
2527

26-
$services->set(YamlFileProcessor::class)->arg('$rules', tagged_iterator('fractor.yaml_rule'));
28+
$services->set('rules_providers.yaml_rule')
29+
->class(RulesProvider::class)
30+
->arg('$baseClassOrInterface', YamlFractorRule::class)
31+
->arg('$rules', tagged_iterator('fractor.yaml_rule'));
32+
$services->set(YamlFileProcessor::class)
33+
->arg('$rulesProvider', service('rules_providers.yaml_rule'));
2734

2835
$containerBuilder->registerForAutoconfiguration(YamlFractorRule::class)->addTag('fractor.yaml_rule');
2936
};

fractor-yaml/src/YamlFileProcessor.php

+4-5
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,22 @@
77
use a9f\Fractor\Application\Contract\FileProcessor;
88
use a9f\Fractor\Application\ValueObject\AppliedRule;
99
use a9f\Fractor\Application\ValueObject\File;
10+
use a9f\Fractor\Rules\RulesProvider;
1011
use a9f\Fractor\ValueObject\Indent;
1112
use a9f\FractorYaml\Contract\YamlDumper;
1213
use a9f\FractorYaml\Contract\YamlFractorRule;
1314
use a9f\FractorYaml\Contract\YamlParser;
14-
use Webmozart\Assert\Assert;
1515

1616
final readonly class YamlFileProcessor implements FileProcessor
1717
{
1818
/**
19-
* @param iterable<YamlFractorRule> $rules
19+
* @param RulesProvider<YamlFractorRule> $rulesProvider
2020
*/
2121
public function __construct(
22-
private iterable $rules,
22+
private RulesProvider $rulesProvider,
2323
private YamlParser $yamlParser,
2424
private YamlDumper $yamlDumper
2525
) {
26-
Assert::allIsInstanceOf($this->rules, YamlFractorRule::class);
2726
}
2827

2928
public function canHandle(File $file): bool
@@ -38,7 +37,7 @@ public function handle(File $file): void
3837

3938
$newYaml = $yaml;
4039

41-
foreach ($this->rules as $rule) {
40+
foreach ($this->rulesProvider->getApplicableRules($file) as $rule) {
4241
$oldYaml = $newYaml;
4342
$newYaml = $rule->refactor($newYaml);
4443

fractor/config/application.php

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use a9f\Fractor\Differ\ConsoleDiffer;
1111
use a9f\Fractor\Differ\Contract\Differ;
1212
use a9f\Fractor\FractorApplication;
13+
use a9f\Fractor\Rules\RulesProvider;
1314
use Symfony\Component\Console\Attribute\AsCommand;
1415
use Symfony\Component\DependencyInjection\ChildDefinition;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -89,6 +90,8 @@ static function (ChildDefinition $definition, AsCommand $attribute): void {
8990
$services->set(FractorRunner::class)->arg('$processors', tagged_iterator('fractor.file_processor'));
9091
$services->set(AllowedFileExtensionsResolver::class)->arg('$processors', tagged_iterator('fractor.file_processor'));
9192
$services->set(Filesystem::class);
93+
// RulesProvider must be wired individually for each processor to ensure the correct rules base class is set
94+
$services->set(RulesProvider::class)->autowire(false);
9295

9396
$containerBuilder->registerForAutoconfiguration(FileProcessor::class)->addTag('fractor.file_processor');
9497
};

fractor/src/Rules/RulesProvider.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace a9f\Fractor\Rules;
6+
7+
use a9f\Fractor\Application\Contract\FractorRule;
8+
use a9f\Fractor\Application\RuleSkipper;
9+
use a9f\Fractor\Application\ValueObject\File;
10+
use Webmozart\Assert\Assert;
11+
12+
/**
13+
* @template T of FractorRule
14+
*/
15+
final readonly class RulesProvider
16+
{
17+
/**
18+
* @param iterable<T> $rules
19+
* @param class-string<T> $baseClassOrInterface
20+
*/
21+
public function __construct(
22+
private iterable $rules,
23+
private string $baseClassOrInterface,
24+
private RuleSkipper $ruleSkipper
25+
) {
26+
Assert::allIsInstanceOf($this->rules, $this->baseClassOrInterface);
27+
}
28+
29+
/**
30+
* @return \Generator<int, T, void, void>
31+
*/
32+
public function getApplicableRules(File $file): \Generator
33+
{
34+
foreach ($this->rules as $rule) {
35+
if ($this->ruleSkipper->shouldSkip($rule::class, $file->getFilePath())) {
36+
continue;
37+
}
38+
39+
yield $rule;
40+
}
41+
}
42+
}

fractor/tests/Application/FractorRunner/config/config.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
declare(strict_types=1);
44

5+
use a9f\Fractor\Rules\RulesProvider;
56
use a9f\Fractor\Tests\Fixture\DummyProcessor\Contract\TextRule;
67
use a9f\Fractor\Tests\Fixture\DummyProcessor\FileProcessor\TextFileProcessor;
78
use Symfony\Component\DependencyInjection\ContainerBuilder;
89
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
10+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
911
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
1012

1113
return static function (ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void {
@@ -14,6 +16,11 @@
1416
->autowire()
1517
->autoconfigure();
1618

17-
$services->set(TextFileProcessor::class)->arg('$rules', tagged_iterator('fractor.text_rules'));
19+
$services->set('rules_providers.text_file')
20+
->class(RulesProvider::class)
21+
->arg('$baseClassOrInterface', TextRule::class)
22+
->arg('$rules', tagged_iterator('fractor.text_rules'));
23+
$services->set(TextFileProcessor::class)
24+
->arg('$rulesProvider', service('rules_providers.text_file'));
1825
$containerBuilder->registerForAutoconfiguration(TextRule::class)->addTag('fractor.text_rules');
1926
};

fractor/tests/Configuration/AllowedFileExtensionsResolver/config/config.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@
22

33
declare(strict_types=1);
44

5+
use a9f\Fractor\Rules\RulesProvider;
6+
use a9f\Fractor\Tests\Fixture\DummyProcessor\Contract\TextRule;
57
use a9f\Fractor\Tests\Fixture\DummyProcessor\FileProcessor\TextFileProcessor;
68
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
9+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
710
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
811

912
return static function (ContainerConfigurator $containerConfigurator): void {
1013
$services = $containerConfigurator->services();
1114
$services->defaults()
1215
->autowire()
1316
->autoconfigure();
14-
$services->set(TextFileProcessor::class)->arg('$rules', tagged_iterator('fractor.text_rules'));
17+
$services->set('rules_providers.text_file')
18+
->class(RulesProvider::class)
19+
->arg('$baseClassOrInterface', TextRule::class)
20+
->arg('$rules', tagged_iterator('fractor.text_rules'));
21+
$services->set(TextFileProcessor::class)
22+
->arg('$rulesProvider', service('rules_providers.text_file'));
1523
};

fractor/tests/Fixture/DummyProcessor/FileProcessor/TextFileProcessor.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66

77
use a9f\Fractor\Application\Contract\FileProcessor;
88
use a9f\Fractor\Application\ValueObject\File;
9+
use a9f\Fractor\Rules\RulesProvider;
910
use a9f\Fractor\Tests\Fixture\DummyProcessor\Contract\TextRule;
1011

1112
final readonly class TextFileProcessor implements FileProcessor
1213
{
1314
/**
14-
* @param TextRule[] $rules
15+
* @param RulesProvider<TextRule> $rulesProvider
1516
*/
1617
public function __construct(
17-
private iterable $rules
18+
private RulesProvider $rulesProvider
1819
) {
1920
}
2021

@@ -25,7 +26,7 @@ public function canHandle(File $file): bool
2526

2627
public function handle(File $file): void
2728
{
28-
foreach ($this->rules as $rule) {
29+
foreach ($this->rulesProvider->getApplicableRules($file) as $rule) {
2930
$rule->apply($file);
3031
}
3132
}

0 commit comments

Comments
 (0)