diff --git a/docs/documentation/assertions.md b/docs/documentation/assertions.md index 8eb7bf70..e2a8b9ec 100644 --- a/docs/documentation/assertions.md +++ b/docs/documentation/assertions.md @@ -41,3 +41,6 @@ It asserts that the selected classes **apply** the target attributes. It asserts that the selected classes **do not depend** on anything else than the target classes. This would be equivalent to `shouldNotDependOn()` with the negation of the target classes. + +## shouldBeInterface() +It asserts that the selected classes are **interfaces**. diff --git a/extension.neon b/extension.neon index 4fa08b47..8919864c 100644 --- a/extension.neon +++ b/extension.neon @@ -54,6 +54,12 @@ services: tags: - phpstan.rules.rule + # ShouldBeInterface rules + - + class: PHPat\Rule\Assertion\Declaration\ShouldBeInterface\IsInterfaceRule + tags: + - phpstan.rules.rule + # # # # # RELATION RULES # # # # # # ShouldImplement rules diff --git a/src/Rule/Assertion/Declaration/ShouldBeInterface/IsInterfaceRule.php b/src/Rule/Assertion/Declaration/ShouldBeInterface/IsInterfaceRule.php new file mode 100644 index 00000000..a7e03d87 --- /dev/null +++ b/src/Rule/Assertion/Declaration/ShouldBeInterface/IsInterfaceRule.php @@ -0,0 +1,15 @@ + + */ +final class IsInterfaceRule extends ShouldBeInterface implements Rule +{ + use InterfaceExtractor; +} diff --git a/src/Rule/Assertion/Declaration/ShouldBeInterface/ShouldBeInterface.php b/src/Rule/Assertion/Declaration/ShouldBeInterface/ShouldBeInterface.php new file mode 100644 index 00000000..9782aca3 --- /dev/null +++ b/src/Rule/Assertion/Declaration/ShouldBeInterface/ShouldBeInterface.php @@ -0,0 +1,49 @@ + $tips + * @return array + */ + protected function applyValidation(string $ruleName, ClassReflection $subject, bool $meetsDeclaration, array $tips): array + { + return $this->applyShould($ruleName, $subject, $meetsDeclaration, $tips); + } + + protected function getMessage(string $ruleName, string $subject): string + { + return $this->prepareMessage( + $ruleName, + sprintf('%s should be an interface', $subject) + ); + } +} diff --git a/src/Rule/Extractor/Declaration/InterfaceExtractor.php b/src/Rule/Extractor/Declaration/InterfaceExtractor.php new file mode 100644 index 00000000..c46c3a5a --- /dev/null +++ b/src/Rule/Extractor/Declaration/InterfaceExtractor.php @@ -0,0 +1,23 @@ +getClassReflection()->isInterface(); + } +} diff --git a/src/Test/Builder/AssertionStep.php b/src/Test/Builder/AssertionStep.php index 292e4ebc..f75cdaec 100644 --- a/src/Test/Builder/AssertionStep.php +++ b/src/Test/Builder/AssertionStep.php @@ -4,6 +4,7 @@ use PHPat\Rule\Assertion\Declaration\ShouldBeAbstract\ShouldBeAbstract; use PHPat\Rule\Assertion\Declaration\ShouldBeFinal\ShouldBeFinal; +use PHPat\Rule\Assertion\Declaration\ShouldBeInterface\ShouldBeInterface; use PHPat\Rule\Assertion\Declaration\ShouldBeReadonly\ShouldBeReadonly; use PHPat\Rule\Assertion\Declaration\ShouldHaveOnlyOnePublicMethod\ShouldHaveOnlyOnePublicMethod; use PHPat\Rule\Assertion\Declaration\ShouldNotBeAbstract\ShouldNotBeAbstract; @@ -116,4 +117,11 @@ public function shouldApplyAttribute(): TargetStep return new TargetStep($this->rule); } + + public function shouldBeInterface(): Rule + { + $this->rule->assertion = ShouldBeInterface::class; + + return new BuildStep($this->rule); + } } diff --git a/tests/unit/rules/ShouldBeInterface/InterfaceClassTest.php b/tests/unit/rules/ShouldBeInterface/InterfaceClassTest.php new file mode 100644 index 00000000..c1fdba5f --- /dev/null +++ b/tests/unit/rules/ShouldBeInterface/InterfaceClassTest.php @@ -0,0 +1,48 @@ + + * @internal + * @coversNothing + */ +class InterfaceClassTest extends RuleTestCase +{ + public const RULE_NAME = 'test_FixtureClassShouldBeInterface'; + + public function testRule(): void + { + $this->analyse(['tests/fixtures/Simple/SimpleClass.php'], [ + [sprintf('%s should be an interface', SimpleClass::class), 5], + ]); + } + + protected function getRule(): Rule + { + $testParser = FakeTestParser::create( + self::RULE_NAME, + ShouldBeInterface::class, + [new Classname(SimpleClass::class, false)], + [] + ); + + return new IsInterfaceRule( + new StatementBuilderFactory($testParser), + new Configuration(false, true, false), + $this->createReflectionProvider(), + self::getContainer()->getByType(FileTypeMapper::class) + ); + } +} diff --git a/tests/unit/rules/ShouldBeInterface/ShowRuleNameInterfaceClassTest.php b/tests/unit/rules/ShouldBeInterface/ShowRuleNameInterfaceClassTest.php new file mode 100644 index 00000000..9dc70a30 --- /dev/null +++ b/tests/unit/rules/ShouldBeInterface/ShowRuleNameInterfaceClassTest.php @@ -0,0 +1,48 @@ + + * @internal + * @coversNothing + */ +class ShowRuleNameInterfaceClassTest extends RuleTestCase +{ + public const RULE_NAME = 'test_FixtureClassShouldBeInterface'; + + public function testRule(): void + { + $this->analyse(['tests/fixtures/Simple/SimpleClass.php'], [ + [sprintf('%s: %s should be an interface', self::RULE_NAME, SimpleClass::class), 5], + ]); + } + + protected function getRule(): Rule + { + $testParser = FakeTestParser::create( + self::RULE_NAME, + ShouldBeInterface::class, + [new Classname(SimpleClass::class, false)], + [] + ); + + return new IsInterfaceRule( + new StatementBuilderFactory($testParser), + new Configuration(false, true, true), + $this->createReflectionProvider(), + self::getContainer()->getByType(FileTypeMapper::class) + ); + } +}