Skip to content

Commit

Permalink
Use rule sets' real name when registering
Browse files Browse the repository at this point in the history
It does not make sense to pass name explicitly during registration, because ruleset should identify itself using contract (`RuleSetDescriptionInterface::getName()`).
  • Loading branch information
Wirone committed Jan 19, 2024
1 parent a83eb9e commit 780c403
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 39 deletions.
6 changes: 3 additions & 3 deletions doc/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,19 @@ configure them in your config file.
->setLineEnding("\r\n")
;
It's possible to register custom rule sets, which makes it easier to reuse custom configuration between multiple projects. If you have prepared rule set, you can register it and then enable it in the rules. Custom rule sets (in this example `\MyNameSpace\MyRuleSetClass`) must implement `\PhpCsFixer\RuleSet\RuleSetDescriptionInterface`.
It's possible to register custom rule sets, which makes it easier to reuse custom configuration between multiple projects. If you have prepared rule set, you can register it and then enable it in the rules. Custom rule sets (in this example ``\MyNameSpace\MyRuleSetClass``) must implement ``\PhpCsFixer\RuleSet\RuleSetDescriptionInterface``.

.. code-block:: php
<?php
return (new PhpCsFixer\Config())
->registerCustomRuleSets([
'@MyRuleSet' => MyNameSpace\MyRuleSetClass::class,
MyNameSpace\MyRuleSetClass::class, // It identifies itself as '@MyRuleSet'
])
->setRules([
'@MyRuleSet' => true,
])
;
ℹ️ If you use other `\PhpCsFixer\ConfigInterface` implementation than built-in one, you can register custom rule sets using `\PhpCsFixer\RuleSet\RuleSets::registerRuleSet()` just before the configuration. Currently `\PhpCsFixer\Config::registerCustomRuleSets()` is not part of the `ConfigInterface` because it would be a BC-break - it will be added there in the next major version of the Fixer.
ℹ️ If you use other ``\PhpCsFixer\ConfigInterface`` implementation than built-in one, you can register custom rule sets using ``\PhpCsFixer\RuleSet\RuleSets::registerRuleSet()`` just before the configuration. Currently ``\PhpCsFixer\Config::registerCustomRuleSets()`` is not part of the ``ConfigInterface`` because it would be a BC-break - it will be added there in the next major version of the Fixer.
8 changes: 3 additions & 5 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,14 @@ public function registerCustomFixers(iterable $fixers): ConfigInterface
/**
* Registers custom rule sets to be used the same way as built-in rule sets.
*
* `$ruleSets` must follow `'@RuleName' => RuleSetDescriptionInterface::class` convention.
*
* @param array<string, class-string<RuleSetDescriptionInterface>> $ruleSets
* @param list<class-string<RuleSetDescriptionInterface>> $ruleSets
*
* @todo Introduce it in ConfigInterface in 4.0
*/
public function registerCustomRuleSets(array $ruleSets): ConfigInterface
{
foreach ($ruleSets as $name => $class) {
RuleSets::registerRuleSet($name, $class);
foreach ($ruleSets as $class) {
RuleSets::registerRuleSet($class);
}

return $this;
Expand Down
31 changes: 18 additions & 13 deletions src/RuleSet/RuleSets.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class RuleSets
/**
* @var array<string, RuleSetDescriptionInterface>
*/
private static $setDefinitions;
private static ?array $setDefinitions = null;

/**
* @return array<string, RuleSetDescriptionInterface>
Expand Down Expand Up @@ -76,8 +76,23 @@ public static function getSetDefinition(string $name): RuleSetDescriptionInterfa
/**
* @param class-string<RuleSetDescriptionInterface> $class
*/
public static function registerRuleSet(string $name, string $class): void
public static function registerRuleSet(string $class): void
{
if (!class_exists($class)
|| !\in_array(RuleSetDescriptionInterface::class, class_implements($class), true)
) {
throw new \InvalidArgumentException(
sprintf(
'Class "%s" must be an instance of "%s".',
$class,
RuleSetDescriptionInterface::class
)
);
}

$ruleset = new $class();
$name = $ruleset->getName();

if (!RuleSetNameValidator::isValid($name, false)) {
throw new \InvalidArgumentException('RuleSet name must begin with "@" and a letter (a-z, A-Z), and can contain only letters (a-z, A-Z), numbers, underscores, slashes, colons, dots and hyphens.');
}
Expand All @@ -92,17 +107,7 @@ public static function registerRuleSet(string $name, string $class): void
throw new \InvalidArgumentException(sprintf('Set "%s" is already defined.', $name));
}

if (!\in_array(RuleSetDescriptionInterface::class, class_implements($class), true)) {
throw new \InvalidArgumentException(
sprintf(
'Class "%s" must be an instance of "%s".',
$class,
RuleSetDescriptionInterface::class
)
);
}

self::$setDefinitions[$name] = new $class();
self::$setDefinitions[$name] = $ruleset;

self::sortSetDefinitions();
}
Expand Down
16 changes: 11 additions & 5 deletions tests/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use PhpCsFixer\Fixer\ArrayNotation\NoWhitespaceBeforeCommaInArrayFixer;
use PhpCsFixer\Fixer\ControlStructure\IncludeFixer;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\RuleSet\RuleSetDescriptionInterface;
use PhpCsFixer\RuleSet\RuleSets;
use PhpCsFixer\Tests\Fixtures\ExternalRuleSet\SampleRulesBad;
use PhpCsFixer\Tests\Fixtures\ExternalRuleSet\SampleRulesOk;
Expand Down Expand Up @@ -210,7 +211,7 @@ public function testRegisterCustomFixers(array $expected, iterable $suite): void
}

