From 2459c7446409e00c9dbee1f60996dac9fcc50cbc Mon Sep 17 00:00:00 2001 From: Dawid Parafinski Date: Fri, 10 Oct 2025 14:19:39 +0200 Subject: [PATCH 1/3] Added rule to use proper suffix/prefix --- extension.neon | 2 + phpstan-baseline.neon | 16 ++++ phpstan.neon | 3 + rules/NamingConventionRule.php | 75 +++++++++++++++++++ .../Fixtures/NamingConventionFixture.php | 33 ++++++++ tests/rules/NamingConventionRuleTest.php | 47 ++++++++++++ 6 files changed, 176 insertions(+) create mode 100644 phpstan-baseline.neon create mode 100644 rules/NamingConventionRule.php create mode 100644 tests/rules/Fixtures/NamingConventionFixture.php create mode 100644 tests/rules/NamingConventionRuleTest.php diff --git a/extension.neon b/extension.neon index 80cf7ed..e77ccdd 100644 --- a/extension.neon +++ b/extension.neon @@ -7,3 +7,5 @@ parameters: - stubs/Money/MoneyParser.stub rules: - Ibexa\PHPStan\Rules\NoConfigResolverParametersInConstructorRule + - Ibexa\PHPStan\Rules\RequireInterfaceInDependenciesRule + - Ibexa\PHPStan\Rules\NamingConventionRule diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..0258184 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,16 @@ +parameters: + ignoreErrors: + - + message: "#^Property Ibexa\\\\Tests\\\\PHPStan\\\\Rules\\\\Fixtures\\\\RequireInterfaceInDependenciesFixture\\:\\:\\$classWithoutInterface is never read, only written\\.$#" + count: 1 + path: tests/rules/Fixtures/RequireInterfaceInDependenciesFixture.php + + - + message: "#^Property Ibexa\\\\Tests\\\\PHPStan\\\\Rules\\\\Fixtures\\\\RequireInterfaceInDependenciesFixture\\:\\:\\$concreteClass is never read, only written\\.$#" + count: 1 + path: tests/rules/Fixtures/RequireInterfaceInDependenciesFixture.php + + - + message: "#^Property Ibexa\\\\Tests\\\\PHPStan\\\\Rules\\\\Fixtures\\\\RequireInterfaceInDependenciesFixture\\:\\:\\$testInterface is never read, only written\\.$#" + count: 1 + path: tests/rules/Fixtures/RequireInterfaceInDependenciesFixture.php diff --git a/phpstan.neon b/phpstan.neon index ab3d432..c4d75b1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,3 +1,6 @@ +includes: + - phpstan-baseline.neon + parameters: level: 8 paths: diff --git a/rules/NamingConventionRule.php b/rules/NamingConventionRule.php new file mode 100644 index 0000000..70ccb2a --- /dev/null +++ b/rules/NamingConventionRule.php @@ -0,0 +1,75 @@ + + */ +final class NamingConventionRule implements Rule +{ + public function getNodeType(): string + { + return Node\Stmt\ClassLike::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$this->isRelevantNode($node)) { + return []; + } + + if ($node->name === null) { + return []; + } + + $className = $node->name->toString(); + $errors = []; + + if ($node instanceof Node\Stmt\Interface_ && substr($className, -9) !== 'Interface') { + $errors[] = RuleErrorBuilder::message( + sprintf( + 'Interface "%s" should have "Interface" suffix', + $className + ) + )->build(); + } + + if ($node instanceof Node\Stmt\Trait_ && substr($className, -5) !== 'Trait') { + $errors[] = RuleErrorBuilder::message( + sprintf( + 'Trait "%s" should have "Trait" suffix', + $className + ) + )->build(); + } + + if ($node instanceof Node\Stmt\Class_ && $node->isAbstract() && strpos($className, 'Abstract') !== 0) { + $errors[] = RuleErrorBuilder::message( + sprintf( + 'Abstract class "%s" should have "Abstract" prefix', + $className + ) + )->build(); + } + + return $errors; + } + + private function isRelevantNode(Node $node): bool + { + return $node instanceof Node\Stmt\Interface_ + || $node instanceof Node\Stmt\Trait_ + || ($node instanceof Node\Stmt\Class_ && $node->isAbstract()); + } +} diff --git a/tests/rules/Fixtures/NamingConventionFixture.php b/tests/rules/Fixtures/NamingConventionFixture.php new file mode 100644 index 0000000..237f0bb --- /dev/null +++ b/tests/rules/Fixtures/NamingConventionFixture.php @@ -0,0 +1,33 @@ + + */ +final class NamingConventionRuleTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new NamingConventionRule(); + } + + public function testRule(): void + { + $this->analyse( + [ + __DIR__ . '/Fixtures/NamingConventionFixture.php', + ], + [ + [ + 'Interface "WrongName" should have "Interface" suffix', + 11, + ], + [ + 'Trait "SimpleThing" should have "Trait" suffix', + 19, + ], + [ + 'Abstract class "SimpleClass" should have "Abstract" prefix', + 27, + ], + ] + ); + } +} From bf93fec64b197b4c3be0ae6cc43926ffbd841aaf Mon Sep 17 00:00:00 2001 From: Dawid Parafinski Date: Fri, 10 Oct 2025 15:50:18 +0200 Subject: [PATCH 2/3] cleanup some names --- .../AbstractCorrectClass.php} | 22 +------------------ .../NamingConvention/CorrectNameInterface.php | 13 +++++++++++ .../NamingConvention/CorrectNameTrait.php | 13 +++++++++++ .../Fixtures/NamingConvention/SimpleClass.php | 13 +++++++++++ .../Fixtures/NamingConvention/SimpleThing.php | 13 +++++++++++ .../Fixtures/NamingConvention/WrongName.php | 13 +++++++++++ tests/rules/NamingConventionRuleTest.php | 11 +++++++--- 7 files changed, 74 insertions(+), 24 deletions(-) rename tests/rules/Fixtures/{NamingConventionFixture.php => NamingConvention/AbstractCorrectClass.php} (56%) create mode 100644 tests/rules/Fixtures/NamingConvention/CorrectNameInterface.php create mode 100644 tests/rules/Fixtures/NamingConvention/CorrectNameTrait.php create mode 100644 tests/rules/Fixtures/NamingConvention/SimpleClass.php create mode 100644 tests/rules/Fixtures/NamingConvention/SimpleThing.php create mode 100644 tests/rules/Fixtures/NamingConvention/WrongName.php diff --git a/tests/rules/Fixtures/NamingConventionFixture.php b/tests/rules/Fixtures/NamingConvention/AbstractCorrectClass.php similarity index 56% rename from tests/rules/Fixtures/NamingConventionFixture.php rename to tests/rules/Fixtures/NamingConvention/AbstractCorrectClass.php index 237f0bb..37c4dd5 100644 --- a/tests/rules/Fixtures/NamingConventionFixture.php +++ b/tests/rules/Fixtures/NamingConvention/AbstractCorrectClass.php @@ -6,27 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Fixtures; - -interface WrongName -{ -} - -interface CorrectNameInterface -{ -} - -trait SimpleThing -{ -} - -trait CorrectNameTrait -{ -} - -abstract class SimpleClass -{ -} +namespace Ibexa\Tests\PHPStan\Rules\Fixtures\NamingConvention; abstract class AbstractCorrectClass { diff --git a/tests/rules/Fixtures/NamingConvention/CorrectNameInterface.php b/tests/rules/Fixtures/NamingConvention/CorrectNameInterface.php new file mode 100644 index 0000000..408ec76 --- /dev/null +++ b/tests/rules/Fixtures/NamingConvention/CorrectNameInterface.php @@ -0,0 +1,13 @@ +analyse( [ - __DIR__ . '/Fixtures/NamingConventionFixture.php', + __DIR__ . '/Fixtures/NamingConvention/WrongName.php', + __DIR__ . '/Fixtures/NamingConvention/SimpleThing.php', + __DIR__ . '/Fixtures/NamingConvention/SimpleClass.php', + __DIR__ . '/Fixtures/NamingConvention/CorrectNameInterface.php', + __DIR__ . '/Fixtures/NamingConvention/CorrectNameTrait.php', + __DIR__ . '/Fixtures/NamingConvention/AbstractCorrectClass.php', ], [ [ @@ -35,11 +40,11 @@ public function testRule(): void ], [ 'Trait "SimpleThing" should have "Trait" suffix', - 19, + 11, ], [ 'Abstract class "SimpleClass" should have "Abstract" prefix', - 27, + 11, ], ] ); From 4f25a0290daf9880f61888773f5af964d6e6d239 Mon Sep 17 00:00:00 2001 From: Dawid Parafinski Date: Mon, 13 Oct 2025 08:41:46 +0200 Subject: [PATCH 3/3] Renamed NamingConventionRule to ClassTypeNamingRule --- extension.neon | 3 +-- phpstan-baseline.neon | 16 --------------- ...entionRule.php => ClassTypeNamingRule.php} | 2 +- ...leTest.php => ClassTypeNamingRuleTest.php} | 20 +++++++++---------- .../AbstractCorrectClass.php | 2 +- .../CorrectNameInterface.php | 2 +- .../CorrectNameTrait.php | 2 +- .../SimpleClass.php | 2 +- .../SimpleThing.php | 2 +- .../WrongName.php | 2 +- 10 files changed, 18 insertions(+), 35 deletions(-) rename rules/{NamingConventionRule.php => ClassTypeNamingRule.php} (97%) rename tests/rules/{NamingConventionRuleTest.php => ClassTypeNamingRuleTest.php} (57%) rename tests/rules/Fixtures/{NamingConvention => ClassTypeNaming}/AbstractCorrectClass.php (79%) rename tests/rules/Fixtures/{NamingConvention => ClassTypeNaming}/CorrectNameInterface.php (79%) rename tests/rules/Fixtures/{NamingConvention => ClassTypeNaming}/CorrectNameTrait.php (78%) rename tests/rules/Fixtures/{NamingConvention => ClassTypeNaming}/SimpleClass.php (79%) rename tests/rules/Fixtures/{NamingConvention => ClassTypeNaming}/SimpleThing.php (78%) rename tests/rules/Fixtures/{NamingConvention => ClassTypeNaming}/WrongName.php (78%) diff --git a/extension.neon b/extension.neon index e77ccdd..f1d36f9 100644 --- a/extension.neon +++ b/extension.neon @@ -7,5 +7,4 @@ parameters: - stubs/Money/MoneyParser.stub rules: - Ibexa\PHPStan\Rules\NoConfigResolverParametersInConstructorRule - - Ibexa\PHPStan\Rules\RequireInterfaceInDependenciesRule - - Ibexa\PHPStan\Rules\NamingConventionRule + - Ibexa\PHPStan\Rules\ClassTypeNamingRule diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0258184..e69de29 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,16 +0,0 @@ -parameters: - ignoreErrors: - - - message: "#^Property Ibexa\\\\Tests\\\\PHPStan\\\\Rules\\\\Fixtures\\\\RequireInterfaceInDependenciesFixture\\:\\:\\$classWithoutInterface is never read, only written\\.$#" - count: 1 - path: tests/rules/Fixtures/RequireInterfaceInDependenciesFixture.php - - - - message: "#^Property Ibexa\\\\Tests\\\\PHPStan\\\\Rules\\\\Fixtures\\\\RequireInterfaceInDependenciesFixture\\:\\:\\$concreteClass is never read, only written\\.$#" - count: 1 - path: tests/rules/Fixtures/RequireInterfaceInDependenciesFixture.php - - - - message: "#^Property Ibexa\\\\Tests\\\\PHPStan\\\\Rules\\\\Fixtures\\\\RequireInterfaceInDependenciesFixture\\:\\:\\$testInterface is never read, only written\\.$#" - count: 1 - path: tests/rules/Fixtures/RequireInterfaceInDependenciesFixture.php diff --git a/rules/NamingConventionRule.php b/rules/ClassTypeNamingRule.php similarity index 97% rename from rules/NamingConventionRule.php rename to rules/ClassTypeNamingRule.php index 70ccb2a..c259fab 100644 --- a/rules/NamingConventionRule.php +++ b/rules/ClassTypeNamingRule.php @@ -16,7 +16,7 @@ /** * @implements \PHPStan\Rules\Rule<\PhpParser\Node\Stmt\ClassLike> */ -final class NamingConventionRule implements Rule +final class ClassTypeNamingRule implements Rule { public function getNodeType(): string { diff --git a/tests/rules/NamingConventionRuleTest.php b/tests/rules/ClassTypeNamingRuleTest.php similarity index 57% rename from tests/rules/NamingConventionRuleTest.php rename to tests/rules/ClassTypeNamingRuleTest.php index 7a6724f..507f757 100644 --- a/tests/rules/NamingConventionRuleTest.php +++ b/tests/rules/ClassTypeNamingRuleTest.php @@ -8,30 +8,30 @@ namespace Ibexa\Tests\PHPStan\Rules; -use Ibexa\PHPStan\Rules\NamingConventionRule; +use Ibexa\PHPStan\Rules\ClassTypeNamingRule; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; /** - * @extends \PHPStan\Testing\RuleTestCase<\Ibexa\PHPStan\Rules\NamingConventionRule> + * @extends \PHPStan\Testing\RuleTestCase<\Ibexa\PHPStan\Rules\ClassTypeNamingRule> */ -final class NamingConventionRuleTest extends RuleTestCase +final class ClassTypeNamingRuleTest extends RuleTestCase { protected function getRule(): Rule { - return new NamingConventionRule(); + return new ClassTypeNamingRule(); } public function testRule(): void { $this->analyse( [ - __DIR__ . '/Fixtures/NamingConvention/WrongName.php', - __DIR__ . '/Fixtures/NamingConvention/SimpleThing.php', - __DIR__ . '/Fixtures/NamingConvention/SimpleClass.php', - __DIR__ . '/Fixtures/NamingConvention/CorrectNameInterface.php', - __DIR__ . '/Fixtures/NamingConvention/CorrectNameTrait.php', - __DIR__ . '/Fixtures/NamingConvention/AbstractCorrectClass.php', + __DIR__ . '/Fixtures/ClassTypeNaming/WrongName.php', + __DIR__ . '/Fixtures/ClassTypeNaming/SimpleThing.php', + __DIR__ . '/Fixtures/ClassTypeNaming/SimpleClass.php', + __DIR__ . '/Fixtures/ClassTypeNaming/CorrectNameInterface.php', + __DIR__ . '/Fixtures/ClassTypeNaming/CorrectNameTrait.php', + __DIR__ . '/Fixtures/ClassTypeNaming/AbstractCorrectClass.php', ], [ [ diff --git a/tests/rules/Fixtures/NamingConvention/AbstractCorrectClass.php b/tests/rules/Fixtures/ClassTypeNaming/AbstractCorrectClass.php similarity index 79% rename from tests/rules/Fixtures/NamingConvention/AbstractCorrectClass.php rename to tests/rules/Fixtures/ClassTypeNaming/AbstractCorrectClass.php index 37c4dd5..1a2f293 100644 --- a/tests/rules/Fixtures/NamingConvention/AbstractCorrectClass.php +++ b/tests/rules/Fixtures/ClassTypeNaming/AbstractCorrectClass.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Fixtures\NamingConvention; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures\ClassTypeNaming; abstract class AbstractCorrectClass { diff --git a/tests/rules/Fixtures/NamingConvention/CorrectNameInterface.php b/tests/rules/Fixtures/ClassTypeNaming/CorrectNameInterface.php similarity index 79% rename from tests/rules/Fixtures/NamingConvention/CorrectNameInterface.php rename to tests/rules/Fixtures/ClassTypeNaming/CorrectNameInterface.php index 408ec76..f81d426 100644 --- a/tests/rules/Fixtures/NamingConvention/CorrectNameInterface.php +++ b/tests/rules/Fixtures/ClassTypeNaming/CorrectNameInterface.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Fixtures\NamingConvention; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures\ClassTypeNaming; interface CorrectNameInterface { diff --git a/tests/rules/Fixtures/NamingConvention/CorrectNameTrait.php b/tests/rules/Fixtures/ClassTypeNaming/CorrectNameTrait.php similarity index 78% rename from tests/rules/Fixtures/NamingConvention/CorrectNameTrait.php rename to tests/rules/Fixtures/ClassTypeNaming/CorrectNameTrait.php index 2c87229..7ed6874 100644 --- a/tests/rules/Fixtures/NamingConvention/CorrectNameTrait.php +++ b/tests/rules/Fixtures/ClassTypeNaming/CorrectNameTrait.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Fixtures\NamingConvention; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures\ClassTypeNaming; trait CorrectNameTrait { diff --git a/tests/rules/Fixtures/NamingConvention/SimpleClass.php b/tests/rules/Fixtures/ClassTypeNaming/SimpleClass.php similarity index 79% rename from tests/rules/Fixtures/NamingConvention/SimpleClass.php rename to tests/rules/Fixtures/ClassTypeNaming/SimpleClass.php index 4498615..7ac16c2 100644 --- a/tests/rules/Fixtures/NamingConvention/SimpleClass.php +++ b/tests/rules/Fixtures/ClassTypeNaming/SimpleClass.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Fixtures\NamingConvention; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures\ClassTypeNaming; abstract class SimpleClass { diff --git a/tests/rules/Fixtures/NamingConvention/SimpleThing.php b/tests/rules/Fixtures/ClassTypeNaming/SimpleThing.php similarity index 78% rename from tests/rules/Fixtures/NamingConvention/SimpleThing.php rename to tests/rules/Fixtures/ClassTypeNaming/SimpleThing.php index 1398ca3..05d699a 100644 --- a/tests/rules/Fixtures/NamingConvention/SimpleThing.php +++ b/tests/rules/Fixtures/ClassTypeNaming/SimpleThing.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Fixtures\NamingConvention; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures\ClassTypeNaming; trait SimpleThing { diff --git a/tests/rules/Fixtures/NamingConvention/WrongName.php b/tests/rules/Fixtures/ClassTypeNaming/WrongName.php similarity index 78% rename from tests/rules/Fixtures/NamingConvention/WrongName.php rename to tests/rules/Fixtures/ClassTypeNaming/WrongName.php index b11f90d..1e826db 100644 --- a/tests/rules/Fixtures/NamingConvention/WrongName.php +++ b/tests/rules/Fixtures/ClassTypeNaming/WrongName.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Fixtures\NamingConvention; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures\ClassTypeNaming; interface WrongName {