/**
* @param array<string, string> $ruleSets
* @param list<class-string> $ruleSets
*
* @dataProvider provideRegisterCustomRuleSetsCases
*/
Expand All @@ -224,7 +225,12 @@ public function testRegisterCustomRuleSets(?string $expectedException, array $ru
$config->registerCustomRuleSets($ruleSets);

if (null === $expectedException) {
self::assertContains(array_keys($ruleSets)[0], RuleSets::getSetDefinitionNames());
foreach ($ruleSets as $ruleSetClass) {
/** @var RuleSetDescriptionInterface $ruleSet */
$ruleSet = new $ruleSetClass();

self::assertContains($ruleSet->getName(), RuleSets::getSetDefinitionNames());
}
}
}

Expand Down Expand Up @@ -283,13 +289,13 @@ public static function provideRegisterCustomFixersCases(): iterable
}

/**
* @return iterable<array{0: null|class-string<\Throwable>, 1: array<string, class-string>}>
* @return iterable<array{0: null|class-string<\Throwable>, 1: list<class-string>}>
*/
public static function provideRegisterCustomRuleSetsCases(): iterable
{
yield [null, ['@RulesOk' => SampleRulesOk::class]];
yield [null, [SampleRulesOk::class]];

yield [\InvalidArgumentException::class, ['@RulesBad' => SampleRulesBad::class]];
yield [\InvalidArgumentException::class, [SampleRulesBad::class]];
}

public function testConfigConstructorWithName(): void
Expand Down
31 changes: 18 additions & 13 deletions tests/RuleSet/RuleSetsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@
*/
final class RuleSetsTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();

// Since we register custom rule sets statically, we need to clear resolved rule sets between runs.
$reflection = new \ReflectionClass(RuleSets::class);
$reflectionProperty = $reflection->getProperty('setDefinitions');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($reflection, null);
}

public function testGetSetDefinitionNames(): void
{
self::assertSame(
Expand Down Expand Up @@ -177,33 +188,27 @@ public function testRegisterRuleSetMissingClass(): void
{
self::expectException(\InvalidArgumentException::class);
// @phpstan-ignore-next-line
RuleSets::registerRuleSet('@Vendor/MyRules', '\This\Class\Does\Not\Exists');
RuleSets::registerRuleSet('\This\Class\Does\Not\Exists');
}

public function testRegisterRuleSetOverlappingName(): void
{
RuleSets::registerRuleSet('@Vendor/MyAnotherRuleSetForTesting', SampleRulesOk::class);
self::expectException(\InvalidArgumentException::class);
RuleSets::registerRuleSet('@Vendor/MyAnotherRuleSetForTesting', SampleRulesOk::class);
}

public function testRegisterRuleSetInvalidName(): void
{
RuleSets::registerRuleSet(SampleRulesOk::class);
self::expectException(\InvalidArgumentException::class);
RuleSets::registerRuleSet('bad name', SampleRulesOk::class);
RuleSets::registerRuleSet(SampleRulesOk::class);
}

public function testRegisterRuleSetInvalidClass(): void
{
self::expectException(\InvalidArgumentException::class);
// @phpstan-ignore-next-line
RuleSets::registerRuleSet('@Vendor/MyClass', SampleRulesBad::class);

RuleSets::registerRuleSet(SampleRulesBad::class); // @phpstan-ignore-line
}

public function testCanReadCustomRegisteredRuleSet(): void
{
RuleSets::registerRuleSet('@Vendor/MySet', SampleRulesOk::class);
$set = RuleSets::getSetDefinition('@Vendor/MySet');
RuleSets::registerRuleSet(SampleRulesOk::class);
$set = RuleSets::getSetDefinition('@Vendor/RulesOk');
self::assertSame('@Vendor/RulesOk', $set->getName());
}

Expand Down

0 comments on commit 780c403

Please sign in to comment.